diff --git a/gotham_restful/src/routing.rs b/gotham_restful/src/routing.rs index 5a16281..5bd2b1d 100644 --- a/gotham_restful/src/routing.rs +++ b/gotham_restful/src/routing.rs @@ -16,7 +16,10 @@ use gotham::{ extractor::QueryStringExtractor, handler::{HandlerFuture, IntoHandlerError}, helpers::http::response::{create_empty_response, create_response}, - pipeline::chain::PipelineHandleChain, + pipeline::{ + chain::PipelineHandleChain, + set::PipelineSet + }, router::{ builder::*, non_match::RouteNonMatch, @@ -24,7 +27,8 @@ use gotham::{ content_type::ContentTypeHeaderRouteMatcher, AcceptHeaderRouteMatcher, RouteMatcher - } + }, + tree::node::Node }, state::{FromState, State} }; @@ -49,7 +53,10 @@ struct PathExtractor /// router into one that will only allow RESTful resources, but record them and generate /// an OpenAPI specification on request. #[cfg(feature = "openapi")] -pub trait WithOpenapi +pub trait WithOpenapi +where + C : PipelineHandleChain

+ Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static { fn with_openapi(&mut self, title : Title, version : Version, server_url : Url, block : F) where @@ -61,9 +68,12 @@ pub trait WithOpenapi /// This trait adds the `resource` method to gotham's routing. It allows you to register /// any RESTful `Resource` with a path. -pub trait DrawResources +pub trait DrawResources +where + C : PipelineHandleChain

+ Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static { - fn resource(&mut self, path : T); + fn resource(&mut self, path : &str); } /// This trait allows to draw routes within an resource. Use this only inside the @@ -336,151 +346,186 @@ impl From>> for MaybeMatchContentTypeHeader } } -macro_rules! implDrawResourceRoutes { - ($implType:ident) => { - - #[cfg(feature = "openapi")] - impl<'a, C, P> WithOpenapi for $implType<'a, C, P> - where - C : PipelineHandleChain

+ Copy + Send + Sync + 'static, - P : RefUnwindSafe + Send + Sync + 'static - { - fn with_openapi(&mut self, title : Title, version : Version, server_url : Url, block : F) - where - F : FnOnce((&mut Self, &mut OpenapiRouter)), - Title : ToString, - Version : ToString, - Url : ToString - { - let mut router = OpenapiRouter::new(title, version, server_url); - block((self, &mut router)); - } - } - - impl<'a, C, P> DrawResources for $implType<'a, C, P> - where - C : PipelineHandleChain

+ Copy + Send + Sync + 'static, - P : RefUnwindSafe + Send + Sync + 'static - { - fn resource(&mut self, path : T) - { - R::setup((self, path.to_string())); - } - } - - #[allow(clippy::redundant_closure)] // doesn't work because of type parameters - impl<'a, C, P> DrawResourceRoutes for (&mut $implType<'a, C, P>, String) - where - C : PipelineHandleChain

+ Copy + Send + Sync + 'static, - P : RefUnwindSafe + Send + Sync + 'static - { - fn read_all(&mut self) - where - Res : ResourceResult, - Handler : ResourceReadAll - { - let matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); - self.0.get(&self.1) - .extend_route_matcher(matcher) - .to(|state| read_all_handler::(state)); - } +#[cfg(feature = "openapi")] +impl WithOpenapi for T +where + C : PipelineHandleChain

+ Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static, + T : DrawRoutes +{ + fn with_openapi(&mut self, title : Title, version : Version, server_url : Url, block : F) + where + F : FnOnce((&mut Self, &mut OpenapiRouter)), + Title : ToString, + Version : ToString, + Url : ToString + { + let mut router = OpenapiRouter::new(title, version, server_url); + block((self, &mut router)); + } +} - fn read(&mut self) - where - ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, - Res : ResourceResult, - Handler : ResourceRead - { - let matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); - self.0.get(&format!("{}/:id", self.1)) - .extend_route_matcher(matcher) - .with_path_extractor::>() - .to(|state| read_handler::(state)); - } - - fn search(&mut self) - where - Query : ResourceType + QueryStringExtractor + Send + Sync + 'static, - Res : ResourceResult, - Handler : ResourceSearch - { - let matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); - self.0.get(&format!("{}/search", self.1)) - .extend_route_matcher(matcher) - .with_query_string_extractor::() - .to(|state| search_handler::(state)); - } - - fn create(&mut self) - where - Body : RequestBody, - Res : ResourceResult, - Handler : ResourceCreate - { - let accept_matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); - let content_matcher : MaybeMatchContentTypeHeader = Body::supported_types().into(); - self.0.post(&self.1) - .extend_route_matcher(accept_matcher) - .extend_route_matcher(content_matcher) - .to(|state| create_handler::(state)); - } +impl DrawResources for T +where + C : PipelineHandleChain

+ Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static, + T : DrawRoutes +{ + fn resource(&mut self, path : &str) + { + R::setup(ResourceSetup::new(self, path)); + } +} - fn update_all(&mut self) - where - Body : RequestBody, - Res : ResourceResult, - Handler : ResourceUpdateAll - { - let accept_matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); - let content_matcher : MaybeMatchContentTypeHeader = Body::supported_types().into(); - self.0.put(&self.1) - .extend_route_matcher(accept_matcher) - .extend_route_matcher(content_matcher) - .to(|state| update_all_handler::(state)); - } +struct ResourceSetup<'a, C, P> +where + C : PipelineHandleChain

