diff --git a/gotham_restful/src/lib.rs b/gotham_restful/src/lib.rs index 7b4006a..9253f3e 100644 --- a/gotham_restful/src/lib.rs +++ b/gotham_restful/src/lib.rs @@ -115,6 +115,7 @@ pub use gotham_restful_derive::*; #[doc(hidden)] pub mod export { + pub use futures_util::future::FutureExt; pub use gotham::{ hyper::body::Bytes, state::{FromState, State} diff --git a/gotham_restful/src/resource.rs b/gotham_restful/src/resource.rs index fd1c7e6..79b0de9 100644 --- a/gotham_restful/src/resource.rs +++ b/gotham_restful/src/resource.rs @@ -5,7 +5,11 @@ use gotham::{ }; use hyper::Body; use serde::de::DeserializeOwned; -use std::panic::RefUnwindSafe; +use std::{ + future::Future, + panic::RefUnwindSafe, + pin::Pin +}; /// This trait must be implemented by every RESTful Resource. It will /// allow you to register the different methods for this Resource. @@ -21,7 +25,7 @@ pub trait Resource pub trait ResourceMethod { - type Res : ResourceResult; + type Res : ResourceResult + Send + 'static; #[cfg(feature = "openapi")] fn operation_id() -> Option @@ -38,7 +42,7 @@ pub trait ResourceMethod /// Handle a GET request on the Resource root. pub trait ResourceReadAll : ResourceMethod { - fn read_all(state : &mut State) -> Self::Res; + fn read_all(state : &mut State) -> Pin + Send>>; } /// Handle a GET request on the Resource with an id. @@ -46,7 +50,7 @@ pub trait ResourceRead : ResourceMethod { type ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static; - fn read(state : &mut State, id : Self::ID) -> Self::Res; + fn read(state : &mut State, id : Self::ID) -> Pin + Send>>; } /// Handle a GET request on the Resource with additional search parameters. @@ -54,7 +58,7 @@ pub trait ResourceSearch : ResourceMethod { type Query : ResourceType + QueryStringExtractor + Sync; - fn search(state : &mut State, query : Self::Query) -> Self::Res; + fn search(state : &mut State, query : Self::Query) -> Pin + Send>>; } /// Handle a POST request on the Resource root. @@ -62,7 +66,7 @@ pub trait ResourceCreate : ResourceMethod { type Body : RequestBody; - fn create(state : &mut State, body : Self::Body) -> Self::Res; + fn create(state : &mut State, body : Self::Body) -> Pin + Send>>; } /// Handle a PUT request on the Resource root. @@ -70,7 +74,7 @@ pub trait ResourceUpdateAll : ResourceMethod { type Body : RequestBody; - fn update_all(state : &mut State, body : Self::Body) -> Self::Res; + fn update_all(state : &mut State, body : Self::Body) -> Pin + Send>>; } /// Handle a PUT request on the Resource with an id. @@ -79,13 +83,13 @@ pub trait ResourceUpdate : ResourceMethod type Body : RequestBody; type ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static; - fn update(state : &mut State, id : Self::ID, body : Self::Body) -> Self::Res; + fn update(state : &mut State, id : Self::ID, body : Self::Body) -> Pin + Send>>; } /// Handle a DELETE request on the Resource root. pub trait ResourceDeleteAll : ResourceMethod { - fn delete_all(state : &mut State) -> Self::Res; + fn delete_all(state : &mut State) -> Pin + Send>>; } /// Handle a DELETE request on the Resource with an id. @@ -93,5 +97,5 @@ pub trait ResourceDelete : ResourceMethod { type ID : DeserializeOwned + Clone + RefUnwindSafe + Send + Sync + 'static; - fn delete(state : &mut State, id : Self::ID) -> Self::Res; + fn delete(state : &mut State, id : Self::ID) -> Pin + Send>>; } diff --git a/gotham_restful/src/result.rs b/gotham_restful/src/result.rs index cb96636..a41b4c2 100644 --- a/gotham_restful/src/result.rs +++ b/gotham_restful/src/result.rs @@ -80,7 +80,7 @@ impl Response /// A trait provided to convert a resource's result to json. -pub trait ResourceResult : Send +pub trait ResourceResult { type Err : Error + Send + 'static; diff --git a/gotham_restful/src/routing.rs b/gotham_restful/src/routing.rs index 22af7fb..7fd66f0 100644 --- a/gotham_restful/src/routing.rs +++ b/gotham_restful/src/routing.rs @@ -9,7 +9,7 @@ use crate::OpenapiRouter; use futures_util::{future, future::FutureExt}; use gotham::{ - handler::{HandlerError, HandlerFuture, IntoHandlerError, IntoHandlerFuture}, + handler::{HandlerError, HandlerFuture, IntoHandlerError}, helpers::http::response::{create_empty_response, create_response}, pipeline::chain::PipelineHandleChain, router::{ @@ -32,6 +32,7 @@ use gotham::hyper::{ }; use mime::{Mime, APPLICATION_JSON}; use std::{ + future::Future, panic::RefUnwindSafe, pin::Pin }; @@ -105,27 +106,26 @@ fn response_from(res : Response, state : &State) -> hyper::Response r } -fn to_handler_future(mut state : State, get_result : F) -> Pin> +async fn to_handler_future(mut state : State, get_result : F) -> Result<(State, gotham::hyper::Response), (State, HandlerError)> where - F : FnOnce(&mut State) -> R, + F : FnOnce(&mut State) -> Pin + Send>>, R : ResourceResult { - get_result(&mut state).into_response() - .then(|res| - match res { - Ok(res) => { - let r = response_from(res, &state); - (state, r).into_handler_future() - }, - Err(e) => future::err((state, e.into_handler_error())).boxed() - } - ).boxed() + let res = get_result(&mut state).await; + let res = res.into_response().await; + match res { + Ok(res) => { + let r = response_from(res, &state); + Ok((state, r)) + }, + Err(e) => Err((state, e.into_handler_error())) + } } async fn body_to_res(mut state : State, get_result : F) -> (State, Result, HandlerError>) where B : RequestBody, - F : FnOnce(&mut State, B) -> R, + F : FnOnce(&mut State, B) -> Pin + Send>>, R : ResourceResult { let body = to_bytes(Body::take_from(&mut state)).await; @@ -160,8 +160,11 @@ where }; get_result(&mut state, body) }; - - let res = match res.into_response().await { + + let res = res.await; + let res = res.into_response().await; + + let res = match res { Ok(res) => { let r = response_from(res, &state); Ok(r) @@ -174,8 +177,8 @@ where fn handle_with_body(state : State, get_result : F) -> Pin> where B : RequestBody + 'static, - F : FnOnce(&mut State, B) -> R + Send + 'static, - R : ResourceResult + 'static + F : FnOnce(&mut State, B) -> Pin + Send>> + Send + 'static, + R : ResourceResult + Send + 'static { body_to_res(state, get_result) .then(|(state, res)| match res { @@ -187,7 +190,7 @@ where fn read_all_handler(state : State) -> Pin> { - to_handler_future(state, |state| Handler::read_all(state)) + to_handler_future(state, |state| Handler::read_all(state)).boxed() } fn read_handler(state : State) -> Pin> @@ -196,13 +199,13 @@ fn read_handler(state : State) -> Pin let path : &PathExtractor = PathExtractor::borrow_from(&state); path.id.clone() }; - to_handler_future(state, |state| Handler::read(state, id)) + to_handler_future(state, |state| Handler::read(state, id)).boxed() } fn search_handler(mut state : State) -> Pin> { let query = Handler::Query::take_from(&mut state); - to_handler_future(state, |state| Handler::search(state, query)) + to_handler_future(state, |state| Handler::search(state, query)).boxed() } fn create_handler(state : State) -> Pin> @@ -235,7 +238,7 @@ where fn delete_all_handler(state : State) -> Pin> { - to_handler_future(state, |state| Handler::delete_all(state)) + to_handler_future(state, |state| Handler::delete_all(state)).boxed() } fn delete_handler(state : State) -> Pin> @@ -244,7 +247,7 @@ fn delete_handler(state : State) -> Pin = PathExtractor::borrow_from(&state); path.id.clone() }; - to_handler_future(state, |state| Handler::delete(state, id)) + to_handler_future(state, |state| Handler::delete(state, id)).boxed() } #[derive(Clone)] diff --git a/gotham_restful_derive/src/method.rs b/gotham_restful_derive/src/method.rs index a4c3fb8..9ca87c6 100644 --- a/gotham_restful_derive/src/method.rs +++ b/gotham_restful_derive/src/method.rs @@ -302,6 +302,12 @@ fn expand(method : Method, attrs : TokenStream, item : TokenStream) -> Result(item)?; let fun_ident = &fun.sig.ident; let fun_vis = &fun.vis; + let fun_is_async = fun.sig.asyncness.is_some(); + + if let Some(unsafety) = fun.sig.unsafety + { + return Err(Error::new(unsafety.span(), "Resource methods must not be unsafe")); + } let trait_ident = method.trait_ident(); let method_ident = method.fn_ident(); @@ -374,6 +380,11 @@ fn expand(method : Method, attrs : TokenStream, item : TokenStream) -> Result Result>::borrow_from(&#state_ident).clone(); + }; + block = quote! { #repo_ident.run::<_, #ret, ()>(move |#conn_ident| { Ok({#block}) - }).wait().unwrap() + }).await.unwrap() }; } if let Some(arg) = args.iter().filter(|arg| (*arg).ty.is_auth_status()).nth(0) { let auth_ty = arg.ty.quote_ty(); - block = quote! { + state_block = quote! { + #state_block let #auth_ident : #auth_ty = <#auth_ty>::borrow_from(#state_ident).clone(); - #block }; } @@ -416,6 +430,10 @@ fn expand(method : Method, attrs : TokenStream, item : TokenStream) -> Result Result #ret + fn #method_ident(#(#args_def),*) -> Pin + Send>> { #[allow(unused_imports)] - use #krate::export::FromState; + use #krate::export::{FromState, FutureExt}; - #block + #state_block + + async move { + #block + }.boxed() } }