# Productive and Efficient Web Framework In Rust Lang ## Introduction Rust Lang is a programming language that has been a favourite language for stack overflow users for few years. In comparison to a dynamically typed language popular among web developer, rust is very fast, and it could be said that it has more concise syntax. I made an attempt to explorer the possibility of building productive web framework in rust. ## Features ### Router Router is implemented in procedural macro. Existing frameworks tends evaluate the user input on runtime, however with procedural macro you can evaluate them on compile-time. This allows you to convert the router definition into simple match statement while giving programmer an opportunity to improve their productivity. The router syntax is heavily inspired by Elixir/Phoenix (which is a framework heavily influenced by Ruby on Rails) Macro syntax looks like this: ```rust router! { // ident given after the const keyword becomes the name of the router. const NAME_OF_THE_ROUTER; // scope keyword works like the scope keyword in elixir/phoenix // proceeding string will be a route scope "/api" { // you can nest them scope "/v1" { // syntax for declaring a route is // {method} {function_path}; // so when it receives a get request on "/api/v1" // function named `get` defined in module `api::v1` get api::v1::get; post api::v1::create; delete api::v1::destroy; } } scope "/resource" { /// some syntax can be used to declare multiple routes resource index::resource; // this is an equivalent of } } ``` When expanded, the macro will become. ```rust const NAME_OF_THE_ROUTER: Router = /* closure */ ``` ### Middleware #### Underlying Assumption Only argument that the middleware takes is Conn, and no values are returned. This allows you to type less, as a static language when you have to type lengthy idents all-over you’re your code, it will quickly become obnoxious and it could lead to error. I also wanted the macro to act as a #### Functions at Glance Request parameter can be read from methods on this object. ```rust fn print_req(conn: Conn) { println!(); println!("Method: {:?}", conn.method()); println!("Path: {}", conn.path()); println!("Body: \n {}", conn.body()); } ``` Response can be set from the method of this object as well. ```rust fn not_found(conn: Conn) { conn // obtains a mutex lock on underlying response object .mut_resp() // set reponse body and status code .set_resp(404, "not found"); } ``` #### Plugin Plugin is a set of reusable set of functions. Ident given after the const keyword will become the name of the plugin. ```rust plugin! { const DEFAULT_RESPONSE; func lucky_seven; func default; } ``` This will expand into ```rust const DEFAULT_RESPONSE: Plugin = /* closure for plugin */ ``` #### Server Server macro is there to define a function that will invoke a set of logics ```rust server! { fn resource_server; plugin reject_swear_words; router RESOURCE_ROUTER; plugin DEFAULT_RESPONSE; } ``` This will expand into ```rust fn resource_server(conn: Conn) { /* closure for plugin */ } ``` ## Some Other Idea I initially tried to implement with traits. The code looked like this: ```rust struct MyPlugin; impl Plugin for MyPlugin { fn call(conn: Conn) { /* some logic */ } } fn main() { let my_server = server::build() .plugin(MyPlugin) .router(MyRouter) .host("localhost:3000") .finish(); my_server.start(); } ``` I eventually dropped this. Since I didn't feel comfortable defining new datatype that doesn't hold any data. ## Potential Improvements My interest is primary in UX, not performance so I won't even try to discucss about the latter.(maybe in future) ### use of derive macro instead of declarative one Currently, you need to set the name of server/router/plugin with `fn` or `const` keyword. Which is not very rusty. I wasn't able to come up with a better plan while I was building it; however, now I think that it could've been a derive macro that applies for functions. ```rust #[derive(Router)] fn my_router(conn: Conn) { scope "/api" { get api::get; } } ```