Skip to content

Commit

Permalink
restructure astra-web routing
Browse files Browse the repository at this point in the history
  • Loading branch information
ibraheemdev committed Nov 19, 2023
1 parent b6c8fb5 commit 8ff93b0
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 195 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ mio = { version = "0.8.5", features = ["os-poll", "net"] }

[dev-dependencies]
matchit = "0.7.0"

# [workspace]
# members = ["astra-web"]
3 changes: 2 additions & 1 deletion astra-web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ edition = "2021"
http2 = ["astra/http2"]

[dependencies]
astra = { version = "0.3.0", path = "../astra" }
astra = { version = "0.3.0", path = "../" }
bytes = "1.5.0"
http = "0.2.9"
matchit = "0.7.2"
serde = "1.0.192"
14 changes: 8 additions & 6 deletions astra-web/examples/hello_world.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use astra::{Request, Response};
use astra_web::{layer_fn, Group, Next, Router};
use astra_web::{layer_fn, Next, Router};
use http::{Method, Version};

fn hello_world(_request: Request, _method: Method, _version: Version) -> &'static str {
Expand All @@ -15,17 +15,19 @@ fn create_foo() -> &'static str {
}

fn main() {
Router::new()
let router = Router::new()
.get("/", hello_world)
.group(
.nest(
"/foo",
Group::new()
Router::new()
.get("/", get_foo)
.get("/index", get_foo)
.post("/new", create_foo),
)
.layer(layer_fn(logger))
.serve("localhost:3000")
.layer(layer_fn(logger));

astra::Server::bind("localhost:3000")
.serve(router.into_service())
.unwrap()
}

Expand Down
4 changes: 4 additions & 0 deletions astra-web/src/extract/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
mod parts;
mod state;
mod path;

pub use state::{State, StateError};

use crate::IntoResponse;

Expand Down
37 changes: 37 additions & 0 deletions astra-web/src/extract/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use astra::Body;
use http::StatusCode;

use crate::{FromRequest, IntoResponse};

#[derive(Debug)]
pub struct Path<T>(pub T);

impl<T> FromRequest for Path<T>
where
T: Send + Sync + 'static,
{
type Error = PathError;

fn from_request(_request: &mut astra::Request) -> Result<Self, Self::Error> {
todo!()
}
}

pub struct PathError {}

impl IntoResponse for PathError {
fn into_response(self) -> astra::Response {
astra::ResponseBuilder::new()
.status(StatusCode::BAD_REQUEST)
.body(Body::empty())
.unwrap()
}
}

impl<T> std::ops::Deref for Path<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}
28 changes: 28 additions & 0 deletions astra-web/src/extract/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::{FromRequest, IntoResponse};
use astra::{Body, Response, ResponseBuilder};
use http::StatusCode;

#[derive(Debug, Default, Clone, Copy)]
pub struct State<T>(T);

impl<T> FromRequest for State<T>
where
T: Clone + Send + Sync + 'static,
{
type Error = StateError;

fn from_request(request: &mut astra::Request) -> Result<Self, Self::Error> {
request.extensions().get().cloned().ok_or(StateError)
}
}

pub struct StateError;

impl IntoResponse for StateError {
fn into_response(self) -> Response {
ResponseBuilder::new()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::empty())
.unwrap()
}
}
8 changes: 3 additions & 5 deletions astra-web/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
pub mod handler;

mod extract;
mod layer;
mod response;
mod router;
mod server;
mod layer;

pub use extract::FromRequest;
pub use layer::{layer_fn, Layer, Next};
pub use response::IntoResponse;
pub use router::Group;
pub use server::Router;
pub use layer::{layer_fn, Next, Layer};
pub use router::Router;
159 changes: 69 additions & 90 deletions astra-web/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,37 @@ use std::sync::Arc;

use http::{Method, StatusCode};

pub struct Routes<L> {
pub routes: Vec<(Method, matchit::Router<handler::Erased>)>,
pub layer: L,
}

