from typing import TypeGuard, override from app.subroutines.http import Response, SimpleRequest, SimpleResponse from app.types_ import ( AnyScope, AsyncCallable, HTTPScope, PassthroughDecorator, Receive, ReceiveHTTP, RouteMapping, Send, ) from .base import RouteComponent as _RouteComponent class HTTPComponent( _RouteComponent[HTTPScope, ReceiveHTTP, dict[str, RouteMapping[Response]], Response] ): routes: dict[str, RouteMapping[Response]] def __init__(self) -> None: self.routes = {} super().__init__() @override async def condition(self, scope: AnyScope) -> TypeGuard[HTTPScope]: return scope["type"] == "http" @override async def handle( self, scope: HTTPScope, receive: Receive[ReceiveHTTP], send: Send ) -> None: req = SimpleRequest() while not req.receive(await receive()): pass print(scope, req.body) resp = await self.route_dispatch(scope, receive, send) if resp is None: resp = Response(status=404, body=b"404 Not Found\n") async with SimpleResponse(send).prepare( resp.status, headers=resp.headers ) as rsp: await rsp.finish(resp.body if resp.body is not None else b"") return None @override async def route_dispatch( self, scope: HTTPScope, receive: Receive[ReceiveHTTP], send: Send ) -> Response | None: for k, callee in self.routes[scope["method"].upper()].items(): if scope["path"] == k: # temporary impl. return await callee() def route[T: AsyncCallable[..., Response]]( self, route: str, *, get: bool = False, post: bool = False, put: bool = False, delete: bool = False, ) -> PassthroughDecorator[T]: def __wrap_route(fn: T) -> T: if get: self.route_install("GET", route, fn) if post: self.route_install("POST", route, fn) if put: self.route_install("PUT", route, fn) if delete: self.route_install("DELETE", route, fn) return fn return __wrap_route def get[T: AsyncCallable[..., Response]]( self, route: str ) -> PassthroughDecorator[T]: return self.route(route, get=True) def post[T: AsyncCallable[..., Response]]( self, route: str ) -> PassthroughDecorator[T]: return self.route(route, post=True) def put[T: AsyncCallable[..., Response]]( self, route: str ) -> PassthroughDecorator[T]: return self.route(route, put=True) def delete[T: AsyncCallable[..., Response]]( self, route: str ) -> PassthroughDecorator[T]: return self.route(route, delete=True)