use crate::{ resource::*, result::{ResourceError, ResourceResult}, ResourceType, StatusCode }; #[cfg(feature = "openapi")] use crate::OpenapiRouter; use futures::{ future::{Future, err, ok}, stream::Stream }; use gotham::{ extractor::QueryStringExtractor, handler::{HandlerFuture, IntoHandlerError}, helpers::http::response::create_response, pipeline::chain::PipelineHandleChain, router::builder::*, state::{FromState, State} }; use hyper::Body; use mime::APPLICATION_JSON; use serde::de::DeserializeOwned; use std::panic::RefUnwindSafe; /// Allow us to extract an id from a path. #[derive(Deserialize, StateData, StaticResponseExtender)] struct PathExtractor { id : ID } /// This trait adds the `with_openapi` method to gotham's routing. It turns the default /// 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 { fn with_openapi(&mut self, title : Title, version : Version, server_url : Url, block : F) where F : FnOnce((&mut D, &mut OpenapiRouter)), Title : ToString, Version : ToString, Url : ToString; } /// This trait adds the `resource` method to gotham's routing. It allows you to register /// any RESTful `Resource` with a path. pub trait DrawResources { fn resource(&mut self, path : T); } /// This trait allows to draw routes within an resource. Use this only inside the /// `Resource::setup` method. pub trait DrawResourceRoutes { fn read_all(&mut self) where Res : ResourceResult, Handler : ResourceReadAll; fn read(&mut self) where ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, Res : ResourceResult, Handler : ResourceRead; fn search(&mut self) where Query : ResourceType + QueryStringExtractor + Send + Sync + 'static, Res : ResourceResult, Handler : ResourceSearch; fn create(&mut self) where Body : ResourceType, Res : ResourceResult, Handler : ResourceCreate; fn update_all(&mut self) where Body : ResourceType, Res : ResourceResult, Handler : ResourceUpdateAll; fn update(&mut self) where ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, Body : ResourceType, Res : ResourceResult, Handler : ResourceUpdate; fn delete_all(&mut self) where Res : ResourceResult, Handler : ResourceDeleteAll; fn delete(&mut self) where ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, Res : ResourceResult, Handler : ResourceDelete; } fn to_handler_future(mut state : State, get_result : F) -> Box where F : FnOnce(&mut State) -> R, R : ResourceResult { let res = get_result(&mut state).to_json(); match res { Ok((status, body)) => { let res = create_response(&state, status, APPLICATION_JSON, body); Box::new(ok((state, res))) }, Err(e) => Box::new(err((state, e.into_handler_error()))) } } fn handle_with_body(mut state : State, get_result : F) -> Box where Body : DeserializeOwned, F : FnOnce(&mut State, Body) -> R + Send + 'static, R : ResourceResult { let f = hyper::Body::take_from(&mut state) .concat2() .then(|body| { let body = match body { Ok(body) => body, Err(e) => return err((state, e.into_handler_error())) }; let body = match serde_json::from_slice(&body) { Ok(body) => body, Err(e) => return { let error : ResourceError = e.into(); match serde_json::to_string(&error) { Ok(json) => { let res = create_response(&state, StatusCode::BAD_REQUEST, APPLICATION_JSON, json); ok((state, res)) }, Err(e) => err((state, e.into_handler_error())) } } }; let res = get_result(&mut state, body).to_json(); match res { Ok((status, body)) => { let res = create_response(&state, status, APPLICATION_JSON, body); ok((state, res)) }, Err(e) => err((state, e.into_handler_error())) } }); Box::new(f) } fn read_all_handler(state : State) -> Box where Res : ResourceResult, Handler : ResourceReadAll { to_handler_future(state, |state| Handler::read_all(state)) } fn read_handler(state : State) -> Box where ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, Res : ResourceResult, Handler : ResourceRead { let id = { let path : &PathExtractor = PathExtractor::borrow_from(&state); path.id.clone() }; to_handler_future(state, |state| Handler::read(state, id)) } fn search_handler(mut state : State) -> Box where Query : ResourceType + QueryStringExtractor + Send + Sync + 'static, Res : ResourceResult, Handler : ResourceSearch { let query = Query::take_from(&mut state); to_handler_future(state, |state| Handler::search(state, query)) } fn create_handler(state : State) -> Box where Body : ResourceType, Res : ResourceResult, Handler : ResourceCreate { handle_with_body::(state, |state, body| Handler::create(state, body)) } fn update_all_handler(state : State) -> Box where Body : ResourceType, Res : ResourceResult, Handler : ResourceUpdateAll { handle_with_body::(state, |state, body| Handler::update_all(state, body)) } fn update_handler(state : State) -> Box where ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, Body : ResourceType, Res : ResourceResult, Handler : ResourceUpdate { let id = { let path : &PathExtractor = PathExtractor::borrow_from(&state); path.id.clone() }; handle_with_body::(state, |state, body| Handler::update(state, id, body)) } fn delete_all_handler(state : State) -> Box where Res : ResourceResult, Handler : ResourceDeleteAll { to_handler_future(state, |state| Handler::delete_all(state)) } fn delete_handler(state : State) -> Box where ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, Res : ResourceResult, Handler : ResourceDelete { let id = { let path : &PathExtractor = PathExtractor::borrow_from(&state); path.id.clone() }; to_handler_future(state, |state| Handler::delete(state, id)) } 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())); } } 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 { self.0.get(&self.1) .to(|state| read_all_handler::(state)); } fn read(&mut self) where ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, Res : ResourceResult, Handler : ResourceRead { self.0.get(&format!("{}/:id", self.1)) .with_path_extractor::>() .to(|state| read_handler::(state)); } fn search(&mut self) where Query : ResourceType + QueryStringExtractor + Send + Sync + 'static, Res : ResourceResult, Handler : ResourceSearch { self.0.get(&format!("{}/search", self.1)) .with_query_string_extractor::() .to(|state| search_handler::(state)); } fn create(&mut self) where Body : ResourceType, Res : ResourceResult, Handler : ResourceCreate { self.0.post(&self.1) .to(|state| create_handler::(state)); } fn update_all(&mut self) where Body : ResourceType, Res : ResourceResult, Handler : ResourceUpdateAll { self.0.put(&self.1) .to(|state| update_all_handler::(state)); } fn update(&mut self) where ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, Body : ResourceType, Res : ResourceResult, Handler : ResourceUpdate { self.0.put(&format!("{}/:id", self.1)) .with_path_extractor::>() .to(|state| update_handler::(state)); } fn delete_all(&mut self) where Res : ResourceResult, Handler : ResourceDeleteAll { self.0.delete(&self.1) .to(|state| delete_all_handler::(state)); } fn delete(&mut self) where ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static, Res : ResourceResult, Handler : ResourceDelete { self.0.delete(&format!("{}/:id", self.1)) .with_path_extractor::>() .to(|state| delete_handler::(state)); } } } } implDrawResourceRoutes!(RouterBuilder); implDrawResourceRoutes!(ScopeBuilder);