#[derive(Clone)]
pub struct SharedRouter<L> {
pub routes: Arc<Vec<(Method, matchit::Router<handler::Erased>)>>,
pub struct Router<L> {
pub routes: Vec<(Method, Vec<(String, handler::Erased)>)>,
pub layer: L,
}

struct Params(HashMap<String, String>);

impl Routes<()> {
pub fn new() -> Routes<()> {
Routes {
impl Router<()> {
pub fn new() -> Router<()> {
Router {
routes: Vec::new(),
layer: (),
}
}
}

impl<L> Routes<L>
impl<L> Router<L>
where
L: Layer,
{
pub fn layer<O>(self, layer: O) -> Routes<impl Layer>
pub fn layer<O>(self, layer: O) -> Router<impl Layer>
where
O: Layer,
{
Routes {
Router {
layer: self.layer.layer(layer),
routes: self.routes,
}
}

pub fn route<H, R>(
&mut self,
path: &str,
method: Method,
handler: H,
) -> Result<(), matchit::InsertError>
pub fn route<H, R>(self, path: &str, method: Method, handler: H) -> Router<L>
where
H: Handler<R>,
H::Response: IntoResponse,
Expand All @@ -57,22 +46,38 @@ where
self.route_erased(path, method, handler::erase(handler))
}

pub fn get<H, R>(self, path: &str, handler: H) -> Router<L>
where
H: Handler<R>,
H::Response: IntoResponse,
R: FromRequest,
{
self.route(path, Method::GET, handler)
}

route!(put => PUT);
route!(post => POST);
route!(head => HEAD);
route!(patch => PATCH);
route!(delete => DELETE);
route!(options => OPTIONS);

pub fn route_erased(
&mut self,
mut self,
path: &str,
method: Method,
handler: handler::Erased,
) -> Result<(), matchit::InsertError> {
) -> Router<L> {
if let Some(routes) = self.routes_mut(&method) {
return routes.insert(path, handler);
routes.push((path.to_owned(), handler));
return self;
}

self.routes.push((method, matchit::Router::new()));
let router = &mut self.routes.last_mut().unwrap().1;
router.insert(path, handler)
self.routes.push((method, vec![(path.to_owned(), handler)]));
self
}

pub fn group<O>(&mut self, prefix: &str, group: Group<O>) -> Result<(), matchit::InsertError>
pub fn nest<O>(mut self, prefix: &str, router: Router<O>) -> Router<L>
where
O: Layer,
{
Expand All @@ -81,21 +86,51 @@ where
prefix.push('/');
}

for (method, path, handler) in group.routes {
let path = format!("{}{}", prefix, path);
let handler = handler.layer(group.layer.clone());
self.route_erased(&path, method, handler::erase(handler))?;
for (method, routes) in router.routes {
for (path, handler) in routes {
let path = format!("{}{}", prefix, path);
let handler = handler.layer(router.layer.clone());
self = self.route_erased(&path, method.clone(), handler::erase(handler));
}
}

Ok(())
self
}

fn routes_mut(&mut self, method: &Method) -> Option<&mut matchit::Router<handler::Erased>> {
fn routes_mut(&mut self, method: &Method) -> Option<&mut Vec<(String, handler::Erased)>> {
self.routes
.iter_mut()
.find(|(m, _)| m == method)
.map(|(_, router)| router)
}

pub fn into_service(self) -> impl astra::Service {
let router = SharedRouter::new(self);
move |req, _| router.serve(req)
}
}

#[derive(Clone)]
pub struct SharedRouter<L> {
pub routes: Arc<Vec<(Method, matchit::Router<handler::Erased>)>>,
pub layer: L,
}

impl<L> SharedRouter<L> {
pub fn new(router: Router<L>) -> SharedRouter<L> {
let routes = router.routes.into_iter().map(|(method, routes)| {
let mut router = matchit::Router::new();
for (path, handler) in routes {
router.insert(path, handler).unwrap();
}
(method, router)
});

SharedRouter {
routes: Arc::new(routes.collect()),
layer: router.layer,
}
}
}

impl<L> SharedRouter<L>
Expand Down Expand Up @@ -140,65 +175,9 @@ where
}
}

pub struct Group<L> {
pub routes: Vec<(Method, String, handler::Erased)>,
pub layer: L,
}

impl Group<()> {
pub fn new() -> Group<()> {
Group {
routes: Vec::new(),
layer: (),
}
}
}

impl<L> Group<L>
where
L: Layer,
{
pub fn layer<O>(self, layer: O) -> Group<impl Layer>
where
O: Layer,
{
Group {
layer: self.layer.layer(layer),
routes: self.routes,
}
}

pub fn route<H, R>(mut self, path: &str, method: Method, handler: H) -> Group<L>
where
H: Handler<R>,
H::Response: IntoResponse,
R: FromRequest,
{
self.routes
.push((method, path.to_owned(), handler::erase(handler)));
self
}

pub fn get<H, R>(self, path: &str, handler: H) -> Group<L>
where
H: Handler<R>,
H::Response: IntoResponse,
R: FromRequest,
{
self.route(path, Method::GET, handler)
}

route!(put => PUT);
route!(post => POST);
route!(head => HEAD);
route!(patch => PATCH);
route!(delete => DELETE);
route!(options => OPTIONS);
}

macro_rules! route {
($name:ident => $method:ident) => {
pub fn $name<H, R>(self, path: &str, handler: H) -> Group<L>
pub fn $name<H, R>(self, path: &str, handler: H) -> Router<L>
where
H: Handler<R>,
H::Response: IntoResponse,
Expand Down
Loading

0 comments on commit 8ff93b0

Please sign in to comment.