diff --git a/gotham_restful/src/lib.rs b/gotham_restful/src/lib.rs index 1cc9420..f78109a 100644 --- a/gotham_restful/src/lib.rs +++ b/gotham_restful/src/lib.rs @@ -151,7 +151,7 @@ pub use auth::{ mod openapi; #[cfg(feature = "openapi")] pub use openapi::{ - router::{GetOpenapi, OpenapiRouter}, + router::GetOpenapi, types::{OpenapiSchema, OpenapiType} }; diff --git a/gotham_restful/src/openapi/router.rs b/gotham_restful/src/openapi/router.rs index 9f5c185..6e9be42 100644 --- a/gotham_restful/src/openapi/router.rs +++ b/gotham_restful/src/openapi/router.rs @@ -12,8 +12,14 @@ use gotham::{ extractor::QueryStringExtractor, handler::{Handler, HandlerFuture, NewHandler}, helpers::http::response::create_response, - pipeline::chain::PipelineHandleChain, - router::builder::*, + pipeline::{ + chain::PipelineHandleChain, + set::PipelineSet + }, + router::{ + builder::*, + tree::node::Node + }, state::State }; use hyper::Body; @@ -28,15 +34,10 @@ use openapiv3::{ use serde::de::DeserializeOwned; use std::panic::RefUnwindSafe; -/** -This type is required to build routes while adding them to the generated OpenAPI Spec at the -same time. There is no need to use this type directly. See [`WithOpenapi`] on how to do this. +/// A helper struct for `OpenapiRouter` to build the OpenAPI specification. +pub struct OpenapiBuilder(OpenAPI); -[`WithOpenapi`]: trait.WithOpenapi.html -*/ -pub struct OpenapiRouter(OpenAPI); - -impl OpenapiRouter +impl OpenapiBuilder { pub fn new(title : Title, version : Version, server_url : Url) -> Self { @@ -129,7 +130,7 @@ struct OpenapiHandler(OpenAPI); impl OpenapiHandler { - fn new(openapi : &OpenapiRouter) -> Self + fn new(openapi : &OpenapiBuilder) -> Self { Self(openapi.0.clone()) } @@ -365,173 +366,259 @@ fn new_operation( } } -macro_rules! implOpenapiRouter { - ($implType:ident) => { +struct OpenapiRouteBuilder<'a, C, P> +where + C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, + P : Send + Sync + 'static +{ + node_builder : &'a mut Node, + pipeline_chain : C, + pipelines : PipelineSet<P> +} - impl<'a, C, P> GetOpenapi for (&mut $implType<'a, C, P>, &mut OpenapiRouter) - where - C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, - P : RefUnwindSafe + Send + Sync + 'static - { - fn get_openapi(&mut self, path : &str) - { - self.0.get(path).to_new_handler(OpenapiHandler::new(&self.1)); - } +impl<'a, C, P> OpenapiRouteBuilder<'a, C, P> +where + C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static +{ + fn new<D>(router : &'a mut D) -> Self + where + D : DrawRoutes<C, P> + 'a + { + let (node_builder, pipeline_chain, pipelines) = router.component_refs(); + Self { + node_builder, + pipeline_chain: *pipeline_chain, + pipelines: pipelines.clone() } - - impl<'a, C, P> DrawResources for (&mut $implType<'a, C, P>, &mut OpenapiRouter) - where - C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, - P : RefUnwindSafe + Send + Sync + 'static - { - fn resource<R : Resource, T : ToString>(&mut self, path : T) - { - R::setup((self, path.to_string())); - } - } - - impl<'a, C, P> DrawResourceRoutes for (&mut (&mut $implType<'a, C, P>, &mut OpenapiRouter), String) - where - C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, - P : RefUnwindSafe + Send + Sync + 'static - { - fn read_all<Handler, Res>(&mut self) - where - Res : ResourceResult, - Handler : ResourceReadAll<Res> - { - let schema = (self.0).1.add_schema::<Res>(); - - let path = format!("/{}", &self.1); - let mut item = (self.0).1.remove_path(&path); - item.get = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::default(), None, None, Res::requires_auth())); - (self.0).1.add_path(path, item); - - (&mut *(self.0).0, self.1.to_string()).read_all::<Handler, Res>() - } - - fn read<Handler, ID, Res>(&mut self) - where - ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, - Res : ResourceResult, - Handler : ResourceRead<ID, Res> - { - let schema = (self.0).1.add_schema::<Res>(); - - let path = format!("/{}/{{id}}", &self.1); - let mut item = (self.0).1.remove_path(&path); - item.get = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::from_path_params(vec!["id"]), None, None, Res::requires_auth())); - (self.0).1.add_path(path, item); - - (&mut *(self.0).0, self.1.to_string()).read::<Handler, ID, Res>() - } - - fn search<Handler, Query, Res>(&mut self) - where - Query : ResourceType + DeserializeOwned + QueryStringExtractor<Body> + Send + Sync + 'static, - Res : ResourceResult, - Handler : ResourceSearch<Query, Res> - { - let schema = (self.0).1.add_schema::<Res>(); - - let path = format!("/{}/search", &self.1); - let mut item = (self.0).1.remove_path(&self.1); - item.get = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::from_query_params(Query::schema()), None, None, Res::requires_auth())); - (self.0).1.add_path(path, item); - - (&mut *(self.0).0, self.1.to_string()).search::<Handler, Query, Res>() - } - - fn create<Handler, Body, Res>(&mut self) - where - Body : RequestBody, - Res : ResourceResult, - Handler : ResourceCreate<Body, Res> - { - let schema = (self.0).1.add_schema::<Res>(); - let body_schema = (self.0).1.add_schema::<Body>(); - - let path = format!("/{}", &self.1); - let mut item = (self.0).1.remove_path(&path); - item.post = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::default(), Some(body_schema), Body::supported_types(), Res::requires_auth())); - (self.0).1.add_path(path, item); - - (&mut *(self.0).0, self.1.to_string()).create::<Handler, Body, Res>() - } - - fn update_all<Handler, Body, Res>(&mut self) - where - Body : RequestBody, - Res : ResourceResult, - Handler : ResourceUpdateAll<Body, Res> - { - let schema = (self.0).1.add_schema::<Res>(); - let body_schema = (self.0).1.add_schema::<Body>(); - - let path = format!("/{}", &self.1); - let mut item = (self.0).1.remove_path(&path); - item.put = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::default(), Some(body_schema), Body::supported_types(), Res::requires_auth())); - (self.0).1.add_path(path, item); - - (&mut *(self.0).0, self.1.to_string()).update_all::<Handler, Body, Res>() - } - - fn update<Handler, ID, Body, Res>(&mut self) - where - ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, - Body : RequestBody, - Res : ResourceResult, - Handler : ResourceUpdate<ID, Body, Res> - { - let schema = (self.0).1.add_schema::<Res>(); - let body_schema = (self.0).1.add_schema::<Body>(); - - let path = format!("/{}/{{id}}", &self.1); - let mut item = (self.0).1.remove_path(&path); - item.put = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::from_path_params(vec!["id"]), Some(body_schema), Body::supported_types(), Res::requires_auth())); - (self.0).1.add_path(path, item); - - (&mut *(self.0).0, self.1.to_string()).update::<Handler, ID, Body, Res>() - } - - fn delete_all<Handler, Res>(&mut self) - where - Res : ResourceResult, - Handler : ResourceDeleteAll<Res> - { - let schema = (self.0).1.add_schema::<Res>(); - - let path = format!("/{}", &self.1); - let mut item = (self.0).1.remove_path(&path); - item.delete = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::default(), None, None, Res::requires_auth())); - (self.0).1.add_path(path, item); - - (&mut *(self.0).0, self.1.to_string()).delete_all::<Handler, Res>() - } - - fn delete<Handler, ID, Res>(&mut self) - where - ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, - Res : ResourceResult, - Handler : ResourceDelete<ID, Res> - { - let schema = (self.0).1.add_schema::<Res>(); - - let path = format!("/{}/{{id}}", &self.1); - let mut item = (self.0).1.remove_path(&path); - item.delete = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::from_path_params(vec!["id"]), None, None, Res::requires_auth())); - (self.0).1.add_path(path, item); - - (&mut *(self.0).0, self.1.to_string()).delete::<Handler, ID, Res>() - } - } - } } -implOpenapiRouter!(RouterBuilder); -implOpenapiRouter!(ScopeBuilder); +impl<'a, C, P> DrawRoutes<C, P> for OpenapiRouteBuilder<'a, C, P> +where + C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static +{ + fn component_refs(&mut self) -> (&mut Node, &mut C, &PipelineSet<P>) + { + (&mut self.node_builder, &mut self.pipeline_chain, &self.pipelines) + } +} +/** +This type is required to build routes while adding them to the generated OpenAPI Spec at the +same time. There is no need to use this type directly. See [`WithOpenapi`] on how to do this. + +[`WithOpenapi`]: trait.WithOpenapi.html +*/ +pub struct OpenapiRouter<'a, C, P> +where + C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, + P : Send + Sync + 'static +{ + builder : OpenapiRouteBuilder<'a, C, P>, + openapi : OpenapiBuilder +} + +impl<'a, C, P> OpenapiRouter<'a, C, P> +where + C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static +{ + pub fn new<D>(router : &'a mut D, openapi : OpenapiBuilder) -> Self + where + D : DrawRoutes<C, P> + { + Self { + builder: OpenapiRouteBuilder::new(router), + openapi + } + } +} + +impl<'a, C, P> GetOpenapi for OpenapiRouter<'a, C, P> +where + C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static +{ + fn get_openapi(&mut self, path : &str) + { + self.builder.get(path).to_new_handler(OpenapiHandler::new(&self.openapi)); + } +} + +impl<'a, C, P> DrawResources<C, P> for OpenapiRouter<'a, C, P> +where + C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static +{ + fn resource<'b, R : Resource>(&'b mut self, path : &'b str) + { + R::setup(OpenapiResourceSetup::new(self, path)); + } +} + +struct OpenapiResourceSetup<'a, C, P> +where + C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, + P : Send + Sync + 'static +{ + builder : OpenapiRouteBuilder<'a, C, P>, + openapi : &'a mut OpenapiBuilder, + path : &'a str +} + +impl<'a, C, P> OpenapiResourceSetup<'a, C, P> +where + C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, + P : Send + Sync + 'static +{ + fn new(router : &'a mut OpenapiRouter<'a, C, P>, path : &'a str) -> Self + { + Self { + builder: &mut router.builder, + openapi: &mut router.openapi, + path + } + } +} + +/* +impl<'a, C, P> DrawResourceRoutes for OpenapiResourceSetup<'a, C, P> +where + C : PipelineHandleChain<P> + Copy + Send + Sync + 'static, + P : RefUnwindSafe + Send + Sync + 'static +{ + fn read_all<Handler, Res>(&mut self) + where + Res : ResourceResult, + Handler : ResourceReadAll<Res> + { + let schema = (self.0).1.add_schema::<Res>(); + + let path = format!("/{}", &self.1); + let mut item = (self.0).1.remove_path(&path); + item.get = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::default(), None, None, Res::requires_auth())); + (self.0).1.add_path(path, item); + + (&mut *(self.0).0, self.1.to_string()).read_all::<Handler, Res>() + } + + fn read<Handler, ID, Res>(&mut self) + where + ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, + Res : ResourceResult, + Handler : ResourceRead<ID, Res> + { + let schema = (self.0).1.add_schema::<Res>(); + + let path = format!("/{}/{{id}}", &self.1); + let mut item = (self.0).1.remove_path(&path); + item.get = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::from_path_params(vec!["id"]), None, None, Res::requires_auth())); + (self.0).1.add_path(path, item); + + (&mut *(self.0).0, self.1.to_string()).read::<Handler, ID, Res>() + } + + fn search<Handler, Query, Res>(&mut self) + where + Query : ResourceType + DeserializeOwned + QueryStringExtractor<Body> + Send + Sync + 'static, + Res : ResourceResult, + Handler : ResourceSearch<Query, Res> + { + let schema = (self.0).1.add_schema::<Res>(); + + let path = format!("/{}/search", &self.1); + let mut item = (self.0).1.remove_path(&self.1); + item.get = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::from_query_params(Query::schema()), None, None, Res::requires_auth())); + (self.0).1.add_path(path, item); + + (&mut *(self.0).0, self.1.to_string()).search::<Handler, Query, Res>() + } + + fn create<Handler, Body, Res>(&mut self) + where + Body : RequestBody, + Res : ResourceResult, + Handler : ResourceCreate<Body, Res> + { + let schema = (self.0).1.add_schema::<Res>(); + let body_schema = (self.0).1.add_schema::<Body>(); + + let path = format!("/{}", &self.1); + let mut item = (self.0).1.remove_path(&path); + item.post = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::default(), Some(body_schema), Body::supported_types(), Res::requires_auth())); + (self.0).1.add_path(path, item); + + (&mut *(self.0).0, self.1.to_string()).create::<Handler, Body, Res>() + } + + fn update_all<Handler, Body, Res>(&mut self) + where + Body : RequestBody, + Res : ResourceResult, + Handler : ResourceUpdateAll<Body, Res> + { + let schema = (self.0).1.add_schema::<Res>(); + let body_schema = (self.0).1.add_schema::<Body>(); + + let path = format!("/{}", &self.1); + let mut item = (self.0).1.remove_path(&path); + item.put = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::default(), Some(body_schema), Body::supported_types(), Res::requires_auth())); + (self.0).1.add_path(path, item); + + (&mut *(self.0).0, self.1.to_string()).update_all::<Handler, Body, Res>() + } + + fn update<Handler, ID, Body, Res>(&mut self) + where + ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, + Body : RequestBody, + Res : ResourceResult, + Handler : ResourceUpdate<ID, Body, Res> + { + let schema = (self.0).1.add_schema::<Res>(); + let body_schema = (self.0).1.add_schema::<Body>(); + + let path = format!("/{}/{{id}}", &self.1); + let mut item = (self.0).1.remove_path(&path); + item.put = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::from_path_params(vec!["id"]), Some(body_schema), Body::supported_types(), Res::requires_auth())); + (self.0).1.add_path(path, item); + + (&mut *(self.0).0, self.1.to_string()).update::<Handler, ID, Body, Res>() + } + + fn delete_all<Handler, Res>(&mut self) + where + Res : ResourceResult, + Handler : ResourceDeleteAll<Res> + { + let schema = (self.0).1.add_schema::<Res>(); + + let path = format!("/{}", &self.1); + let mut item = (self.0).1.remove_path(&path); + item.delete = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::default(), None, None, Res::requires_auth())); + (self.0).1.add_path(path, item); + + (&mut *(self.0).0, self.1.to_string()).delete_all::<Handler, Res>() + } + + fn delete<Handler, ID, Res>(&mut self) + where + ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, + Res : ResourceResult, + Handler : ResourceDelete<ID, Res> + { + let schema = (self.0).1.add_schema::<Res>(); + + let path = format!("/{}/{{id}}", &self.1); + let mut item = (self.0).1.remove_path(&path); + item.delete = Some(new_operation(Res::default_status(), Res::accepted_types(), schema, OperationParams::from_path_params(vec!["id"]), None, None, Res::requires_auth())); + (self.0).1.add_path(path, item); + + (&mut *(self.0).0, self.1.to_string()).delete::<Handler, ID, Res>() + } +} #[cfg(test)] mod test @@ -603,3 +690,4 @@ mod test assert_eq!(json, r#"{"schema":{"type":"string","format":"binary"}}"#); } } +*/ \ No newline at end of file diff --git a/gotham_restful/src/routing.rs b/gotham_restful/src/routing.rs index 5bd2b1d..61c8c97 100644 --- a/gotham_restful/src/routing.rs +++ b/gotham_restful/src/routing.rs @@ -6,7 +6,7 @@ use crate::{ StatusCode }; #[cfg(feature = "openapi")] -use crate::OpenapiRouter; +use crate::openapi::router::{OpenapiBuilder, OpenapiRouter}; use futures::{ future::{Future, err, ok}, @@ -60,7 +60,7 @@ where { fn with_openapi<F, Title, Version, Url>(&mut self, title : Title, version : Version, server_url : Url, block : F) where - F : FnOnce((&mut D, &mut OpenapiRouter)), + F : FnOnce(OpenapiRouter<C, P>), Title : ToString, Version : ToString, Url : ToString; @@ -355,13 +355,13 @@ where { fn with_openapi<F, Title, Version, Url>(&mut self, title : Title, version : Version, server_url : Url, block : F) where - F : FnOnce((&mut Self, &mut OpenapiRouter)), + F : FnOnce(OpenapiRouter<C, P>), Title : ToString, Version : ToString, Url : ToString { - let mut router = OpenapiRouter::new(title, version, server_url); - block((self, &mut router)); + let router = OpenapiRouter::new(self, OpenapiBuilder::new(title, version, server_url)); + block(router); } }