# Design (WIP) When i began building arc-reactor, i was still learning the rust language. Coming from a nodejs background, I wanted a web framework that closely mimiced the simplicity of expressjs the most popular web framework for nodejs. So I wrote Arc Reactor. ## ArcService ```rust pub trait ArcService: ArcServiceClone + Send + Sync { fn call(&self, req: Request, res: Response) -> Box>; } ``` All Service Handlers in ArcReactor implement this trait it is a heavily simplified version of tokio's `Service` trait. ```rust trait Service { type Request; type Response; type Error; type Future = Box>; fn call(&self, req: Self::Request) -> Self::Future; } ``` This trait enables a generic way to define Service Handlers. ## ArcHandler ```rust pub struct ArcHandler { pub before: Option>>, pub handler: Box, pub after: Option>>, } impl ArcService for ArcHandler {} ``` ArcHandler is one of the central pieces to arc reactor. - [x] Becuase of it's structure, It becomes possible to nest ArcHandlers. - [x] Nested ArcHandlers inherit their parent's middlewares. - [x] A request is passed from the `before`(`MiddleWare`) to the `handler`(which is also possibly an ArcHandler) and finally to the `after`(MiddleWare). - [x] If the `MiddleWare` returns an `Err(Response)`, the `handler` is skipped, and the response is forwarded to the `after`(MiddleWare)*. - [x] Returning an `Err(Response)` from a `handler` has no effect, if the `after`(MiddleWare) exists it would *always* recieve the response returned from the `handler`. Returning an `Err(Response)` should only be done for testability sake with `FakeReactor`. ## MiddleWare ```rust pub trait MiddleWare: MiddleWareClone + Sync + Send { fn call(&self, param: T) -> Box>; } ``` Another one of the central pieces to arc reactor. The middleware trait enables a generic way of defining a MiddleWare. It does so by providing two types of MiddleWares. - [x] `MiddleWare` - [x] This is run before the service handler and has time to do extra processing on the request, e.g the body parser, staticfilserver and multipart parser in contrib were all implemented as `MiddleWare` - [x] A vector of `Vec>>` can behave like a single `MiddleWare`. - [x] If a `MiddleWare` in a `Vec>>` returns an Err(Response), the rest of the Middlewares are skipped, - [x] Therefore the order matters when mounting MiddleWares. - [x] `MiddleWare` - [x] This is run after the service handler, for additional processing to be done on the response. - [x] A vector of `Vec>>` can behave like a single `MiddleWare`. - [x] If a `MiddleWare` in a `Vec>>` returns an `Err(Response)`, the rest of the Middlewares are skipped, ## Proc Macros ```rust #[service] fn ServiceHandler(req: Request, res: Response) { Ok(res) } #[middleware(Request)] fn AuthMiddleWare(req: Request) { Ok(req) } #[middleware(Response)] fn LoggerMiddleWare(res: Request) { Ok(res) } ``` In order to provide ease of use, and integration with `futures-await`. the `service` and `middleware` proc_macros were introduced. the `service` proc_macro, creates a zero-sized struct (i.e a struct with no fields, with the same name as the `fn`), and implements the `ArcService` trait for the struct wrapping its function body in an `async_block`. the `middleware` proc_macro, creates a zero-sized struct (i.e a struct with no fields, with the same name as the `fn`), and implements the `MiddleWare` trait for the struct wrapping its function body in an `async_block`. ```rust pub struct ServiceHandler; impl ArcService for ServiceHandler { fn call(&self, req: Request, res: Response) -> Box { let future = async_block! { Ok(req) }; Box::new(future) } } pub struct AuthMiddleWare; impl MiddleWare for AuthMiddleWare { fn call(&self, req: Request) -> Box { let future = async_block! { Ok(req) }; Box::new(future) } } pub struct LoggerMiddleWare; impl MiddleWare for LoggerMiddleWare { fn call(&self, res: Response) -> Box { let future = async_block! { Ok(res) }; Box::new(future) } } ```