+ Copy + Send + Sync + 'static, + P : Send + Sync + 'static +{ + node_builder : &'a mut Node, + pipeline_chain : C, + pipelines : PipelineSet

, + path : &'a str +} - fn update(&mut self) - where - ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, - Body : RequestBody, - Res : ResourceResult, - Handler : ResourceUpdate - { - let accept_matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); - let content_matcher : MaybeMatchContentTypeHeader = Body::supported_types().into(); - self.0.put(&format!("{}/:id", self.1)) - .extend_route_matcher(accept_matcher) - .extend_route_matcher(content_matcher) - .with_path_extractor::>() - .to(|state| update_handler::(state)); - } - - fn delete_all(&mut self) - where - Res : ResourceResult, - Handler : ResourceDeleteAll - { - let matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); - self.0.delete(&self.1) - .extend_route_matcher(matcher) - .to(|state| delete_all_handler::(state)); - } - - fn delete(&mut self) - where - ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, - Res : ResourceResult, - Handler : ResourceDelete - { - let matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); - self.0.delete(&format!("{}/:id", self.1)) - .extend_route_matcher(matcher) - .with_path_extractor::>() - .to(|state| delete_handler::(state)); - } +impl<'a, C, P> ResourceSetup<'a, C, P> +where + C : PipelineHandleChain

+ Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static +{ + fn new(router : &'a mut D, path : &'a str) -> Self + where + D : DrawRoutes + 'a + { + let (node_builder, pipeline_chain, pipelines) = router.component_refs(); + Self { + node_builder, + pipeline_chain: *pipeline_chain, + pipelines: pipelines.clone(), + path } } } -implDrawResourceRoutes!(RouterBuilder); -implDrawResourceRoutes!(ScopeBuilder); +impl<'a, C, P> DrawRoutes for ResourceSetup<'a, C, P> +where + C : PipelineHandleChain

+ Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static +{ + fn component_refs(&mut self) -> (&mut Node, &mut C, &PipelineSet

) + { + (&mut self.node_builder, &mut self.pipeline_chain, &self.pipelines) + } +} + +#[allow(clippy::redundant_closure)] // doesn't work because of type parameters +impl<'a, C, P> DrawResourceRoutes for ResourceSetup<'a, C, P> +where + C : PipelineHandleChain

+ Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static +{ + fn read_all(&mut self) + where + Res : ResourceResult, + Handler : ResourceReadAll + { + let matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); + self.get(self.path) + .extend_route_matcher(matcher) + .to(|state| read_all_handler::(state)); + } + + fn read(&mut self) + where + ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, + Res : ResourceResult, + Handler : ResourceRead + { + let matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); + self.get(&format!("{}/:id", self.path)) + .extend_route_matcher(matcher) + .with_path_extractor::>() + .to(|state| read_handler::(state)); + } + + fn search(&mut self) + where + Query : ResourceType + QueryStringExtractor + Send + Sync + 'static, + Res : ResourceResult, + Handler : ResourceSearch + { + let matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); + self.get(&format!("{}/search", self.path)) + .extend_route_matcher(matcher) + .with_query_string_extractor::() + .to(|state| search_handler::(state)); + } + + fn create(&mut self) + where + Body : RequestBody, + Res : ResourceResult, + Handler : ResourceCreate + { + let accept_matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); + let content_matcher : MaybeMatchContentTypeHeader = Body::supported_types().into(); + self.post(self.path) + .extend_route_matcher(accept_matcher) + .extend_route_matcher(content_matcher) + .to(|state| create_handler::(state)); + } + + fn update_all(&mut self) + where + Body : RequestBody, + Res : ResourceResult, + Handler : ResourceUpdateAll + { + let accept_matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); + let content_matcher : MaybeMatchContentTypeHeader = Body::supported_types().into(); + self.put(self.path) + .extend_route_matcher(accept_matcher) + .extend_route_matcher(content_matcher) + .to(|state| update_all_handler::(state)); + } + + fn update(&mut self) + where + ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, + Body : RequestBody, + Res : ResourceResult, + Handler : ResourceUpdate + { + let accept_matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); + let content_matcher : MaybeMatchContentTypeHeader = Body::supported_types().into(); + self.put(&format!("{}/:id", self.path)) + .extend_route_matcher(accept_matcher) + .extend_route_matcher(content_matcher) + .with_path_extractor::>() + .to(|state| update_handler::(state)); + } + + fn delete_all(&mut self) + where + Res : ResourceResult, + Handler : ResourceDeleteAll + { + let matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); + DrawRoutes::delete(self, self.path) + .extend_route_matcher(matcher) + .to(|state| delete_all_handler::(state)); + } + + fn delete(&mut self) + where + ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, + Res : ResourceResult, + Handler : ResourceDelete + { + let matcher : MaybeMatchAcceptHeader = Res::accepted_types().into(); + DrawRoutes::delete(self, &format!("{}/:id", self.path)) + .extend_route_matcher(matcher) + .with_path_extractor::>() + .to(|state| delete_handler::(state)); + } +}