diff --git a/doc/.timestamp b/doc/.timestamp index a3bcc7ef4..726536ed0 100644 --- a/doc/.timestamp +++ b/doc/.timestamp @@ -1 +1 @@ -Thu Aug 3 13:30:53 UTC 2023 +Thu Aug 3 13:35:36 UTC 2023 diff --git a/doc/gotham_restful/enum.AuthErrorOrOther.html b/doc/gotham_restful/enum.AuthErrorOrOther.html index d3a1c20fb..4433f30e4 100644 --- a/doc/gotham_restful/enum.AuthErrorOrOther.html +++ b/doc/gotham_restful/enum.AuthErrorOrOther.html @@ -7,8 +7,8 @@ from an authentication error, or delegates to another error type. This type is best used with AuthResult.

Variants§

§

Forbidden(AuthError)

§

Other(E)

Trait Implementations§

source§

impl<E: Clone> Clone for AuthErrorOrOther<E>

source§

fn clone(&self) -> AuthErrorOrOther<E>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<E: Debug> Debug for AuthErrorOrOther<E>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<E> From<AuthError> for AuthErrorOrOther<E>where - AuthError: IntoResponseError,

source§

fn from(arg0: AuthError) -> Self

Converts to this type from the input type.
source§

impl<E, F> From<F> for AuthErrorOrOther<E>where - F: Sealed + Into<E>,

source§

fn from(err: F) -> Self

Converts to this type from the input type.
source§

impl<E> IntoResponseError for AuthErrorOrOther<E>where + AuthError: IntoResponseError,

source§

fn from(arg0: AuthError) -> Self

Converts to this type from the input type.
source§

impl<E, F> From<F> for AuthErrorOrOther<E>where + F: Sealed + Into<E>,

source§

fn from(err: F) -> Self

Converts to this type from the input type.
source§

impl<E> IntoResponseError for AuthErrorOrOther<E>where AuthError: IntoResponseError,

Auto Trait Implementations§

§

impl<E> RefUnwindSafe for AuthErrorOrOther<E>where E: RefUnwindSafe,

§

impl<E> Send for AuthErrorOrOther<E>where E: Send,

§

impl<E> Sync for AuthErrorOrOther<E>where diff --git a/doc/gotham_restful/struct.Raw.html b/doc/gotham_restful/struct.Raw.html index 1cf3222fd..1214dbbf1 100644 --- a/doc/gotham_restful/struct.Raw.html +++ b/doc/gotham_restful/struct.Raw.html @@ -24,9 +24,9 @@ Self: Send,

§

type Err = Error

source§

fn into_response( self ) -> Pin<Box<dyn Future<Output = Result<Response, SerdeJsonError>> + Send>>

Turn this into a response that can be returned to the browser. This api will likely -change in the future.
source§

fn accepted_types() -> Option<Vec<Mime>>

Return a list of supported mime types.
source§

impl<T> OpenapiType for Raw<T>

source§

fn visit_type<V: Visitor>(visitor: &mut V)

§

fn schema() -> OpenapiSchema

source§

impl<T> RequestBody for Raw<T>where +change in the future.

source§

fn accepted_types() -> Option<Vec<Mime>>

Return a list of supported mime types.
source§

impl<T> OpenapiType for Raw<T>

source§

fn visit_type<V: Visitor>(visitor: &mut V)

§

fn schema() -> OpenapiSchema

source§

impl<T> RequestBody for Raw<T>where Raw<T>: FromBody + ResourceType,

source§

fn supported_types() -> Option<Vec<Mime>>

Return all types that are supported as content types. Use None if all types are supported.
source§

impl<T: Into<Body>> ResponseSchema for Raw<T>where - Self: Send,

source§

fn schema(code: StatusCode) -> OpenapiSchema

Return the schema of the response for the given status code. The code may + Self: Send,
source§

fn schema(code: StatusCode) -> OpenapiSchema

Return the schema of the response for the given status code. The code may only be one that was previously returned by Self::status_codes. The implementation should panic if that is not the case.
source§

fn status_codes() -> Vec<StatusCode>

All status codes returned by this response. Returns [StatusCode::OK] by default.

Auto Trait Implementations§

§

impl<T> RefUnwindSafe for Raw<T>where T: RefUnwindSafe,

§

impl<T> Send for Raw<T>where diff --git a/doc/gotham_restful/trait.DrawResourceRoutes.html b/doc/gotham_restful/trait.DrawResourceRoutes.html index 2e488a521..bea12484b 100644 --- a/doc/gotham_restful/trait.DrawResourceRoutes.html +++ b/doc/gotham_restful/trait.DrawResourceRoutes.html @@ -5,8 +5,8 @@ fn endpoint<E: 'static + Endpoint>(&mut self); }
Expand description

This trait allows to draw routes within an resource. Use this only inside the Resource::setup method.

-

Required Methods§

source

fn endpoint<E: 'static + Endpoint>(&mut self)

Implementations on Foreign Types§

source§

impl<'a, C, P> DrawResourceRoutes for (&mut ScopeBuilder<'a, C, P>, &str)where +

Required Methods§

source

fn endpoint<E: 'static + Endpoint>(&mut self)

Implementations on Foreign Types§

source§

impl<'a, C, P> DrawResourceRoutes for (&mut RouterBuilder<'a, C, P>, &str)where C: PipelineHandleChain<P> + Copy + Send + Sync + 'static, - P: RefUnwindSafe + Send + Sync + 'static,

source§

fn endpoint<E: Endpoint + 'static>(&mut self)

source§

impl<'a, C, P> DrawResourceRoutes for (&mut RouterBuilder<'a, C, P>, &str)where + P: RefUnwindSafe + Send + Sync + 'static,

source§

fn endpoint<E: Endpoint + 'static>(&mut self)

source§

impl<'a, C, P> DrawResourceRoutes for (&mut ScopeBuilder<'a, C, P>, &str)where C: PipelineHandleChain<P> + Copy + Send + Sync + 'static, - P: RefUnwindSafe + Send + Sync + 'static,

source§

fn endpoint<E: Endpoint + 'static>(&mut self)

Implementors§

\ No newline at end of file + P: RefUnwindSafe + Send + Sync + 'static,
source§

fn endpoint<E: Endpoint + 'static>(&mut self)

Implementors§

\ No newline at end of file diff --git a/doc/gotham_restful/trait.IntoResponse.html b/doc/gotham_restful/trait.IntoResponse.html index a3a54ebbf..9b48555c0 100644 --- a/doc/gotham_restful/trait.IntoResponse.html +++ b/doc/gotham_restful/trait.IntoResponse.html @@ -17,17 +17,17 @@ R: ResponseBody, E: Debug + IntoResponseError<Err = Error>,
§

type Err = <E as IntoResponseError>::Err

source§

fn into_response( self -) -> Pin<Box<dyn Future<Output = Result<Response, E::Err>> + Send>>

source§

fn accepted_types() -> Option<Vec<Mime>>

source§

impl<T, E> IntoResponse for Result<Raw<T>, E>where - Raw<T>: IntoResponse, - E: Debug + IntoResponseError<Err = <Raw<T> as IntoResponse>::Err>,

§

type Err = <E as IntoResponseError>::Err

source§

fn into_response( +) -> Pin<Box<dyn Future<Output = Result<Response, E::Err>> + Send>>

source§

fn accepted_types() -> Option<Vec<Mime>>

source§

impl<E> IntoResponse for Result<NoContent, E>where + E: Debug + IntoResponseError<Err = Error>,

§

type Err = Error

source§

fn into_response( self -) -> Pin<Box<dyn Future<Output = Result<Response, E::Err>> + Send>>

source§

impl<E> IntoResponse for Result<Redirect, E>where +) -> Pin<Box<dyn Future<Output = Result<Response, Error>> + Send>>

source§

fn accepted_types() -> Option<Vec<Mime>>

source§

impl<E> IntoResponse for Result<Redirect, E>where E: Debug + IntoResponseError, <E as IntoResponseError>::Err: StdError + Sync,

§

type Err = RedirectError<<E as IntoResponseError>::Err>

source§

fn into_response(self) -> BoxFuture<'static, Result<Response, Self::Err>>

source§

impl<Res> IntoResponse for Pin<Box<dyn Future<Output = Res> + Send>>where Res: IntoResponse + 'static,

§

type Err = <Res as IntoResponse>::Err

source§

fn into_response( self -) -> Pin<Box<dyn Future<Output = Result<Response, Self::Err>> + Send>>

source§

fn accepted_types() -> Option<Vec<Mime>>

source§

impl<E> IntoResponse for Result<NoContent, E>where - E: Debug + IntoResponseError<Err = Error>,

§

type Err = Error

source§

fn into_response( +) -> Pin<Box<dyn Future<Output = Result<Response, Self::Err>> + Send>>

source§

fn accepted_types() -> Option<Vec<Mime>>

source§

impl<T, E> IntoResponse for Result<Raw<T>, E>where + Raw<T>: IntoResponse, + E: Debug + IntoResponseError<Err = <Raw<T> as IntoResponse>::Err>,

§

type Err = <E as IntoResponseError>::Err

source§

fn into_response( self -) -> Pin<Box<dyn Future<Output = Result<Response, Error>> + Send>>

source§

fn accepted_types() -> Option<Vec<Mime>>

Implementors§

source§

impl IntoResponse for NoContent

§

type Err = Error

source§

impl IntoResponse for Redirect

source§

impl IntoResponse for Response

source§

impl<T: Into<Body>> IntoResponse for Raw<T>where +) -> Pin<Box<dyn Future<Output = Result<Response, E::Err>> + Send>>

Implementors§

source§

impl IntoResponse for NoContent

§

type Err = Error

source§

impl IntoResponse for Redirect

source§

impl IntoResponse for Response

source§

impl<T: Into<Body>> IntoResponse for Raw<T>where Self: Send,

§

type Err = Error

source§

impl<T: ResponseBody> IntoResponse for Success<T>

§

type Err = Error

\ No newline at end of file diff --git a/doc/gotham_restful/trait.ResponseSchema.html b/doc/gotham_restful/trait.ResponseSchema.html index 3183111b1..f211f1df3 100644 --- a/doc/gotham_restful/trait.ResponseSchema.html +++ b/doc/gotham_restful/trait.ResponseSchema.html @@ -11,13 +11,13 @@ only be one that was previously returned by Self::status_codes. The implementation should panic if that is not the case.

Provided Methods§

source

fn status_codes() -> Vec<StatusCode>

All status codes returned by this response. Returns [StatusCode::OK] by default.

-

Implementations on Foreign Types§

source§

impl<Res> ResponseSchema for Pin<Box<dyn Future<Output = Res> + Send>>where - Res: ResponseSchema,

source§

fn status_codes() -> Vec<StatusCode>

source§

fn schema(code: StatusCode) -> OpenapiSchema

source§

impl<T, E> ResponseSchema for Result<Raw<T>, E>where - Raw<T>: IntoResponseWithSchema, - E: Debug + IntoResponseError<Err = <Raw<T> as IntoResponse>::Err>,

source§

fn status_codes() -> Vec<StatusCode>

source§

fn schema(code: StatusCode) -> OpenapiSchema

source§

impl<E> ResponseSchema for Result<NoContent, E>where - E: Debug + IntoResponseError<Err = Error>,

source§

fn status_codes() -> Vec<StatusCode>

source§

fn schema(code: StatusCode) -> OpenapiSchema

source§

impl<E> ResponseSchema for Result<Redirect, E>where +

Implementations on Foreign Types§

source§

impl<E> ResponseSchema for Result<Redirect, E>where E: Debug + IntoResponseError, - <E as IntoResponseError>::Err: StdError + Sync,

source§

fn status_codes() -> Vec<StatusCode>

source§

fn schema(code: StatusCode) -> OpenapiSchema

source§

impl<R, E> ResponseSchema for Result<R, E>where + <E as IntoResponseError>::Err: StdError + Sync,

source§

fn status_codes() -> Vec<StatusCode>

source§

fn schema(code: StatusCode) -> OpenapiSchema

source§

impl<T, E> ResponseSchema for Result<Raw<T>, E>where + Raw<T>: IntoResponseWithSchema, + E: Debug + IntoResponseError<Err = <Raw<T> as IntoResponse>::Err>,

source§

fn status_codes() -> Vec<StatusCode>

source§

fn schema(code: StatusCode) -> OpenapiSchema

source§

impl<R, E> ResponseSchema for Result<R, E>where R: ResponseBody, - E: Debug + IntoResponseError<Err = Error>,

source§

fn status_codes() -> Vec<StatusCode>

source§

fn schema(code: StatusCode) -> OpenapiSchema

Implementors§

source§

impl ResponseSchema for NoContent

source§

impl ResponseSchema for Redirect

source§

impl<T: Into<Body>> ResponseSchema for Raw<T>where + E: Debug + IntoResponseError<Err = Error>,

source§

fn status_codes() -> Vec<StatusCode>

source§

fn schema(code: StatusCode) -> OpenapiSchema

source§

impl<Res> ResponseSchema for Pin<Box<dyn Future<Output = Res> + Send>>where + Res: ResponseSchema,

source§

fn status_codes() -> Vec<StatusCode>

source§

fn schema(code: StatusCode) -> OpenapiSchema

source§

impl<E> ResponseSchema for Result<NoContent, E>where + E: Debug + IntoResponseError<Err = Error>,

source§

fn status_codes() -> Vec<StatusCode>

source§

fn schema(code: StatusCode) -> OpenapiSchema

Implementors§

source§

impl ResponseSchema for NoContent

source§

impl ResponseSchema for Redirect

source§

impl<T: Into<Body>> ResponseSchema for Raw<T>where Self: Send,

source§

impl<T: ResponseBody> ResponseSchema for Success<T>

\ No newline at end of file diff --git a/doc/implementors/core/clone/trait.Clone.js b/doc/implementors/core/clone/trait.Clone.js index 98a9534a5..246fe74e3 100644 --- a/doc/implementors/core/clone/trait.Clone.js +++ b/doc/implementors/core/clone/trait.Clone.js @@ -1,3 +1,3 @@ (function() {var implementors = { -"gotham_restful":[["impl Clone for NoopExtractor"],["impl Clone for Origin"],["impl<T: Clone> Clone for Success<T>"],["impl Clone for StaticAuthHandler"],["impl Clone for NoContent"],["impl Clone for CorsConfig"],["impl<Data, Handler> Clone for AuthMiddleware<Data, Handler>where\n Handler: Clone,"],["impl<T> Clone for AuthStatus<T>where\n T: Clone + Send + 'static,"],["impl Clone for AuthError"],["impl Clone for OpenapiInfo"],["impl<E: Clone> Clone for AuthErrorOrOther<E>"],["impl Clone for Headers"],["impl Clone for AuthSource"],["impl<T: Clone> Clone for Raw<T>"],["impl Clone for Redirect"]] +"gotham_restful":[["impl Clone for AuthSource"],["impl Clone for Headers"],["impl Clone for AuthError"],["impl<E: Clone> Clone for AuthErrorOrOther<E>"],["impl Clone for Redirect"],["impl Clone for CorsConfig"],["impl Clone for Origin"],["impl Clone for OpenapiInfo"],["impl Clone for StaticAuthHandler"],["impl Clone for NoopExtractor"],["impl Clone for NoContent"],["impl<T: Clone> Clone for Success<T>"],["impl<Data, Handler> Clone for AuthMiddleware<Data, Handler>where\n Handler: Clone,"],["impl<T> Clone for AuthStatus<T>where\n T: Clone + Send + 'static,"],["impl<T: Clone> Clone for Raw<T>"]] };if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/doc/implementors/core/convert/trait.From.js b/doc/implementors/core/convert/trait.From.js index 634a1bc83..651428572 100644 --- a/doc/implementors/core/convert/trait.From.js +++ b/doc/implementors/core/convert/trait.From.js @@ -1,3 +1,3 @@ (function() {var implementors = { -"gotham_restful":[["impl<E> From<AuthError> for AuthErrorOrOther<E>where\n AuthError: IntoResponseError,"],["impl<E, F> From<F> for AuthErrorOrOther<E>where\n F: Sealed + Into<E>,"],["impl From<()> for NoContent"],["impl<T> From<T> for Success<T>"]] +"gotham_restful":[["impl<T> From<T> for Success<T>"],["impl<E, F> From<F> for AuthErrorOrOther<E>where\n F: Sealed + Into<E>,"],["impl From<()> for NoContent"],["impl<E> From<AuthError> for AuthErrorOrOther<E>where\n AuthError: IntoResponseError,"]] };if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/doc/implementors/core/default/trait.Default.js b/doc/implementors/core/default/trait.Default.js index a97c003cb..dd234fc4f 100644 --- a/doc/implementors/core/default/trait.Default.js +++ b/doc/implementors/core/default/trait.Default.js @@ -1,3 +1,3 @@ (function() {var implementors = { -"gotham_restful":[["impl Default for NoContent"],["impl Default for Headers"],["impl<T: Default> Default for Success<T>"],["impl Default for CorsConfig"],["impl Default for Origin"],["impl Default for Redirect"]] +"gotham_restful":[["impl Default for Origin"],["impl<T: Default> Default for Success<T>"],["impl Default for Redirect"],["impl Default for Headers"],["impl Default for CorsConfig"],["impl Default for NoContent"]] };if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/doc/implementors/core/fmt/trait.Debug.js b/doc/implementors/core/fmt/trait.Debug.js index a02be6a53..52dd8a2d2 100644 --- a/doc/implementors/core/fmt/trait.Debug.js +++ b/doc/implementors/core/fmt/trait.Debug.js @@ -1,3 +1,3 @@ (function() {var implementors = { -"gotham_restful":[["impl Debug for AuthError"],["impl Debug for AuthSource"],["impl<T: Debug> Debug for Success<T>"],["impl Debug for NoopExtractor"],["impl<Data: Debug, Handler: Debug> Debug for AuthMiddleware<Data, Handler>"],["impl Debug for Response"],["impl Debug for CorsConfig"],["impl Debug for OpenapiInfo"],["impl<T: Debug + Send + 'static> Debug for AuthStatus<T>"],["impl Debug for Headers"],["impl Debug for Origin"],["impl Debug for NoContent"],["impl<T: Debug> Debug for Raw<T>"],["impl Debug for Redirect"],["impl<E: Debug> Debug for AuthErrorOrOther<E>"],["impl Debug for StaticAuthHandler"]] +"gotham_restful":[["impl<Data: Debug, Handler: Debug> Debug for AuthMiddleware<Data, Handler>"],["impl Debug for NoopExtractor"],["impl Debug for AuthSource"],["impl Debug for Redirect"],["impl<E: Debug> Debug for AuthErrorOrOther<E>"],["impl Debug for Response"],["impl Debug for StaticAuthHandler"],["impl Debug for CorsConfig"],["impl Debug for AuthError"],["impl Debug for Headers"],["impl Debug for Origin"],["impl<T: Debug> Debug for Success<T>"],["impl<T: Debug> Debug for Raw<T>"],["impl Debug for OpenapiInfo"],["impl<T: Debug + Send + 'static> Debug for AuthStatus<T>"],["impl Debug for NoContent"]] };if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/doc/implementors/gotham/middleware/trait.Middleware.js b/doc/implementors/gotham/middleware/trait.Middleware.js index 75cd3fe1d..2403df2a0 100644 --- a/doc/implementors/gotham/middleware/trait.Middleware.js +++ b/doc/implementors/gotham/middleware/trait.Middleware.js @@ -1,3 +1,3 @@ (function() {var implementors = { -"gotham_restful":[["impl<Data, Handler> Middleware for AuthMiddleware<Data, Handler>where\n Data: DeserializeOwned + Send + 'static,\n Handler: AuthHandler<Data>,"],["impl Middleware for CorsConfig"]] +"gotham_restful":[["impl Middleware for CorsConfig"],["impl<Data, Handler> Middleware for AuthMiddleware<Data, Handler>where\n Data: DeserializeOwned + Send + 'static,\n Handler: AuthHandler<Data>,"]] };if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/doc/implementors/gotham/middleware/trait.NewMiddleware.js b/doc/implementors/gotham/middleware/trait.NewMiddleware.js index 3f166b7a0..30268ff96 100644 --- a/doc/implementors/gotham/middleware/trait.NewMiddleware.js +++ b/doc/implementors/gotham/middleware/trait.NewMiddleware.js @@ -1,3 +1,3 @@ (function() {var implementors = { -"gotham_restful":[["impl<Data, Handler> NewMiddleware for AuthMiddleware<Data, Handler>where\n Self: Clone + Middleware + Sync + RefUnwindSafe,"],["impl NewMiddleware for CorsConfig"]] +"gotham_restful":[["impl NewMiddleware for CorsConfig"],["impl<Data, Handler> NewMiddleware for AuthMiddleware<Data, Handler>where\n Self: Clone + Middleware + Sync + RefUnwindSafe,"]] };if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/doc/implementors/gotham/state/data/trait.StateData.js b/doc/implementors/gotham/state/data/trait.StateData.js index 4be554eed..6e19f13e2 100644 --- a/doc/implementors/gotham/state/data/trait.StateData.js +++ b/doc/implementors/gotham/state/data/trait.StateData.js @@ -1,3 +1,3 @@ (function() {var implementors = { -"gotham_restful":[["impl<T: Send + 'static> StateData for AuthStatus<T>"],["impl StateData for AuthSource"],["impl StateData for NoopExtractor"],["impl StateData for CorsConfig"]] +"gotham_restful":[["impl StateData for AuthSource"],["impl StateData for NoopExtractor"],["impl<T: Send + 'static> StateData for AuthStatus<T>"],["impl StateData for CorsConfig"]] };if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/doc/search-index.js b/doc/search-index.js index 224057863..0f06d028c 100644 --- a/doc/search-index.js +++ b/doc/search-index.js @@ -1,5 +1,5 @@ var searchIndex = JSON.parse('{\ -"gotham_restful":{"doc":"This crate is an extension to the popular gotham web …","t":"DEIDGEEGGNNQQNCCIIIIIIQQQNIYINIIINDDDNQQQQQQDDIYIYYIDIIDDNNILLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLAXLLLXXLLLKKXLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKLLLLCKKCLLLLLLLLLKKLLLLLLLLLLLLLKLLLLLKLLLKLLMLLLLLLLLLLLLLLLKKLLKMXXKKKKLLLLLLXKKLKLLLLLLLLLLLLMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLXXKKMMLLLLLLKNNDIENNNENNLLLLLLLLLLLLLLLKMLLLLLLLLLFMLLLMLMLLLLLLLLLLLLLLLL","n":["AuthError","AuthErrorOrOther","AuthHandler","AuthMiddleware","AuthResult","AuthSource","AuthStatus","AuthSuccess","AuthValidation","Authenticated","AuthorizationHeader","Body","Body","Cookie","CorsConfig","CorsRoute","DrawResourceRoutes","DrawResourceRoutesWithSchema","DrawResources","DrawResourcesWithSchema","Endpoint","EndpointWithSchema","Err","Err","Err","Forbidden","FromBody","FromBody","GetOpenapi","Header","IntoResponse","IntoResponseError","IntoResponseWithSchema","Invalid","NoContent","NoopExtractor","OpenapiInfo","Other","Output","Output","Params","Params","Placeholders","Placeholders","Raw","Redirect","RequestBody","RequestBody","Resource","Resource","ResourceError","ResourceWithSchema","Response","ResponseBody","ResponseSchema","StaticAuthHandler","Success","Unauthenticated","Unknown","WithOpenapi","accepted_types","accepted_types","accepted_types","accepted_types","as_mut","as_ref","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_from","borrow_from","borrow_from","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut_from","borrow_mut_from","borrow_mut_from","call","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","cors","create","default","default","default","delete","delete_all","description","description","deserialize","endpoint","endpoint","endpoint","extend","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","forbidden","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from_array","from_body","from_body","from_body","from_source","from_vec","gotham","handle","handle","handle_cors","has_placeholders","has_placeholders","has_placeholders","has_placeholders","header","header","header","headers_mut","headers_mut","http_method","http_method","into","into","into","into","into","into","into","into","into","into","into","into","into","into_response","into_response","into_response","into_response","into_response","into_response","into_response_error","into_response_error","into_response_error","json","jwt_secret","jwt_secret","mime","mime","needs_body","needs_body","needs_body","needs_body","needs_params","needs_params","needs_params","needs_params","new","new","new","new","new_middleware","no_content","ok","openapi_doc","openapi_spec","operation_id","operation_id","operation_verb","raw","read","read_all","resource","resource","schema","schema","schema","schema","schema","schema","schema","schema","search","setup","setup","status","status_codes","status_codes","status_codes","status_codes","status_codes","status_codes","status_codes","supported_types","supported_types","supported_types","take_from","take_from","take_from","title","to","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","try_borrow_from","try_borrow_from","try_borrow_from","try_borrow_mut_from","try_borrow_mut_from","try_borrow_mut_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_take_from","try_take_from","try_take_from","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","update","update_all","uri","uri","urls","version","visit_type","visit_type","wants_auth","wants_auth","wants_auth","wants_auth","with_openapi","Copy","Copy","CorsConfig","CorsRoute","Headers","List","None","None","Origin","Single","Star","borrow","borrow","borrow","borrow_from","borrow_mut","borrow_mut","borrow_mut","borrow_mut_from","call","clone","clone","clone","clone_into","clone_into","clone_into","cors","credentials","default","default","default","fmt","fmt","fmt","from","from","from","handle_cors","headers","into","into","into","max_age","new_middleware","origin","take_from","to_owned","to_owned","to_owned","try_borrow_from","try_borrow_mut_from","try_from","try_from","try_from","try_into","try_into","try_into","try_take_from","type_id","type_id","type_id"],"q":[[0,"gotham_restful"],[343,"gotham_restful::cors"]],"d":["This is an error type that always yields a 403 Forbidden …","This is an error type that either yields a 403 Forbidden …","This trait will help the auth middleware to determine the …","This is the auth middleware. To use it, first make sure …","This return type can be used to wrap any type implementing …","The source of the authentication token in the request.","The authentication status returned by the auth middleware …","This return type can be used to wrap any type implementing …","","The request has been performed with a valid …","Take the token from the HTTP Authorization header. This is …","The type to parse the body into. Use () if needs_body() …","The type to parse the body into. Use () if needs_body() …","Take the token from a cookie with the given name.","","","This trait allows to draw routes within an resource. Use …","This trait allows to draw routes within an resource. Use …","This trait adds the resource method to gotham’s routing. …","This trait adds the resource method to gotham’s routing. …","","","","","The error type returned by the conversion if it was …","","This trait should be implemented for every type that can …","","This trait adds the openapi_spec and openapi_doc method to …","Take the token from a header with the given name.","This trait needs to be implemented by every type returned …","","A trait provided to convert a resource’s result to json, …","The request has been performed with an invalid …","This is the return type of a resource that doesn’t …","A no-op extractor that can be used as a default type for …","","","The output type that provides the response.","The output type that provides the response.","The type that parses the request parameters. Use …","The type that parses the request parameters. Use …","The type that parses the URI placeholders. Use …","The type that parses the URI placeholders. Use …","This type can be used both as a raw request body, as well …","This is the return type of a resource that only returns a …","A type that can be used inside a request body. Implemented …","","This trait must be implemented for every resource. It …","","","This trait must be implemented for every resource. It …","A response, used to create the final gotham response from.","A type that can be used inside a response body. …","Additional details for IntoResponse to be used with an …","An AuthHandler returning always the same secret. See …","This can be returned from a resource when there is no …","The request has been performed without any kind of …","The auth status is unknown. This is likely because no …","This trait adds the with_openapi method to gotham’s …","Return a list of supported mime types.","Return a list of supported mime types.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Add a description to the openapi specification. Usually …","Add a description to the openapi specification. Usually …","","","","","","","","","","","","","","","","","","","Create an empty 403 Forbidden Response.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","Returns the argument unchanged.","","Perform the conversion.","","","","","","The handler for this endpoint.","The handler for this endpoint.","","Returns true iff the URI contains placeholders. false by …","Returns true iff the URI contains placeholders. false by …","Returns true iff the URI contains placeholders. false by …","Returns true iff the URI contains placeholders. false by …","Set a custom HTTP header. If a header with this name was …","Set a custom HTTP header. If a header with this name was …","Add an HTTP header to the Response.","Allow manipulating HTTP headers.","Allow manipulating HTTP headers.","The HTTP Verb of this endpoint.","The HTTP Verb of this endpoint.","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Turn this into a response that can be returned to the …","This will always be a 204 No Content together with an …","","","","","","","","Create a Response with mime type json from already …","Return the SHA256-HMAC secret used to verify the JWT token.","","Return the mime type of this Response.","","Returns true iff the request body should be parsed. false …","Returns true iff the request body should be parsed. false …","Returns true iff the request body should be parsed. false …","Returns true iff the request body should be parsed. false …","Returns true iff the request parameters should be parsed. …","Returns true iff the request parameters should be parsed. …","Returns true iff the request parameters should be parsed. …","Returns true iff the request parameters should be parsed. …","","","","Create a new Response from raw data.","","Create a 204 No Content Response.","","Register a GET route to path that returns the OpenAPI …","Register a GET route to path that returns the OpenAPI …","Replace the automatically generated operation id with a …","Replace the automatically generated operation id with a …","The verb used for generating an operation id if …","","","","","","","Return the schema of the response for the given status …","","","Returns the schema of the () type.","","","","","Register all methods handled by this resource with the …","Register all methods handled by this resource with the …","Return the status code of this Response.","","All status codes returned by this response. Returns …","All status codes returned by this response. Returns …","","","","","Return all types that are supported as content types. Use …","Return all types that are supported as content types. Use …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","The URI that this endpoint listens on in gotham’s format.","The URI that this endpoint listens on in gotham’s format.","","","","","Returns true if the request wants to know the auth status …","Returns true if the request wants to know the auth status …","Returns true if the request wants to know the auth status …","Returns true if the request wants to know the auth status …","","Copy the Origin header into the Access-Control-Allow-Origin…","Copy the Access-Control-Request-Headers header into the …","This is the configuration that the CORS handler will …","Add CORS routing for your path. This is required for …","Specify the allowed headers of the request. It is up to …","Set the Access-Control-Allow-Headers header to the …","Do not send any Access-Control-Allow-Origin headers.","Do not send any Access-Control-Allow-Headers headers.","Specify the allowed origins of the request. It is up to …","Set the Access-Control-Allow-Origin header to a single …","Send Access-Control-Allow-Origin: *. Note that browser …","","","","","","","","","","","","","","","","Handle a preflight request on path for method. To …","Whether or not the request may be made with supplying …","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Handle CORS for a non-preflight request. This means …","The allowed headers.","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","The amount of seconds that the preflight request can be …","","The allowed origins.","","","","","","","","","","","","","","","",""],"i":[0,0,0,0,0,0,0,0,0,17,18,66,67,18,0,0,0,0,0,0,0,0,68,69,70,23,0,0,0,18,0,0,0,17,0,0,0,23,66,67,66,67,66,67,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17,0,69,69,24,26,5,5,17,18,19,11,20,21,22,23,24,5,25,26,36,17,18,21,17,18,19,11,20,21,22,23,24,5,25,26,36,17,18,21,11,17,18,19,11,20,21,22,23,24,5,25,26,17,18,19,11,20,21,22,23,24,5,25,26,0,0,24,25,26,0,0,67,67,21,57,58,0,21,17,18,19,11,20,21,22,23,24,5,25,26,36,36,17,18,19,11,20,21,22,23,23,23,23,24,24,5,25,26,26,26,36,19,70,21,5,11,19,0,66,67,0,66,66,67,67,24,26,36,24,26,66,67,17,18,19,11,20,21,22,23,24,5,25,26,36,69,24,5,25,26,36,68,22,23,36,10,19,36,5,66,66,67,67,66,66,67,67,11,22,5,36,11,36,17,71,71,67,67,67,5,0,0,72,73,68,74,22,23,24,5,25,26,0,75,76,36,68,74,74,22,23,24,25,77,77,21,17,18,21,20,25,17,18,19,11,20,21,22,23,24,5,25,26,17,18,21,17,18,21,17,18,19,11,20,21,22,23,24,5,25,26,36,17,18,19,11,20,21,22,23,24,5,25,26,36,17,18,21,17,18,19,11,20,21,22,23,24,5,25,26,36,0,0,66,67,20,20,21,5,66,66,67,67,78,64,65,0,0,0,65,64,65,0,64,64,64,65,63,63,64,65,63,63,63,64,65,63,64,65,63,79,63,64,65,63,64,65,63,64,65,63,0,63,64,65,63,63,63,63,63,64,65,63,63,63,64,65,63,64,65,63,63,64,65,63],"f":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[[],[[3,[[2,[1]]]]]],[[],[[3,[[2,[1]]]]]],[[],[[3,[[2,[1]]]]]],[[],[[3,[[2,[1]]]]]],[[[5,[4]]]],[[[5,[6]]]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[7],[7],[7],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[7],[7],[7],[[[11,[[0,[8,9]],[10,[[0,[8,9]]]]]],7,12],[[15,[[14,[13]]]]]],[[[17,[[0,[16,9]]]]],[[17,[[0,[16,9]]]]]],[18,18],[19,19],[[[11,[16]]],[[11,[16]]]],[20,20],[21,21],[22,22],[[[23,[16]]],[[23,[16]]]],[24,24],[[[5,[16]]],[[5,[16]]]],[25,25],[[[26,[16]]],[[26,[16]]]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],0,0,[[],24],[[],25],[[],[[26,[27]]]],0,0,[[],[[3,[28]]]],[[],[[3,[28]]]],[29,[[30,[21]]]],[[]],[[]],0,[[7,[32,[31]]]],[[[17,[[0,[33,9]]]],34],35],[[18,34],35],[[19,34],35],[[[11,[33,33]],34],35],[[20,34],35],[[21,34],35],[[22,34],35],[[[23,[33]],34],35],[[24,34],35],[[[5,[33]],34],35],[[25,34],35],[[[26,[33]],34],35],[[36,34],35],[[],36],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[22,23],[[[0,[0,37]]],23],[38],[[]],[[],24],[[]],[[]],[[]],[[]],[38],[[],26],[[]],[[[40,[39]]],19],[[41,1],30],[[41,1],30],[[41,1],[[30,[[5,[[42,[[40,[39]]]]]]]]]],[18,[[11,[[0,[8,9]],[0,[[10,[[0,[8,9]]]],27]]]]]],[[[2,[39]]],19],0,[[7,3],43],[[7,3],43],0,[[],44],[[],44],[[],44],[[],44],[[24,45,46]],[[26,45,46]],[[36,47,46]],[24,48],[26,48],[[],49],[[],49],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[],[[43,[[30,[36]]]]]],[24,[[15,[[14,[50]]]]]],[[[5,[[37,[31]]]]],[[15,[[14,[50]]]]]],[25,[[43,[[30,[36]]]]]],[[[26,[51]]],[[15,[[14,[50]]]]]],[36,[[43,[[30,[36]]]]]],[[],[[30,[36]]]],[22,[[30,[36]]]],[23,[[30,[36]]]],[[52,[37,[31]]],36],[[7,12],[[3,[[2,[39]]]]]],[[19,7,12],[[3,[[2,[39]]]]]],[36,[[3,[1]]]],0,[[],44],[[],44],[[],44],[[],44],[[],44],[[],44],[[],44],[[],44],[[18,53,[10,[[0,[8,9]]]]],[[11,[[0,[8,9]],[10,[[0,[8,9]]]]]]]],[[[37,[28]]],22],[1,5],[[52,[37,[31]],[3,[1]]],36],[11,[[54,[11]]]],[[],36],[[[17,[9]]],[[30,[9,22]]]],[55],[55],[[],[[3,[28]]]],[[],[[3,[28]]]],[[],[[3,[55]]]],0,0,0,[55],[55],[52,56],[52,56],[52,56],[52,56],[52,56],[52,56],[52,56],[52,56],0,[57],[58],[36,52],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[3,[[2,[1]]]]]],[[],[[3,[[2,[1]]]]]],[[],[[3,[[2,[1,59]]]]]],[7],[7],[7],0,0,[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[7,3],[7,3],[7,3],[7,3],[7,3],[7,3],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[7,3],[7,3],[7,3],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],0,0,[[],[[61,[55]]]],[[],[[61,[55]]]],0,0,[62],[62],[[],44],[[],44],[[],44],[[],44],[[20,12]],0,0,0,0,0,0,0,0,0,0,0,[[]],[[]],[[]],[7],[[]],[[]],[[]],[7],[[63,7,12],[[15,[[14,[13]]]]]],[64,64],[65,65],[63,63],[[]],[[]],[[]],[[55,49]],0,[[],64],[[],65],[[],63],[[64,34],35],[[65,34],35],[[63,34],35],[[]],[[]],[[]],[[7,[32,[31]]]],0,[[]],[[]],[[]],0,[63,[[54,[63]]]],0,[7],[[]],[[]],[[]],[7,3],[7,3],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[7,3],[[],60],[[],60],[[],60]],"c":[],"p":[[3,"Mime"],[3,"Vec"],[4,"Option"],[8,"AsMut"],[3,"Raw"],[8,"AsRef"],[3,"State"],[8,"DeserializeOwned"],[8,"Send"],[8,"AuthHandler"],[3,"AuthMiddleware"],[8,"FnOnce"],[6,"HandlerFuture"],[3,"Box"],[3,"Pin"],[8,"Clone"],[4,"AuthStatus"],[4,"AuthSource"],[3,"StaticAuthHandler"],[3,"OpenapiInfo"],[3,"NoopExtractor"],[3,"AuthError"],[4,"AuthErrorOrOther"],[3,"NoContent"],[3,"Redirect"],[3,"Success"],[8,"Default"],[3,"String"],[8,"Deserializer"],[4,"Result"],[3,"Body"],[3,"Response"],[8,"Debug"],[3,"Formatter"],[6,"Result"],[3,"Response"],[8,"Into"],[15,"never"],[15,"u8"],[15,"slice"],[3,"Bytes"],[8,"From"],[6,"BoxFuture"],[15,"bool"],[8,"IntoHeaderName"],[3,"HeaderValue"],[3,"HeaderName"],[3,"HeaderMap"],[3,"Method"],[8,"Future"],[8,"ResponseBody"],[3,"StatusCode"],[6,"AuthValidation"],[6,"Result"],[15,"str"],[3,"OpenapiSchema"],[8,"DrawResourceRoutes"],[8,"DrawResourceRoutesWithSchema"],[3,"Global"],[3,"TypeId"],[4,"Cow"],[8,"Visitor"],[3,"CorsConfig"],[4,"Origin"],[4,"Headers"],[8,"Endpoint"],[8,"EndpointWithSchema"],[8,"IntoResponseError"],[8,"IntoResponse"],[8,"FromBody"],[8,"GetOpenapi"],[8,"DrawResources"],[8,"DrawResourcesWithSchema"],[8,"ResponseSchema"],[8,"Resource"],[8,"ResourceWithSchema"],[8,"RequestBody"],[8,"WithOpenapi"],[8,"CorsRoute"]]}\ +"gotham_restful":{"doc":"This crate is an extension to the popular gotham web …","t":"DEIDGEEGGNNQQNCCIIIIIIQQQNIYINIIINDDDNQQQQQQDDIYIYYIDIIDDNNILLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLAXLLLXXLLLKKXLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKLLLLCKKCLLLLLLLLLKKLLLLLLLLLLLLLKLLLLLKLLLKLLMLLLLLLLLLLLLLLLKKLLKMXXKKKKLLLLLLXKKLKLLLLLLLLLLLLMMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLXXKKMMLLLLLLKNNDIENNNENNLLLLLLLLLLLLLLLKMLLLLLLLLLFMLLLMLMLLLLLLLLLLLLLLLL","n":["AuthError","AuthErrorOrOther","AuthHandler","AuthMiddleware","AuthResult","AuthSource","AuthStatus","AuthSuccess","AuthValidation","Authenticated","AuthorizationHeader","Body","Body","Cookie","CorsConfig","CorsRoute","DrawResourceRoutes","DrawResourceRoutesWithSchema","DrawResources","DrawResourcesWithSchema","Endpoint","EndpointWithSchema","Err","Err","Err","Forbidden","FromBody","FromBody","GetOpenapi","Header","IntoResponse","IntoResponseError","IntoResponseWithSchema","Invalid","NoContent","NoopExtractor","OpenapiInfo","Other","Output","Output","Params","Params","Placeholders","Placeholders","Raw","Redirect","RequestBody","RequestBody","Resource","Resource","ResourceError","ResourceWithSchema","Response","ResponseBody","ResponseSchema","StaticAuthHandler","Success","Unauthenticated","Unknown","WithOpenapi","accepted_types","accepted_types","accepted_types","accepted_types","as_mut","as_ref","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_from","borrow_from","borrow_from","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut_from","borrow_mut_from","borrow_mut_from","call","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","cors","create","default","default","default","delete","delete_all","description","description","deserialize","endpoint","endpoint","endpoint","extend","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","forbidden","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from","from_array","from_body","from_body","from_body","from_source","from_vec","gotham","handle","handle","handle_cors","has_placeholders","has_placeholders","has_placeholders","has_placeholders","header","header","header","headers_mut","headers_mut","http_method","http_method","into","into","into","into","into","into","into","into","into","into","into","into","into","into_response","into_response","into_response","into_response","into_response","into_response","into_response_error","into_response_error","into_response_error","json","jwt_secret","jwt_secret","mime","mime","needs_body","needs_body","needs_body","needs_body","needs_params","needs_params","needs_params","needs_params","new","new","new","new","new_middleware","no_content","ok","openapi_doc","openapi_spec","operation_id","operation_id","operation_verb","raw","read","read_all","resource","resource","schema","schema","schema","schema","schema","schema","schema","schema","search","setup","setup","status","status_codes","status_codes","status_codes","status_codes","status_codes","status_codes","status_codes","supported_types","supported_types","supported_types","take_from","take_from","take_from","title","to","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","try_borrow_from","try_borrow_from","try_borrow_from","try_borrow_mut_from","try_borrow_mut_from","try_borrow_mut_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_take_from","try_take_from","try_take_from","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","update","update_all","uri","uri","urls","version","visit_type","visit_type","wants_auth","wants_auth","wants_auth","wants_auth","with_openapi","Copy","Copy","CorsConfig","CorsRoute","Headers","List","None","None","Origin","Single","Star","borrow","borrow","borrow","borrow_from","borrow_mut","borrow_mut","borrow_mut","borrow_mut_from","call","clone","clone","clone","clone_into","clone_into","clone_into","cors","credentials","default","default","default","fmt","fmt","fmt","from","from","from","handle_cors","headers","into","into","into","max_age","new_middleware","origin","take_from","to_owned","to_owned","to_owned","try_borrow_from","try_borrow_mut_from","try_from","try_from","try_from","try_into","try_into","try_into","try_take_from","type_id","type_id","type_id"],"q":[[0,"gotham_restful"],[343,"gotham_restful::cors"]],"d":["This is an error type that always yields a 403 Forbidden …","This is an error type that either yields a 403 Forbidden …","This trait will help the auth middleware to determine the …","This is the auth middleware. To use it, first make sure …","This return type can be used to wrap any type implementing …","The source of the authentication token in the request.","The authentication status returned by the auth middleware …","This return type can be used to wrap any type implementing …","","The request has been performed with a valid …","Take the token from the HTTP Authorization header. This is …","The type to parse the body into. Use () if needs_body() …","The type to parse the body into. Use () if needs_body() …","Take the token from a cookie with the given name.","","","This trait allows to draw routes within an resource. Use …","This trait allows to draw routes within an resource. Use …","This trait adds the resource method to gotham’s routing. …","This trait adds the resource method to gotham’s routing. …","","","","","The error type returned by the conversion if it was …","","This trait should be implemented for every type that can …","","This trait adds the openapi_spec and openapi_doc method to …","Take the token from a header with the given name.","This trait needs to be implemented by every type returned …","","A trait provided to convert a resource’s result to json, …","The request has been performed with an invalid …","This is the return type of a resource that doesn’t …","A no-op extractor that can be used as a default type for …","","","The output type that provides the response.","The output type that provides the response.","The type that parses the request parameters. Use …","The type that parses the request parameters. Use …","The type that parses the URI placeholders. Use …","The type that parses the URI placeholders. Use …","This type can be used both as a raw request body, as well …","This is the return type of a resource that only returns a …","A type that can be used inside a request body. Implemented …","","This trait must be implemented for every resource. It …","","","This trait must be implemented for every resource. It …","A response, used to create the final gotham response from.","A type that can be used inside a response body. …","Additional details for IntoResponse to be used with an …","An AuthHandler returning always the same secret. See …","This can be returned from a resource when there is no …","The request has been performed without any kind of …","The auth status is unknown. This is likely because no …","This trait adds the with_openapi method to gotham’s …","Return a list of supported mime types.","Return a list of supported mime types.","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Add a description to the openapi specification. Usually …","Add a description to the openapi specification. Usually …","","","","","","","","","","","","","","","","","","","Create an empty 403 Forbidden Response.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","Returns the argument unchanged.","Returns the argument unchanged.","","Perform the conversion.","","","","","","The handler for this endpoint.","The handler for this endpoint.","","Returns true iff the URI contains placeholders. false by …","Returns true iff the URI contains placeholders. false by …","Returns true iff the URI contains placeholders. false by …","Returns true iff the URI contains placeholders. false by …","Set a custom HTTP header. If a header with this name was …","Set a custom HTTP header. If a header with this name was …","Add an HTTP header to the Response.","Allow manipulating HTTP headers.","Allow manipulating HTTP headers.","The HTTP Verb of this endpoint.","The HTTP Verb of this endpoint.","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Turn this into a response that can be returned to the …","This will always be a 204 No Content together with an …","","","","","","","","Create a Response with mime type json from already …","Return the SHA256-HMAC secret used to verify the JWT token.","","Return the mime type of this Response.","","Returns true iff the request body should be parsed. false …","Returns true iff the request body should be parsed. false …","Returns true iff the request body should be parsed. false …","Returns true iff the request body should be parsed. false …","Returns true iff the request parameters should be parsed. …","Returns true iff the request parameters should be parsed. …","Returns true iff the request parameters should be parsed. …","Returns true iff the request parameters should be parsed. …","","","","Create a new Response from raw data.","","Create a 204 No Content Response.","","Register a GET route to path that returns the OpenAPI …","Register a GET route to path that returns the OpenAPI …","Replace the automatically generated operation id with a …","Replace the automatically generated operation id with a …","The verb used for generating an operation id if …","","","","","","","Return the schema of the response for the given status …","","","Returns the schema of the () type.","","","","","Register all methods handled by this resource with the …","Register all methods handled by this resource with the …","Return the status code of this Response.","","All status codes returned by this response. Returns …","All status codes returned by this response. Returns …","","","","","Return all types that are supported as content types. Use …","Return all types that are supported as content types. Use …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","The URI that this endpoint listens on in gotham’s format.","The URI that this endpoint listens on in gotham’s format.","","","","","Returns true if the request wants to know the auth status …","Returns true if the request wants to know the auth status …","Returns true if the request wants to know the auth status …","Returns true if the request wants to know the auth status …","","Copy the Origin header into the Access-Control-Allow-Origin…","Copy the Access-Control-Request-Headers header into the …","This is the configuration that the CORS handler will …","Add CORS routing for your path. This is required for …","Specify the allowed headers of the request. It is up to …","Set the Access-Control-Allow-Headers header to the …","Do not send any Access-Control-Allow-Origin headers.","Do not send any Access-Control-Allow-Headers headers.","Specify the allowed origins of the request. It is up to …","Set the Access-Control-Allow-Origin header to a single …","Send Access-Control-Allow-Origin: *. Note that browser …","","","","","","","","","","","","","","","","Handle a preflight request on path for method. To …","Whether or not the request may be made with supplying …","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Handle CORS for a non-preflight request. This means …","The allowed headers.","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","The amount of seconds that the preflight request can be …","","The allowed origins.","","","","","","","","","","","","","","","",""],"i":[0,0,0,0,0,0,0,0,0,17,18,66,67,18,0,0,0,0,0,0,0,0,68,69,70,23,0,0,0,18,0,0,0,17,0,0,0,23,66,67,66,67,66,67,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17,0,69,69,24,26,5,5,17,18,19,11,20,21,22,23,24,5,25,26,36,17,18,21,17,18,19,11,20,21,22,23,24,5,25,26,36,17,18,21,11,17,18,19,11,20,21,22,23,24,5,25,26,17,18,19,11,20,21,22,23,24,5,25,26,0,0,24,25,26,0,0,67,67,21,57,58,0,21,17,18,19,11,20,21,22,23,24,5,25,26,36,36,17,18,19,11,20,21,22,23,23,23,23,24,24,5,25,26,26,26,36,19,70,21,5,11,19,0,66,67,0,66,66,67,67,24,26,36,24,26,66,67,17,18,19,11,20,21,22,23,24,5,25,26,36,69,24,5,25,26,36,68,22,23,36,10,19,36,5,66,66,67,67,66,66,67,67,11,22,5,36,11,36,17,71,71,67,67,67,5,0,0,72,73,68,74,22,23,24,5,25,26,0,75,76,36,68,74,74,22,23,24,25,77,77,21,17,18,21,20,25,17,18,19,11,20,21,22,23,24,5,25,26,17,18,21,17,18,21,17,18,19,11,20,21,22,23,24,5,25,26,36,17,18,19,11,20,21,22,23,24,5,25,26,36,17,18,21,17,18,19,11,20,21,22,23,24,5,25,26,36,0,0,66,67,20,20,21,5,66,66,67,67,78,64,65,0,0,0,65,64,65,0,64,64,64,65,63,63,64,65,63,63,63,64,65,63,64,65,63,79,63,64,65,63,64,65,63,64,65,63,0,63,64,65,63,63,63,63,63,64,65,63,63,63,64,65,63,64,65,63,63,64,65,63],"f":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[[],[[3,[[2,[1]]]]]],[[],[[3,[[2,[1]]]]]],[[],[[3,[[2,[1]]]]]],[[],[[3,[[2,[1]]]]]],[[[5,[4]]]],[[[5,[6]]]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[7],[7],[7],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[7],[7],[7],[[[11,[[0,[8,9]],[10,[[0,[8,9]]]]]],7,12],[[15,[[14,[13]]]]]],[[[17,[[0,[16,9]]]]],[[17,[[0,[16,9]]]]]],[18,18],[19,19],[[[11,[16]]],[[11,[16]]]],[20,20],[21,21],[22,22],[[[23,[16]]],[[23,[16]]]],[24,24],[[[5,[16]]],[[5,[16]]]],[25,25],[[[26,[16]]],[[26,[16]]]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],0,0,[[],24],[[],25],[[],[[26,[27]]]],0,0,[[],[[3,[28]]]],[[],[[3,[28]]]],[29,[[30,[21]]]],[[]],[[]],0,[[7,[32,[31]]]],[[[17,[[0,[33,9]]]],34],35],[[18,34],35],[[19,34],35],[[[11,[33,33]],34],35],[[20,34],35],[[21,34],35],[[22,34],35],[[[23,[33]],34],35],[[24,34],35],[[[5,[33]],34],35],[[25,34],35],[[[26,[33]],34],35],[[36,34],35],[[],36],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[[0,[0,37]]],23],[22,23],[38],[[]],[[],24],[[]],[[]],[[]],[[],26],[38],[[]],[[]],[[[40,[39]]],19],[[41,1],30],[[41,1],30],[[41,1],[[30,[[5,[[42,[[40,[39]]]]]]]]]],[18,[[11,[[0,[8,9]],[0,[[10,[[0,[8,9]]]],27]]]]]],[[[2,[39]]],19],0,[[7,3],43],[[7,3],43],0,[[],44],[[],44],[[],44],[[],44],[[24,45,46]],[[26,45,46]],[[36,47,46]],[24,48],[26,48],[[],49],[[],49],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[],[[43,[[30,[36]]]]]],[24,[[15,[[14,[50]]]]]],[[[5,[[37,[31]]]]],[[15,[[14,[50]]]]]],[25,[[43,[[30,[36]]]]]],[[[26,[51]]],[[15,[[14,[50]]]]]],[36,[[43,[[30,[36]]]]]],[[],[[30,[36]]]],[22,[[30,[36]]]],[23,[[30,[36]]]],[[52,[37,[31]]],36],[[7,12],[[3,[[2,[39]]]]]],[[19,7,12],[[3,[[2,[39]]]]]],[36,[[3,[1]]]],0,[[],44],[[],44],[[],44],[[],44],[[],44],[[],44],[[],44],[[],44],[[18,53,[10,[[0,[8,9]]]]],[[11,[[0,[8,9]],[10,[[0,[8,9]]]]]]]],[[[37,[28]]],22],[1,5],[[52,[37,[31]],[3,[1]]],36],[11,[[54,[11]]]],[[],36],[[[17,[9]]],[[30,[9,22]]]],[55],[55],[[],[[3,[28]]]],[[],[[3,[28]]]],[[],[[3,[55]]]],0,0,0,[55],[55],[52,56],[52,56],[52,56],[52,56],[52,56],[52,56],[52,56],[52,56],0,[57],[58],[36,52],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[2,[52]]]],[[],[[3,[[2,[1]]]]]],[[],[[3,[[2,[1]]]]]],[[],[[3,[[2,[1,59]]]]]],[7],[7],[7],0,0,[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],[7,3],[7,3],[7,3],[7,3],[7,3],[7,3],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[7,3],[7,3],[7,3],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],[[],60],0,0,[[],[[61,[55]]]],[[],[[61,[55]]]],0,0,[62],[62],[[],44],[[],44],[[],44],[[],44],[[20,12]],0,0,0,0,0,0,0,0,0,0,0,[[]],[[]],[[]],[7],[[]],[[]],[[]],[7],[[63,7,12],[[15,[[14,[13]]]]]],[64,64],[65,65],[63,63],[[]],[[]],[[]],[[55,49]],0,[[],64],[[],65],[[],63],[[64,34],35],[[65,34],35],[[63,34],35],[[]],[[]],[[]],[[7,[32,[31]]]],0,[[]],[[]],[[]],0,[63,[[54,[63]]]],0,[7],[[]],[[]],[[]],[7,3],[7,3],[[],30],[[],30],[[],30],[[],30],[[],30],[[],30],[7,3],[[],60],[[],60],[[],60]],"c":[],"p":[[3,"Mime"],[3,"Vec"],[4,"Option"],[8,"AsMut"],[3,"Raw"],[8,"AsRef"],[3,"State"],[8,"DeserializeOwned"],[8,"Send"],[8,"AuthHandler"],[3,"AuthMiddleware"],[8,"FnOnce"],[6,"HandlerFuture"],[3,"Box"],[3,"Pin"],[8,"Clone"],[4,"AuthStatus"],[4,"AuthSource"],[3,"StaticAuthHandler"],[3,"OpenapiInfo"],[3,"NoopExtractor"],[3,"AuthError"],[4,"AuthErrorOrOther"],[3,"NoContent"],[3,"Redirect"],[3,"Success"],[8,"Default"],[3,"String"],[8,"Deserializer"],[4,"Result"],[3,"Body"],[3,"Response"],[8,"Debug"],[3,"Formatter"],[6,"Result"],[3,"Response"],[8,"Into"],[15,"never"],[15,"u8"],[15,"slice"],[3,"Bytes"],[8,"From"],[6,"BoxFuture"],[15,"bool"],[8,"IntoHeaderName"],[3,"HeaderValue"],[3,"HeaderName"],[3,"HeaderMap"],[3,"Method"],[8,"Future"],[8,"ResponseBody"],[3,"StatusCode"],[6,"AuthValidation"],[6,"Result"],[15,"str"],[3,"OpenapiSchema"],[8,"DrawResourceRoutes"],[8,"DrawResourceRoutesWithSchema"],[3,"Global"],[3,"TypeId"],[4,"Cow"],[8,"Visitor"],[3,"CorsConfig"],[4,"Origin"],[4,"Headers"],[8,"Endpoint"],[8,"EndpointWithSchema"],[8,"IntoResponseError"],[8,"IntoResponse"],[8,"FromBody"],[8,"GetOpenapi"],[8,"DrawResources"],[8,"DrawResourcesWithSchema"],[8,"ResponseSchema"],[8,"Resource"],[8,"ResourceWithSchema"],[8,"RequestBody"],[8,"WithOpenapi"],[8,"CorsRoute"]]}\ }'); if (typeof window !== 'undefined' && window.initSearch) {window.initSearch(searchIndex)}; if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; diff --git a/tarpaulin-report.html b/tarpaulin-report.html index cb8577f7d..e3126e852 100644 --- a/tarpaulin-report.html +++ b/tarpaulin-report.html @@ -107,8 +107,8 @@
\"#,\n\t\t\t\"\",\n\t\t\t\"\"\n\t\t),\n\t\tencoded_spec, script\n\t)\n\t.unwrap();\n\n\tRedoc { html, script_hash }\n}\n","traces":[{"line":20,"address":[7851961,7850880,7851926],"length":1,"stats":{"Line":0},"fn_name":"html"},{"line":21,"address":[7850931,7850999],"length":1,"stats":{"Line":0},"fn_name":null},{"line":23,"address":[8899339,8899312],"length":1,"stats":{"Line":0},"fn_name":"{closure#0}"},{"line":24,"address":[7852069],"length":1,"stats":{"Line":0},"fn_name":null},{"line":25,"address":[8899431],"length":1,"stats":{"Line":0},"fn_name":null},{"line":26,"address":[8899465],"length":1,"stats":{"Line":0},"fn_name":null},{"line":27,"address":[8899372],"length":1,"stats":{"Line":0},"fn_name":null},{"line":31,"address":[7850910,7851072],"length":1,"stats":{"Line":0},"fn_name":null},{"line":32,"address":[8898427],"length":1,"stats":{"Line":0},"fn_name":null},{"line":33,"address":[8576762],"length":1,"stats":{"Line":0},"fn_name":null},{"line":34,"address":[7851197],"length":1,"stats":{"Line":0},"fn_name":null},{"line":36,"address":[7851370],"length":1,"stats":{"Line":0},"fn_name":null},{"line":37,"address":[7851665],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":13},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","auth.rs"],"content":"use crate::AuthError;\n\nuse base64::prelude::*;\nuse cookie::CookieJar;\nuse futures_util::{\n\tfuture,\n\tfuture::{FutureExt, TryFutureExt}\n};\nuse gotham::{\n\tanyhow,\n\thandler::HandlerFuture,\n\thyper::header::{HeaderMap, HeaderName, AUTHORIZATION},\n\tmiddleware::{cookie::CookieParser, Middleware, NewMiddleware},\n\tprelude::*,\n\tstate::State\n};\nuse jsonwebtoken::DecodingKey;\nuse serde::de::DeserializeOwned;\nuse std::{marker::PhantomData, panic::RefUnwindSafe, pin::Pin};\n\npub type AuthValidation = jsonwebtoken::Validation;\n\n/// The authentication status returned by the auth middleware for each request.\n#[derive(Debug, StateData)]\npub enum AuthStatus {\n\t/// The auth status is unknown. This is likely because no secret was provided\n\t/// that could be used to verify the token of the client.\n\tUnknown,\n\n\t/// The request has been performed without any kind of authentication.\n\tUnauthenticated,\n\n\t/// The request has been performed with an invalid authentication. This\n\t/// includes expired tokens. Further details can be obtained from the\n\t/// included error.\n\tInvalid(jsonwebtoken::errors::Error),\n\n\t/// The request has been performed with a valid authentication. The claims\n\t/// that were decoded from the token are attached.\n\tAuthenticated(T)\n}\n\nimpl Clone for AuthStatus\nwhere\n\tT: Clone + Send + 'static\n{\n\tfn clone(&self) -> Self {\n\t\t// TODO why is this manually implemented?\n\t\tmatch self {\n\t\t\tSelf::Unknown => Self::Unknown,\n\t\t\tSelf::Unauthenticated => Self::Unauthenticated,\n\t\t\tSelf::Invalid(err) => Self::Invalid(err.clone()),\n\t\t\tSelf::Authenticated(data) => Self::Authenticated(data.clone())\n\t\t}\n\t}\n}\n\nimpl AuthStatus {\n\tpub fn ok(self) -> Result {\n\t\tmatch self {\n\t\t\tSelf::Unknown => Err(AuthError::new(\"The authentication could not be determined\")),\n\t\t\tSelf::Unauthenticated => Err(AuthError::new(\"Missing token\")),\n\t\t\tSelf::Invalid(err) => Err(AuthError::new(format!(\"Invalid token: {err}\"))),\n\t\t\tSelf::Authenticated(data) => Ok(data)\n\t\t}\n\t}\n}\n\n/// The source of the authentication token in the request.\n#[derive(Clone, Debug, StateData)]\npub enum AuthSource {\n\t/// Take the token from a cookie with the given name.\n\tCookie(String),\n\t/// Take the token from a header with the given name.\n\tHeader(HeaderName),\n\t/// Take the token from the HTTP Authorization header. This is different from `Header(\"Authorization\")`\n\t/// as it will follow the `scheme param` format from the HTTP specification. The `scheme` will\n\t/// be discarded, so its value doesn't matter.\n\tAuthorizationHeader\n}\n\n/// This trait will help the auth middleware to determine the validity of an authentication token.\n///\n/// A very basic implementation could look like this:\n///\n/// ```\n/// # use gotham_restful::{AuthHandler, gotham::state::State};\n/// #\n/// const SECRET: &'static [u8; 32] = b\"zlBsA2QXnkmpe0QTh8uCvtAEa4j33YAc\";\n///\n/// struct CustomAuthHandler;\n/// impl AuthHandler for CustomAuthHandler {\n/// \tfn jwt_secret Option>(\n/// \t\t&self,\n/// \t\t_state: &mut State,\n/// \t\t_decode_data: F\n/// \t) -> Option> {\n/// \t\tSome(SECRET.to_vec())\n/// \t}\n/// }\n/// ```\npub trait AuthHandler {\n\t/// Return the SHA256-HMAC secret used to verify the JWT token.\n\tfn jwt_secret Option>(\n\t\t&self,\n\t\tstate: &mut State,\n\t\tdecode_data: F\n\t) -> Option>;\n}\n\n/// An [AuthHandler] returning always the same secret. See [AuthMiddleware] for a usage example.\n#[derive(Clone, Debug)]\npub struct StaticAuthHandler {\n\tsecret: Vec\n}\n\nimpl StaticAuthHandler {\n\tpub fn from_vec(secret: Vec) -> Self {\n\t\tSelf { secret }\n\t}\n\n\tpub fn from_array(secret: &[u8]) -> Self {\n\t\tSelf::from_vec(secret.to_vec())\n\t}\n}\n\nimpl AuthHandler for StaticAuthHandler {\n\tfn jwt_secret Option>(\n\t\t&self,\n\t\t_state: &mut State,\n\t\t_decode_data: F\n\t) -> Option> {\n\t\tSome(self.secret.clone())\n\t}\n}\n\n/// This is the auth middleware. To use it, first make sure you have the `auth` feature enabled. Then\n/// simply add it to your pipeline and request it inside your handler:\n///\n/// ```rust,no_run\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # use gotham::{router::builder::*, pipeline::*, state::State};\n/// # use gotham_restful::*;\n/// # use serde::{Deserialize, Serialize};\n/// #\n/// #[derive(Resource)]\n/// #[resource(read_all)]\n/// struct AuthResource;\n///\n/// #[derive(Debug, Deserialize, Clone)]\n/// struct AuthData {\n/// \tsub: String,\n/// \texp: u64\n/// }\n///\n/// #[read_all]\n/// fn read_all(auth: &AuthStatus) -> Success {\n/// \tformat!(\"{auth:?}\").into()\n/// }\n///\n/// fn main() {\n/// \tlet auth: AuthMiddleware = AuthMiddleware::new(\n/// \t\tAuthSource::AuthorizationHeader,\n/// \t\tAuthValidation::default(),\n/// \t\tStaticAuthHandler::from_array(b\"zlBsA2QXnkmpe0QTh8uCvtAEa4j33YAc\")\n/// \t);\n/// \tlet (chain, pipelines) = single_pipeline(new_pipeline().add(auth).build());\n/// \tgotham::start(\n/// \t\t\"127.0.0.1:8080\",\n/// \t\tbuild_router(chain, pipelines, |route| {\n/// \t\t\troute.resource::(\"auth\");\n/// \t\t})\n/// \t);\n/// }\n/// ```\n#[derive(Debug)]\npub struct AuthMiddleware {\n\tsource: AuthSource,\n\tvalidation: AuthValidation,\n\thandler: Handler,\n\t_data: PhantomData\n}\n\nimpl Clone for AuthMiddleware\nwhere\n\tHandler: Clone\n{\n\tfn clone(&self) -> Self {\n\t\tSelf {\n\t\t\tsource: self.source.clone(),\n\t\t\tvalidation: self.validation.clone(),\n\t\t\thandler: self.handler.clone(),\n\t\t\t_data: self._data\n\t\t}\n\t}\n}\n\nimpl AuthMiddleware\nwhere\n\tData: DeserializeOwned + Send,\n\tHandler: AuthHandler + Default\n{\n\tpub fn from_source(source: AuthSource) -> Self {\n\t\tSelf {\n\t\t\tsource,\n\t\t\tvalidation: Default::default(),\n\t\t\thandler: Default::default(),\n\t\t\t_data: Default::default()\n\t\t}\n\t}\n}\n\nimpl AuthMiddleware\nwhere\n\tData: DeserializeOwned + Send,\n\tHandler: AuthHandler\n{\n\tpub fn new(source: AuthSource, validation: AuthValidation, handler: Handler) -> Self {\n\t\tSelf {\n\t\t\tsource,\n\t\t\tvalidation,\n\t\t\thandler,\n\t\t\t_data: Default::default()\n\t\t}\n\t}\n\n\tfn auth_status(&self, state: &mut State) -> AuthStatus {\n\t\t// extract the provided token, if any\n\t\tlet token = match &self.source {\n\t\t\tAuthSource::Cookie(name) => CookieJar::try_borrow_from(&state)\n\t\t\t\t.map(|jar| jar.get(&name).map(|cookie| cookie.value().to_owned()))\n\t\t\t\t.unwrap_or_else(|| {\n\t\t\t\t\tCookieParser::from_state(&state)\n\t\t\t\t\t\t.get(&name)\n\t\t\t\t\t\t.map(|cookie| cookie.value().to_owned())\n\t\t\t\t}),\n\t\t\tAuthSource::Header(name) => HeaderMap::try_borrow_from(&state)\n\t\t\t\t.and_then(|map| map.get(name))\n\t\t\t\t.and_then(|header| header.to_str().ok())\n\t\t\t\t.map(|value| value.to_owned()),\n\t\t\tAuthSource::AuthorizationHeader => HeaderMap::try_borrow_from(&state)\n\t\t\t\t.and_then(|map| map.get(AUTHORIZATION))\n\t\t\t\t.and_then(|header| header.to_str().ok())\n\t\t\t\t.and_then(|value| value.split_whitespace().nth(1))\n\t\t\t\t.map(|value| value.to_owned())\n\t\t};\n\n\t\t// unauthed if no token\n\t\tlet token = match token {\n\t\t\tSome(token) => token,\n\t\t\tNone => return AuthStatus::Unauthenticated\n\t\t};\n\n\t\t// get the secret from the handler, possibly decoding claims ourselves\n\t\tlet secret = self.handler.jwt_secret(state, || {\n\t\t\tlet b64 = token.split('.').nth(1)?;\n\t\t\tlet raw = BASE64_URL_SAFE_NO_PAD.decode(b64).ok()?;\n\t\t\tserde_json::from_slice(&raw).ok()?\n\t\t});\n\n\t\t// unknown if no secret\n\t\tlet secret = match secret {\n\t\t\tSome(secret) => secret,\n\t\t\tNone => return AuthStatus::Unknown\n\t\t};\n\n\t\t// validate the token\n\t\tlet data: Data = match jsonwebtoken::decode(\n\t\t\t&token,\n\t\t\t&DecodingKey::from_secret(&secret),\n\t\t\t&self.validation\n\t\t) {\n\t\t\tOk(data) => data.claims,\n\t\t\tErr(e) => return AuthStatus::Invalid(e)\n\t\t};\n\n\t\t// we found a valid token\n\t\tAuthStatus::Authenticated(data)\n\t}\n}\n\nimpl Middleware for AuthMiddleware\nwhere\n\tData: DeserializeOwned + Send + 'static,\n\tHandler: AuthHandler\n{\n\tfn call(self, mut state: State, chain: Chain) -> Pin>\n\twhere\n\t\tChain: FnOnce(State) -> Pin>\n\t{\n\t\t// put the source in our state, required for e.g. openapi\n\t\tstate.put(self.source.clone());\n\n\t\t// put the status in our state\n\t\tlet status = self.auth_status(&mut state);\n\t\tstate.put(status);\n\n\t\t// call the rest of the chain\n\t\tchain(state)\n\t\t\t.and_then(|(state, res)| future::ok((state, res)))\n\t\t\t.boxed()\n\t}\n}\n\nimpl NewMiddleware for AuthMiddleware\nwhere\n\tSelf: Clone + Middleware + Sync + RefUnwindSafe\n{\n\ttype Instance = Self;\n\n\tfn new_middleware(&self) -> anyhow::Result {\n\t\tlet c: Self = self.clone();\n\t\tOk(c)\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse cookie::Cookie;\n\tuse gotham::hyper::header::COOKIE;\n\tuse jsonwebtoken::errors::ErrorKind;\n\tuse std::fmt::Debug;\n\n\t// 256-bit random string\n\tconst JWT_SECRET: &'static [u8; 32] = b\"Lyzsfnta0cdxyF0T9y6VGxp3jpgoMUuW\";\n\n\t// some known tokens\n\tconst VALID_TOKEN: &'static str = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjQxMDI0NDQ4MDB9.8h8Ax-nnykqEQ62t7CxmM3ja6NzUQ4L0MLOOzddjLKk\";\n\tconst EXPIRED_TOKEN: &'static str = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjE1Nzc4MzcxMDB9.eV1snaGLYrJ7qUoMk74OvBY3WUU9M0Je5HTU2xtX1v0\";\n\tconst INVALID_TOKEN: &'static str = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjQxMDI0NDQ4MDB9\";\n\n\t#[derive(Debug, Deserialize, PartialEq)]\n\tstruct TestData {\n\t\tiss: String,\n\t\tsub: String,\n\t\tiat: u64,\n\t\texp: u64\n\t}\n\n\timpl Default for TestData {\n\t\tfn default() -> Self {\n\t\t\tSelf {\n\t\t\t\tiss: \"msrd0\".to_owned(),\n\t\t\t\tsub: \"gotham-restful\".to_owned(),\n\t\t\t\tiat: 1577836800,\n\t\t\t\texp: 4102444800\n\t\t\t}\n\t\t}\n\t}\n\n\t#[derive(Default)]\n\tstruct NoneAuthHandler;\n\timpl AuthHandler for NoneAuthHandler {\n\t\tfn jwt_secret Option>(\n\t\t\t&self,\n\t\t\t_state: &mut State,\n\t\t\t_decode_data: F\n\t\t) -> Option> {\n\t\t\tNone\n\t\t}\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_none_secret() {\n\t\tlet middleware = >::from_source(\n\t\t\tAuthSource::AuthorizationHeader\n\t\t);\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tAUTHORIZATION,\n\t\t\t\tformat!(\"Bearer {VALID_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tmiddleware.auth_status(&mut state);\n\t\t});\n\t}\n\n\t#[derive(Default)]\n\tstruct TestAssertingHandler;\n\timpl AuthHandler for TestAssertingHandler\n\twhere\n\t\tT: Debug + Default + PartialEq\n\t{\n\t\tfn jwt_secret Option>(\n\t\t\t&self,\n\t\t\t_state: &mut State,\n\t\t\tdecode_data: F\n\t\t) -> Option> {\n\t\t\tassert_eq!(decode_data(), Some(T::default()));\n\t\t\tSome(JWT_SECRET.to_vec())\n\t\t}\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_decode_data() {\n\t\tlet middleware = >::from_source(\n\t\t\tAuthSource::AuthorizationHeader\n\t\t);\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tAUTHORIZATION,\n\t\t\t\tformat!(\"Bearer {VALID_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tmiddleware.auth_status(&mut state);\n\t\t});\n\t}\n\n\tfn new_middleware(source: AuthSource) -> AuthMiddleware\n\twhere\n\t\tT: DeserializeOwned + Send\n\t{\n\t\tAuthMiddleware::new(\n\t\t\tsource,\n\t\t\tDefault::default(),\n\t\t\tStaticAuthHandler::from_array(JWT_SECRET)\n\t\t)\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_no_token() {\n\t\tlet middleware = new_middleware::(AuthSource::AuthorizationHeader);\n\t\tState::with_new(|mut state| {\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Unauthenticated => {},\n\t\t\t\t_ => panic!(\"Expected AuthStatus::Unauthenticated, got {status:?}\")\n\t\t\t};\n\t\t});\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_expired_token() {\n\t\tlet middleware = new_middleware::(AuthSource::AuthorizationHeader);\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tAUTHORIZATION,\n\t\t\t\tformat!(\"Bearer {EXPIRED_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Invalid(err) if *err.kind() == ErrorKind::ExpiredSignature => {},\n\t\t\t\t_ => panic!(\n\t\t\t\t\t\"Expected AuthStatus::Invalid(..) with ErrorKind::ExpiredSignature, got {status:?}\"\n\t\t\t\t)\n\t\t\t};\n\t\t});\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_invalid_token() {\n\t\tlet middleware = new_middleware::(AuthSource::AuthorizationHeader);\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tAUTHORIZATION,\n\t\t\t\tformat!(\"Bearer {INVALID_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Invalid(err) if *err.kind() == ErrorKind::InvalidToken => {},\n\t\t\t\t_ => panic!(\n\t\t\t\t\t\"Expected AuthStatus::Invalid(..) with ErrorKind::InvalidToken, got {status:?}\"\n\t\t\t\t)\n\t\t\t};\n\t\t});\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_auth_header_token() {\n\t\tlet middleware = new_middleware::(AuthSource::AuthorizationHeader);\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tAUTHORIZATION,\n\t\t\t\tformat!(\"Bearer {VALID_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Authenticated(data) => assert_eq!(data, TestData::default()),\n\t\t\t\t_ => panic!(\"Expected AuthStatus::Authenticated, got {status:?}\")\n\t\t\t};\n\t\t})\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_header_token() {\n\t\tlet header_name = \"x-znoiprwmvfexju\";\n\t\tlet middleware =\n\t\t\tnew_middleware::(AuthSource::Header(HeaderName::from_static(header_name)));\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(header_name, VALID_TOKEN.parse().unwrap());\n\t\t\tstate.put(headers);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Authenticated(data) => assert_eq!(data, TestData::default()),\n\t\t\t\t_ => panic!(\"Expected AuthStatus::Authenticated, got {status:?}\")\n\t\t\t};\n\t\t})\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_cookie_token() {\n\t\tlet cookie_name = \"znoiprwmvfexju\";\n\t\tlet middleware = new_middleware::(AuthSource::Cookie(cookie_name.to_owned()));\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut jar = CookieJar::new();\n\t\t\tjar.add_original(Cookie::new(cookie_name, VALID_TOKEN));\n\t\t\tstate.put(jar);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Authenticated(data) => assert_eq!(data, TestData::default()),\n\t\t\t\t_ => panic!(\"Expected AuthStatus::Authenticated, got {status:?}\")\n\t\t\t};\n\t\t})\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_cookie_no_jar() {\n\t\tlet cookie_name = \"znoiprwmvfexju\";\n\t\tlet middleware = new_middleware::(AuthSource::Cookie(cookie_name.to_owned()));\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tCOOKIE,\n\t\t\t\tformat!(\"{cookie_name}={VALID_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Authenticated(data) => assert_eq!(data, TestData::default()),\n\t\t\t\t_ => panic!(\"Expected AuthStatus::Authenticated, got {status:?}\")\n\t\t\t};\n\t\t})\n\t}\n}\n","traces":[{"line":47,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":49,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":51,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":52,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":53,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":59,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":60,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":61,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":62,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[7757968],"length":1,"stats":{"Line":3},"fn_name":null},{"line":122,"address":[7758000],"length":1,"stats":{"Line":3},"fn_name":null},{"line":123,"address":[7758023],"length":1,"stats":{"Line":3},"fn_name":null},{"line":128,"address":[7241520,7241565],"length":1,"stats":{"Line":2},"fn_name":"jwt_secret>"},{"line":133,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":188,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":190,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":191,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":192,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":193,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":203,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":206,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":207,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":208,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":218,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":223,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":227,"address":[],"length":0,"stats":{"Line":5},"fn_name":null},{"line":229,"address":[],"length":0,"stats":{"Line":5},"fn_name":null},{"line":230,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":231,"address":[],"length":0,"stats":{"Line":5},"fn_name":null},{"line":232,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":233,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":234,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":235,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":237,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":238,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":239,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":240,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":241,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":242,"address":[],"length":0,"stats":{"Line":8},"fn_name":null},{"line":243,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":244,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":245,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":249,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":250,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":251,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":255,"address":[],"length":0,"stats":{"Line":5},"fn_name":null},{"line":256,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":257,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":258,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":262,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":263,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":264,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":268,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":269,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":270,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":271,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":273,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":274,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":278,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":287,"address":[7245376,7243006,7241648,7244414,7244448,7244878,7245342,7243504,7243924,7243040,7243470,7242112,7242078,7242576,7243963,7243984,7244912,7242542,7245806],"length":1,"stats":{"Line":1},"fn_name":"call, gotham_restful::routing::MaybeMatchAcceptHeader>, (borrow_bag::handle::Handle, ())>, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::auth::AuthMiddleware, ())>, ()), gotham_restful::endpoint::NoopExtractor, openapi_specification::SecretQuery, fn(&mut gotham::state::State) -> gotham_restful::routing::endpoint_handler::{async_fn_env#0}>, (borrow_bag::handle::Handle, ())>, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::auth::AuthMiddleware, ())>, ())>>"},{"line":292,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":295,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":296,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":299,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":300,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":311,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":312,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":313,"address":[],"length":0,"stats":{"Line":1},"fn_name":null}],"covered":58,"coverable":71},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","cors.rs"],"content":"use gotham::{\n\thandler::HandlerFuture,\n\thelpers::http::response::create_empty_response,\n\thyper::{\n\t\theader::{\n\t\t\tHeaderMap, HeaderName, HeaderValue, ACCESS_CONTROL_ALLOW_CREDENTIALS,\n\t\t\tACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS,\n\t\t\tACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_MAX_AGE, ACCESS_CONTROL_REQUEST_HEADERS,\n\t\t\tACCESS_CONTROL_REQUEST_METHOD, ORIGIN, VARY\n\t\t},\n\t\tBody, Method, Response, StatusCode\n\t},\n\tmiddleware::Middleware,\n\tpipeline::PipelineHandleChain,\n\tprelude::*,\n\trouter::{builder::ExtendRouteMatcher, route::matcher::AccessControlRequestMethodMatcher},\n\tstate::State\n};\nuse std::{panic::RefUnwindSafe, pin::Pin};\n\n/// Specify the allowed origins of the request. It is up to the browser to check the validity of the\n/// origin. This, when sent to the browser, will indicate whether or not the request's origin was\n/// allowed to make the request.\n#[derive(Clone, Debug)]\npub enum Origin {\n\t/// Do not send any `Access-Control-Allow-Origin` headers.\n\tNone,\n\t/// Send `Access-Control-Allow-Origin: *`. Note that browser will not send credentials.\n\tStar,\n\t/// Set the `Access-Control-Allow-Origin` header to a single origin.\n\tSingle(String),\n\t/// Copy the `Origin` header into the `Access-Control-Allow-Origin` header.\n\tCopy\n}\n\nimpl Default for Origin {\n\tfn default() -> Self {\n\t\tSelf::None\n\t}\n}\n\nimpl Origin {\n\t/// Get the header value for the `Access-Control-Allow-Origin` header.\n\tfn header_value(&self, state: &State) -> Option {\n\t\tmatch self {\n\t\t\tSelf::None => None,\n\t\t\tSelf::Star => Some(\"*\".parse().unwrap()),\n\t\t\tSelf::Single(origin) => Some(origin.parse().unwrap()),\n\t\t\tSelf::Copy => {\n\t\t\t\tlet headers = HeaderMap::borrow_from(state);\n\t\t\t\theaders.get(ORIGIN).map(Clone::clone)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns true if the `Vary` header has to include `Origin`.\n\tfn varies(&self) -> bool {\n\t\tmatches!(self, Self::Copy)\n\t}\n}\n\n/// Specify the allowed headers of the request. It is up to the browser to check that only the allowed\n/// headers are sent with the request.\n#[derive(Clone, Debug)]\npub enum Headers {\n\t/// Do not send any `Access-Control-Allow-Headers` headers.\n\tNone,\n\t/// Set the `Access-Control-Allow-Headers` header to the following header list. If empty, this\n\t/// is treated as if it was [None].\n\tList(Vec),\n\t/// Copy the `Access-Control-Request-Headers` header into the `Access-Control-Allow-Header`\n\t/// header.\n\tCopy\n}\n\nimpl Default for Headers {\n\tfn default() -> Self {\n\t\tSelf::None\n\t}\n}\n\nimpl Headers {\n\t/// Get the header value for the `Access-Control-Allow-Headers` header.\n\tfn header_value(&self, state: &State) -> Option {\n\t\tmatch self {\n\t\t\tSelf::None => None,\n\t\t\tSelf::List(list) => Some(list.join(\",\").parse().unwrap()),\n\t\t\tSelf::Copy => {\n\t\t\t\tlet headers = HeaderMap::borrow_from(state);\n\t\t\t\theaders\n\t\t\t\t\t.get(ACCESS_CONTROL_REQUEST_HEADERS)\n\t\t\t\t\t.map(Clone::clone)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns true if the `Vary` header has to include `Origin`.\n\tfn varies(&self) -> bool {\n\t\tmatches!(self, Self::Copy)\n\t}\n}\n\n/// This is the configuration that the CORS handler will follow. Its default configuration is basically\n/// not to touch any responses, resulting in the browser's default behaviour.\n///\n/// To change settings, you need to put this type into gotham's [State]:\n///\n/// ```rust,no_run\n/// # use gotham::{router::builder::*, pipeline::*, state::State};\n/// # use gotham_restful::{*, cors::Origin};\n/// # #[cfg_attr(feature = \"cargo-clippy\", allow(clippy::needless_doctest_main))]\n/// fn main() {\n/// \tlet cors = CorsConfig {\n/// \t\torigin: Origin::Star,\n/// \t\t..Default::default()\n/// \t};\n/// \tlet (chain, pipelines) = single_pipeline(new_pipeline().add(cors).build());\n/// \tgotham::start(\n/// \t\t\"127.0.0.1:8080\",\n/// \t\tbuild_router(chain, pipelines, |route| {\n/// \t\t\t// your routing logic\n/// \t\t})\n/// \t);\n/// }\n/// ```\n///\n/// This easy approach allows you to have one global cors configuration. If you prefer to have separate\n/// configurations for different scopes, you need to register the middleware inside your routing logic:\n///\n/// ```rust,no_run\n/// # use gotham::{router::builder::*, pipeline::*, state::State};\n/// # use gotham_restful::{*, cors::Origin};\n/// let pipelines = new_pipeline_set();\n///\n/// // The first cors configuration\n/// let cors_a = CorsConfig {\n/// \torigin: Origin::Star,\n/// \t..Default::default()\n/// };\n/// let (pipelines, chain_a) = pipelines.add(new_pipeline().add(cors_a).build());\n///\n/// // The second cors configuration\n/// let cors_b = CorsConfig {\n/// \torigin: Origin::Copy,\n/// \t..Default::default()\n/// };\n/// let (pipelines, chain_b) = pipelines.add(new_pipeline().add(cors_b).build());\n///\n/// let pipeline_set = finalize_pipeline_set(pipelines);\n/// gotham::start(\n/// \t\"127.0.0.1:8080\",\n/// \tbuild_router((), pipeline_set, |route| {\n/// \t\t// routing without any cors config\n/// \t\troute.with_pipeline_chain((chain_a, ()), |route| {\n/// \t\t\t// routing with cors config a\n/// \t\t});\n/// \t\troute.with_pipeline_chain((chain_b, ()), |route| {\n/// \t\t\t// routing with cors config b\n/// \t\t});\n/// \t})\n/// );\n/// ```\n#[derive(Clone, Debug, Default, NewMiddleware, StateData)]\npub struct CorsConfig {\n\t/// The allowed origins.\n\tpub origin: Origin,\n\t/// The allowed headers.\n\tpub headers: Headers,\n\t/// The amount of seconds that the preflight request can be cached.\n\tpub max_age: u64,\n\t/// Whether or not the request may be made with supplying credentials.\n\tpub credentials: bool\n}\n\nimpl Middleware for CorsConfig {\n\tfn call(self, mut state: State, chain: Chain) -> Pin>\n\twhere\n\t\tChain: FnOnce(State) -> Pin>\n\t{\n\t\tstate.put(self);\n\t\tchain(state)\n\t}\n}\n\n/// Handle CORS for a non-preflight request. This means manipulating the `res` HTTP headers so that\n/// the response is aligned with the `state`'s [CorsConfig].\n///\n/// If you are using the [Resource](crate::Resource) type (which is the recommended way), you'll never\n/// have to call this method. However, if you are writing your own handler method, you might want to\n/// call this after your request to add the required CORS headers.\n///\n/// For further information on CORS, read .\npub fn handle_cors(state: &State, res: &mut Response) {\n\tlet config = CorsConfig::try_borrow_from(state);\n\tif let Some(cfg) = config {\n\t\tlet headers = res.headers_mut();\n\n\t\t// non-preflight requests require the Access-Control-Allow-Origin header\n\t\tif let Some(header) = cfg.origin.header_value(state) {\n\t\t\theaders.insert(ACCESS_CONTROL_ALLOW_ORIGIN, header);\n\t\t}\n\n\t\t// if the origin is copied over, we should tell the browser by specifying the Vary header\n\t\tif cfg.origin.varies() {\n\t\t\tlet vary = headers\n\t\t\t\t.get(VARY)\n\t\t\t\t.map(|vary| format!(\"{},origin\", vary.to_str().unwrap()));\n\t\t\theaders.insert(VARY, vary.as_deref().unwrap_or(\"origin\").parse().unwrap());\n\t\t}\n\n\t\t// if we allow credentials, tell the browser\n\t\tif cfg.credentials {\n\t\t\theaders.insert(\n\t\t\t\tACCESS_CONTROL_ALLOW_CREDENTIALS,\n\t\t\t\tHeaderValue::from_static(\"true\")\n\t\t\t);\n\t\t}\n\t}\n}\n\n/// Add CORS routing for your path. This is required for handling preflight requests.\n///\n/// Example:\n///\n/// ```rust,no_run\n/// # use gotham::{hyper::{Body, Method, Response}, router::builder::*};\n/// # use gotham_restful::*;\n/// build_simple_router(|router| {\n/// \t// The handler that needs preflight handling\n/// \trouter.post(\"/foo\").to(|state| {\n/// \t\tlet mut res: Response = unimplemented!();\n/// \t\thandle_cors(&state, &mut res);\n/// \t\t(state, res)\n/// \t});\n/// \t// Add preflight handling\n/// \trouter.cors(\"/foo\", Method::POST);\n/// });\n/// ```\npub trait CorsRoute\nwhere\n\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\tP: RefUnwindSafe + Send + Sync + 'static\n{\n\t/// Handle a preflight request on `path` for `method`. To configure the behaviour, use\n\t/// [CorsConfig].\n\tfn cors(&mut self, path: &str, method: Method);\n}\n\npub(crate) fn cors_preflight_handler(state: State) -> (State, Response) {\n\tlet config = CorsConfig::try_borrow_from(&state);\n\n\t// prepare the response\n\tlet mut res = create_empty_response(&state, StatusCode::NO_CONTENT);\n\tlet headers = res.headers_mut();\n\tlet mut vary: Vec = Vec::new();\n\n\t// copy the request method over to the response\n\tlet method = HeaderMap::borrow_from(&state)\n\t\t.get(ACCESS_CONTROL_REQUEST_METHOD)\n\t\t.unwrap()\n\t\t.clone();\n\theaders.insert(ACCESS_CONTROL_ALLOW_METHODS, method);\n\tvary.push(ACCESS_CONTROL_REQUEST_METHOD);\n\n\tif let Some(cfg) = config {\n\t\t// if we allow any headers, copy them over\n\t\tif let Some(header) = cfg.headers.header_value(&state) {\n\t\t\theaders.insert(ACCESS_CONTROL_ALLOW_HEADERS, header);\n\t\t}\n\n\t\t// if the headers are copied over, we should tell the browser by specifying the Vary header\n\t\tif cfg.headers.varies() {\n\t\t\tvary.push(ACCESS_CONTROL_REQUEST_HEADERS);\n\t\t}\n\n\t\t// set the max age for the preflight cache\n\t\tif let Some(age) = config.map(|cfg| cfg.max_age) {\n\t\t\theaders.insert(ACCESS_CONTROL_MAX_AGE, age.into());\n\t\t}\n\t}\n\n\t// make sure the browser knows that this request was based on the method\n\theaders.insert(VARY, vary.join(\",\").parse().unwrap());\n\n\thandle_cors(&state, &mut res);\n\t(state, res)\n}\n\nimpl CorsRoute for D\nwhere\n\tD: DrawRoutes,\n\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\tP: RefUnwindSafe + Send + Sync + 'static\n{\n\tfn cors(&mut self, path: &str, method: Method) {\n\t\tlet matcher = AccessControlRequestMethodMatcher::new(method);\n\t\tself.options(path)\n\t\t\t.extend_route_matcher(matcher)\n\t\t\t.to(cors_preflight_handler);\n\t}\n}\n","traces":[{"line":37,"address":[7566816],"length":1,"stats":{"Line":2},"fn_name":"default"},{"line":38,"address":[7566819],"length":1,"stats":{"Line":2},"fn_name":null},{"line":44,"address":[7566832],"length":1,"stats":{"Line":2},"fn_name":null},{"line":45,"address":[7566874],"length":1,"stats":{"Line":2},"fn_name":null},{"line":46,"address":[7566910],"length":1,"stats":{"Line":2},"fn_name":null},{"line":47,"address":[7566919],"length":1,"stats":{"Line":1},"fn_name":null},{"line":48,"address":[7566995],"length":1,"stats":{"Line":1},"fn_name":null},{"line":50,"address":[7567081],"length":1,"stats":{"Line":1},"fn_name":null},{"line":51,"address":[7567098],"length":1,"stats":{"Line":1},"fn_name":null},{"line":57,"address":[7567200],"length":1,"stats":{"Line":2},"fn_name":null},{"line":58,"address":[7567205],"length":1,"stats":{"Line":2},"fn_name":null},{"line":77,"address":[7567248],"length":1,"stats":{"Line":2},"fn_name":"default"},{"line":78,"address":[7567251],"length":1,"stats":{"Line":2},"fn_name":null},{"line":84,"address":[7567264,7567697],"length":1,"stats":{"Line":2},"fn_name":null},{"line":85,"address":[7567307],"length":1,"stats":{"Line":2},"fn_name":null},{"line":86,"address":[7567344],"length":1,"stats":{"Line":1},"fn_name":null},{"line":87,"address":[7567358,7567563],"length":1,"stats":{"Line":1},"fn_name":null},{"line":89,"address":[7567445],"length":1,"stats":{"Line":1},"fn_name":null},{"line":90,"address":[7567522],"length":1,"stats":{"Line":1},"fn_name":null},{"line":91,"address":[7567462],"length":1,"stats":{"Line":1},"fn_name":null},{"line":98,"address":[7567728],"length":1,"stats":{"Line":2},"fn_name":null},{"line":99,"address":[7567733],"length":1,"stats":{"Line":2},"fn_name":null},{"line":176,"address":[6413280,6413461,6413489,6413056,6413013,6413041,6413237,6413265,6412832],"length":1,"stats":{"Line":4},"fn_name":"call, gotham_restful::routing::MaybeMatchAcceptHeader>, (borrow_bag::handle::Handle, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::cors::CorsConfig, ())>, ()), gotham_restful::endpoint::NoopExtractor, gotham_restful::endpoint::NoopExtractor, fn(&mut gotham::state::State) -> gotham_restful::routing::endpoint_handler::{async_fn_env#0}>, (borrow_bag::handle::Handle, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::cors::CorsConfig, ())>, ())>>"},{"line":180,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":181,"address":[],"length":0,"stats":{"Line":8},"fn_name":null},{"line":193,"address":[7568657,7568605,7567776],"length":1,"stats":{"Line":6},"fn_name":"handle_cors"},{"line":194,"address":[7567809],"length":1,"stats":{"Line":6},"fn_name":null},{"line":195,"address":[7567844],"length":1,"stats":{"Line":6},"fn_name":null},{"line":196,"address":[7567893],"length":1,"stats":{"Line":2},"fn_name":null},{"line":199,"address":[7567922,7567979],"length":1,"stats":{"Line":3},"fn_name":null},{"line":200,"address":[7568012],"length":1,"stats":{"Line":1},"fn_name":null},{"line":204,"address":[7568158],"length":1,"stats":{"Line":2},"fn_name":null},{"line":205,"address":[7568241],"length":1,"stats":{"Line":1},"fn_name":null},{"line":206,"address":[7568211],"length":1,"stats":{"Line":1},"fn_name":null},{"line":207,"address":[7569022,7568880,7568918],"length":1,"stats":{"Line":3},"fn_name":"{closure#0}"},{"line":208,"address":[7568586,7568288],"length":1,"stats":{"Line":1},"fn_name":null},{"line":212,"address":[7568191],"length":1,"stats":{"Line":2},"fn_name":null},{"line":213,"address":[7568776],"length":1,"stats":{"Line":1},"fn_name":null},{"line":214,"address":[7568672],"length":1,"stats":{"Line":1},"fn_name":null},{"line":215,"address":[7568710],"length":1,"stats":{"Line":1},"fn_name":null},{"line":249,"address":[7570540,7569136,7571151],"length":1,"stats":{"Line":2},"fn_name":"cors_preflight_handler"},{"line":250,"address":[7569293,7569172],"length":1,"stats":{"Line":4},"fn_name":null},{"line":253,"address":[7569301],"length":1,"stats":{"Line":2},"fn_name":null},{"line":254,"address":[7569333,7569403],"length":1,"stats":{"Line":4},"fn_name":null},{"line":255,"address":[7569411],"length":1,"stats":{"Line":2},"fn_name":null},{"line":258,"address":[7569527,7569435],"length":1,"stats":{"Line":4},"fn_name":null},{"line":259,"address":[7569497],"length":1,"stats":{"Line":2},"fn_name":null},{"line":262,"address":[7569604],"length":1,"stats":{"Line":2},"fn_name":null},{"line":263,"address":[7569736],"length":1,"stats":{"Line":2},"fn_name":null},{"line":265,"address":[7569793],"length":1,"stats":{"Line":2},"fn_name":null},{"line":267,"address":[7569856,7569933],"length":1,"stats":{"Line":3},"fn_name":null},{"line":268,"address":[7570119,7570027],"length":1,"stats":{"Line":2},"fn_name":null},{"line":272,"address":[7570161,7570195],"length":1,"stats":{"Line":3},"fn_name":null},{"line":273,"address":[7570230],"length":1,"stats":{"Line":1},"fn_name":null},{"line":277,"address":[7570299,7571189,7570201,7571184],"length":1,"stats":{"Line":7},"fn_name":"{closure#0}"},{"line":278,"address":[7570346],"length":1,"stats":{"Line":2},"fn_name":null},{"line":283,"address":[7569863,7570551],"length":1,"stats":{"Line":2},"fn_name":null},{"line":285,"address":[7570928],"length":1,"stats":{"Line":2},"fn_name":null},{"line":286,"address":[7570952],"length":1,"stats":{"Line":1},"fn_name":null},{"line":295,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":296,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":297,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":298,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":299,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":59,"coverable":64},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","endpoint.rs"],"content":"use crate::{IntoResponse, RequestBody};\nuse futures_util::future::BoxFuture;\nuse gotham::{\n\textractor::{PathExtractor, QueryStringExtractor},\n\thyper::{Body, Method, Response},\n\trouter::response::StaticResponseExtender,\n\tstate::{State, StateData}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiType, Visitor};\nuse serde::{Deserialize, Deserializer};\nuse std::borrow::Cow;\n\n/// A no-op extractor that can be used as a default type for [Endpoint::Placeholders] and\n/// [Endpoint::Params].\n#[derive(Debug, Clone, Copy)]\npub struct NoopExtractor;\n\nimpl<'de> Deserialize<'de> for NoopExtractor {\n\tfn deserialize>(_: D) -> Result {\n\t\tOk(Self)\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl OpenapiType for NoopExtractor {\n\tfn visit_type(visitor: &mut V) {\n\t\twarn!(\n\t\t\t\"You're asking for the OpenAPI Schema for gotham_restful::NoopExtractor. This is probably not what you want.\"\n\t\t);\n\t\tvisitor.visit_unit();\n\t}\n}\n\nimpl StateData for NoopExtractor {}\n\nimpl StaticResponseExtender for NoopExtractor {\n\ttype ResBody = Body;\n\tfn extend(_: &mut State, _: &mut Response) {}\n}\n\n// TODO: Specify default types once https://github.com/rust-lang/rust/issues/29661 lands.\n#[_private_openapi_trait(EndpointWithSchema)]\npub trait Endpoint {\n\t/// The HTTP Verb of this endpoint.\n\tfn http_method() -> Method;\n\t/// The URI that this endpoint listens on in gotham's format.\n\tfn uri() -> Cow<'static, str>;\n\n\t/// The verb used for generating an operation id if [Self::operation_id] returns [None].\n\t/// For example `read`, `read_all`, `create`, `update` etc.\n\t#[openapi_only]\n\tfn operation_verb() -> Option<&'static str>;\n\n\t/// The output type that provides the response.\n\t#[openapi_bound(Output: crate::ResponseSchema)]\n\ttype Output: IntoResponse + Send;\n\n\t/// Returns `true` _iff_ the URI contains placeholders. `false` by default.\n\tfn has_placeholders() -> bool {\n\t\tfalse\n\t}\n\t/// The type that parses the URI placeholders. Use [NoopExtractor] if `has_placeholders()`\n\t/// returns `false`.\n\t#[openapi_bound(Placeholders: OpenapiType)]\n\ttype Placeholders: PathExtractor + Clone + Sync;\n\n\t/// Returns `true` _iff_ the request parameters should be parsed. `false` by default.\n\tfn needs_params() -> bool {\n\t\tfalse\n\t}\n\t/// The type that parses the request parameters. Use [NoopExtractor] if `needs_params()`\n\t/// returns `false`.\n\t#[openapi_bound(Params: OpenapiType)]\n\ttype Params: QueryStringExtractor + Clone + Sync;\n\n\t/// Returns `true` _iff_ the request body should be parsed. `false` by default.\n\tfn needs_body() -> bool {\n\t\tfalse\n\t}\n\t/// The type to parse the body into. Use `()` if `needs_body()` returns `false`.\n\ttype Body: RequestBody + Send;\n\n\t/// Returns `true` if the request wants to know the auth status of the client. `false` by default.\n\tfn wants_auth() -> bool {\n\t\tfalse\n\t}\n\n\t/// Replace the automatically generated operation id with a custom one. Only relevant for the\n\t/// OpenAPI Specification.\n\t#[openapi_only]\n\tfn operation_id() -> Option {\n\t\tNone\n\t}\n\n\t/// Add a description to the openapi specification. Usually taken from the rustdoc comment\n\t/// when using the proc macro.\n\t#[openapi_only]\n\tfn description() -> Option {\n\t\tNone\n\t}\n\n\t/// The handler for this endpoint.\n\tfn handle(\n\t\tstate: &mut State,\n\t\tplaceholders: Self::Placeholders,\n\t\tparams: Self::Params,\n\t\tbody: Option\n\t) -> BoxFuture<'_, Self::Output>;\n}\n\n#[cfg(feature = \"openapi\")]\nimpl Endpoint for E {\n\tfn http_method() -> Method {\n\t\tE::http_method()\n\t}\n\tfn uri() -> Cow<'static, str> {\n\t\tE::uri()\n\t}\n\n\ttype Output = E::Output;\n\n\tfn has_placeholders() -> bool {\n\t\tE::has_placeholders()\n\t}\n\ttype Placeholders = E::Placeholders;\n\n\tfn needs_params() -> bool {\n\t\tE::needs_params()\n\t}\n\ttype Params = E::Params;\n\n\tfn needs_body() -> bool {\n\t\tE::needs_body()\n\t}\n\ttype Body = E::Body;\n\n\tfn wants_auth() -> bool {\n\t\tE::wants_auth()\n\t}\n\n\tfn handle<'a>(\n\t\tstate: &'a mut State,\n\t\tplaceholders: Self::Placeholders,\n\t\tparams: Self::Params,\n\t\tbody: Option\n\t) -> BoxFuture<'a, Self::Output> {\n\t\tE::handle(state, placeholders, params, body)\n\t}\n}\n","traces":[{"line":20,"address":[6896016,6895968],"length":1,"stats":{"Line":10},"fn_name":"deserialize>, alloc::vec::Vec<&gotham::helpers::http::PercentDecoded, alloc::alloc::Global>, gotham::helpers::http::PercentDecoded>>>"},{"line":21,"address":[6912483,6912521],"length":1,"stats":{"Line":10},"fn_name":null},{"line":27,"address":[8270800],"length":1,"stats":{"Line":0},"fn_name":"visit_type"},{"line":28,"address":[8270924,8270814,8270874],"length":1,"stats":{"Line":0},"fn_name":null},{"line":31,"address":[8270913],"length":1,"stats":{"Line":0},"fn_name":null},{"line":39,"address":[8270992,8271002],"length":1,"stats":{"Line":0},"fn_name":"extend"},{"line":60,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":61,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":69,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":70,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":78,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":79,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":85,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":86,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[6827280],"length":1,"stats":{"Line":6},"fn_name":"operation_id"},{"line":93,"address":[6827283],"length":1,"stats":{"Line":6},"fn_name":null},{"line":99,"address":[6827264],"length":1,"stats":{"Line":7},"fn_name":"description"},{"line":100,"address":[6644531,6644499,6644515,6644467,6644547,6644483],"length":1,"stats":{"Line":7},"fn_name":null},{"line":114,"address":[6650752,6650592,6650624,6650560,6650688,6650656,6650720],"length":1,"stats":{"Line":30},"fn_name":"http_method"},{"line":115,"address":[6650600,6650728,6650664,6650696,6650632,6650568,6650760],"length":1,"stats":{"Line":30},"fn_name":null},{"line":117,"address":[6836208],"length":1,"stats":{"Line":30},"fn_name":"uri"},{"line":118,"address":[6836216],"length":1,"stats":{"Line":30},"fn_name":null},{"line":123,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":129,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[6836160],"length":1,"stats":{"Line":21},"fn_name":"needs_body"},{"line":134,"address":[6836161],"length":1,"stats":{"Line":21},"fn_name":null},{"line":138,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":139,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[6836240],"length":1,"stats":{"Line":21},"fn_name":"handle"},{"line":148,"address":[6836248],"length":1,"stats":{"Line":21},"fn_name":null}],"covered":14,"coverable":32},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","lib.rs"],"content":"#![warn(missing_debug_implementations, rust_2018_idioms, unreachable_pub)]\n#![forbid(unsafe_code)]\n// deny warnings in CI\n#![cfg_attr(gotham_restful_deny_warnings, deny(warnings))]\n// clippy doesn't like our code style\n#![cfg_attr(feature = \"cargo-clippy\", allow(clippy::tabs_in_doc_comments))]\n// intra-doc links only fully work when OpenAPI is enabled\n#![cfg_attr(feature = \"openapi\", deny(rustdoc::broken_intra_doc_links))]\n#![cfg_attr(not(feature = \"openapi\"), allow(rustdoc::broken_intra_doc_links))]\n\n//! This crate is an extension to the popular [gotham web framework][gotham] for Rust. It allows you to\n//! create resources with assigned endpoints that aim to be a more convenient way of creating handlers\n//! for requests.\n//!\n//! # Features\n//!\n//! - Automatically parse **JSON** request and produce response bodies\n//! - Allow using **raw** request and response bodies\n//! - Convenient **macros** to create responses that can be registered with gotham's router\n//! - Auto-Generate an **OpenAPI** specification for your API\n//! - Manage **CORS** headers so you don't have to\n//! - Manage **Authentication** with JWT\n//! - Integrate diesel connection pools for easy **database** integration\n//!\n//! # Safety\n//!\n//! This crate is just as safe as you'd expect from anything written in safe Rust - and\n//! `#![forbid(unsafe_code)]` ensures that no unsafe was used.\n//!\n//! # Endpoints\n//!\n//! There are a set of pre-defined endpoints that should cover the majority of REST APIs. However,\n//! it is also possible to define your own endpoints.\n//!\n//! ## Pre-defined Endpoints\n//!\n//! Assuming you assign `/foobar` to your resource, the following pre-defined endpoints exist:\n//!\n//! | Endpoint Name | Required Arguments | HTTP Verb | HTTP Path |\n//! | ------------- | ------------------ | --------- | -------------- |\n//! | read_all | | GET | /foobar |\n//! | read | id | GET | /foobar/:id |\n//! | search | query | GET | /foobar/search |\n//! | create | body | POST | /foobar |\n//! | update_all | body | PUT | /foobar |\n//! | update | id, body | PUT | /foobar/:id |\n//! | delete_all | | DELETE | /foobar |\n//! | delete | id | DELETE | /foobar/:id |\n//!\n//! Each of those endpoints has a macro that creates the neccessary boilerplate for the Resource. A\n//! simple example looks like this:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # use gotham::router::builder::*;\n//! # use gotham_restful::*;\n//! # use serde::{Deserialize, Serialize};\n//! /// Our RESTful resource.\n//! #[derive(Resource)]\n//! #[resource(read)]\n//! struct FooResource;\n//!\n//! /// The return type of the foo read endpoint.\n//! #[derive(Serialize)]\n//! # #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n//! struct Foo {\n//! \tid: u64\n//! }\n//!\n//! /// The foo read endpoint.\n//! #[read]\n//! fn read(id: u64) -> Success {\n//! \tFoo { id }.into()\n//! }\n//! # fn main() {\n//! # \tgotham::start(\"127.0.0.1:8080\", build_simple_router(|route| {\n//! # \t\troute.resource::(\"foo\");\n//! # \t})).expect(\"Failed to start gotham\");\n//! # }\n//! ```\n//!\n//! ## Custom Endpoints\n//!\n//! Defining custom endpoints is done with the `#[endpoint]` macro. The syntax is similar to that\n//! of the pre-defined endpoints, but you need to give it more context:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # use gotham::{router::build_simple_router, prelude::*};\n//! # use gotham_restful::*;\n//! # use serde::{Deserialize, Serialize};\n//! use gotham_restful::gotham::hyper::Method;\n//!\n//! #[derive(Resource)]\n//! #[resource(custom_endpoint)]\n//! struct CustomResource;\n//!\n//! /// This type is used to parse path parameters.\n//! #[derive(Clone, Deserialize, StateData, StaticResponseExtender)]\n//! # #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n//! struct CustomPath {\n//! \tname: String\n//! }\n//!\n//! #[endpoint(\n//! \turi = \"custom/:name/read\",\n//! \tmethod = \"Method::GET\",\n//! \tparams = false,\n//! \tbody = false\n//! )]\n//! fn custom_endpoint(path: CustomPath) -> Success {\n//! \tpath.name.into()\n//! }\n//! # fn main() {\n//! # \tgotham::start(\"127.0.0.1:8080\", build_simple_router(|route| {\n//! # \t\troute.resource::(\"custom\");\n//! # \t})).expect(\"Failed to start gotham\");\n//! # }\n//! ```\n//!\n//! # Arguments\n//!\n//! Some endpoints require arguments. Those should be\n//! * **id** Should be a deserializable json-primitive like [`i64`] or [`String`].\n//! * **body** Should be any deserializable object, or any type implementing [`RequestBody`].\n//! * **query** Should be any deserializable object whose variables are json-primitives. It will\n//! however not be parsed from json, but from HTTP GET parameters like in `search?id=1`. The\n//! type needs to implement [`QueryStringExtractor`](gotham::extractor::QueryStringExtractor).\n//!\n//! Additionally, all handlers may take a reference to gotham's [`State`]. Please note that for async\n//! handlers, it needs to be a mutable reference until rustc's lifetime checks across await bounds\n//! improve.\n//!\n//! # Uploads and Downloads\n//!\n//! By default, every request body is parsed from json, and every respone is converted to json using\n//! [serde_json]. However, you may also use raw bodies. This is an example where the request body\n//! is simply returned as the response again, no json parsing involved:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # use gotham::{mime::{self, Mime}, router::builder::*};\n//! # use gotham_restful::*;\n//! # use serde::{Deserialize, Serialize};\n//! #[derive(Resource)]\n//! #[resource(create)]\n//! struct ImageResource;\n//!\n//! #[derive(FromBody, RequestBody)]\n//! #[supported_types(mime::IMAGE_GIF, mime::IMAGE_JPEG, mime::IMAGE_PNG)]\n//! struct RawImage {\n//! \tcontent: Vec,\n//! \tcontent_type: Mime\n//! }\n//!\n//! #[create]\n//! fn create(body: RawImage) -> Raw> {\n//! \tRaw::new(body.content, body.content_type)\n//! }\n//! # fn main() {\n//! # \tgotham::start(\"127.0.0.1:8080\", build_simple_router(|route| {\n//! # \t\troute.resource::(\"image\");\n//! # \t})).expect(\"Failed to start gotham\");\n//! # }\n//! ```\n//!\n//! # Custom HTTP Headers\n//!\n//! You can read request headers from the state as you would in any other gotham handler, and specify\n//! custom response headers using [Response::header].\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # use gotham::hyper::header::{ACCEPT, HeaderMap, VARY};\n//! # use gotham::{router::builder::*, state::State};\n//! # use gotham_restful::*;\n//! #[derive(Resource)]\n//! #[resource(read_all)]\n//! struct FooResource;\n//!\n//! #[read_all]\n//! async fn read_all(state: &mut State) -> NoContent {\n//! \tlet headers: &HeaderMap = state.borrow();\n//! \tlet accept = &headers[ACCEPT];\n//! # drop(accept);\n//!\n//! \tlet mut res = NoContent::default();\n//! \tres.header(VARY, \"accept\".parse().unwrap());\n//! \tres\n//! }\n//! # fn main() {\n//! # \tgotham::start(\"127.0.0.1:8080\", build_simple_router(|route| {\n//! # \t\troute.resource::(\"foo\");\n//! # \t})).expect(\"Failed to start gotham\");\n//! # }\n//! ```\n//!\n//! # Features\n//!\n//! To make life easier for common use-cases, this create offers a few features that might be helpful\n//! when you implement your web server. The complete feature list is\n//! - [`auth`](#authentication-feature) Advanced JWT middleware\n//! - [`cors`](#cors-feature) CORS handling for all endpoint handlers\n//! - [`database`](#database-feature) diesel middleware support\n//! - `errorlog` log errors returned from endpoint handlers\n//! - `full` enables all features except `without-openapi`\n//! - [`openapi`](#openapi-feature) router additions to generate an openapi spec\n//! - `without-openapi` (**default**) disables `openapi` support.\n//!\n//! ## Authentication Feature\n//!\n//! In order to enable authentication support, enable the `auth` feature gate. This allows you to\n//! register a middleware that can automatically check for the existence of an JWT authentication\n//! token. Besides being supported by the endpoint macros, it supports to lookup the required JWT secret\n//! with the JWT data, hence you can use several JWT secrets and decide on the fly which secret to use.\n//! None of this is currently supported by gotham's own JWT middleware.\n//!\n//! A simple example that uses only a single secret looks like this:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # #[cfg(feature = \"auth\")]\n//! # mod auth_feature_enabled {\n//! # use gotham::{router::builder::*, pipeline::*, state::State};\n//! # use gotham_restful::*;\n//! # use serde::{Deserialize, Serialize};\n//! #[derive(Resource)]\n//! #[resource(read)]\n//! struct SecretResource;\n//!\n//! #[derive(Serialize)]\n//! # #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n//! struct Secret {\n//! \tid: u64,\n//! \tintended_for: String\n//! }\n//!\n//! #[derive(Deserialize, Clone)]\n//! struct AuthData {\n//! \tsub: String,\n//! \texp: u64\n//! }\n//!\n//! #[read]\n//! fn read(auth: AuthStatus, id: u64) -> AuthSuccess {\n//! \tlet intended_for = auth.ok()?.sub;\n//! \tOk(Secret { id, intended_for })\n//! }\n//!\n//! fn main() {\n//! \tlet auth: AuthMiddleware = AuthMiddleware::new(\n//! \t\tAuthSource::AuthorizationHeader,\n//! \t\tAuthValidation::default(),\n//! \t\tStaticAuthHandler::from_array(b\"zlBsA2QXnkmpe0QTh8uCvtAEa4j33YAc\")\n//! \t);\n//! \tlet (chain, pipelines) = single_pipeline(new_pipeline().add(auth).build());\n//! \tgotham::start(\n//! \t\t\"127.0.0.1:8080\",\n//! \t\tbuild_router(chain, pipelines, |route| {\n//! \t\t\troute.resource::(\"secret\");\n//! \t\t})\n//! \t)\n//! \t.expect(\"Failed to start gotham\");\n//! }\n//! # }\n//! ```\n//!\n//! ## CORS Feature\n//!\n//! The cors feature allows an easy usage of this web server from other origins. By default, only\n//! the `Access-Control-Allow-Methods` header is touched. To change the behaviour, add your desired\n//! configuration as a middleware.\n//!\n//! A simple example that allows authentication from every origin (note that `*` always disallows\n//! authentication), and every content type, looks like this:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # #[cfg(feature = \"cors\")]\n//! # mod cors_feature_enabled {\n//! # use gotham::{hyper::header::*, router::builder::*, pipeline::*, state::State};\n//! # use gotham_restful::{*, cors::*};\n//! # use serde::{Deserialize, Serialize};\n//! #[derive(Resource)]\n//! #[resource(read_all)]\n//! struct FooResource;\n//!\n//! #[read_all]\n//! fn read_all() {\n//! \t// your handler\n//! }\n//!\n//! fn main() {\n//! \tlet cors = CorsConfig {\n//! \t\torigin: Origin::Copy,\n//! \t\theaders: Headers::List(vec![CONTENT_TYPE]),\n//! \t\tmax_age: 0,\n//! \t\tcredentials: true\n//! \t};\n//! \tlet (chain, pipelines) = single_pipeline(new_pipeline().add(cors).build());\n//! \tgotham::start(\n//! \t\t\"127.0.0.1:8080\",\n//! \t\tbuild_router(chain, pipelines, |route| {\n//! \t\t\troute.resource::(\"foo\");\n//! \t\t})\n//! \t)\n//! \t.expect(\"Failed to start gotham\");\n//! }\n//! # }\n//! ```\n//!\n//! The cors feature can also be used for non-resource handlers. Take a look at [`CorsRoute`]\n//! for an example.\n//!\n//! ## Database Feature\n//!\n//! The database feature allows an easy integration of [diesel] into your handler functions. Please\n//! note however that due to the way gotham's diesel middleware implementation, it is not possible\n//! to run async code while holding a database connection. If you need to combine async and database,\n//! you'll need to borrow the connection from the [`State`] yourself and return a boxed future.\n//!\n//! A simple non-async example looks like this:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate diesel;\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # #[cfg(feature = \"database\")]\n//! # mod database_feature_enabled {\n//! # use diesel::{table, PgConnection, QueryResult, RunQueryDsl};\n//! # use gotham::{router::builder::*, pipeline::*, state::State};\n//! # use gotham_middleware_diesel::DieselMiddleware;\n//! # use gotham_restful::*;\n//! # use serde::{Deserialize, Serialize};\n//! # use std::env;\n//! # table! {\n//! # foo (id) {\n//! # id -> Int8,\n//! # value -> Text,\n//! # }\n//! # }\n//! #[derive(Resource)]\n//! #[resource(read_all)]\n//! struct FooResource;\n//!\n//! #[derive(Queryable, Serialize)]\n//! # #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n//! struct Foo {\n//! \tid: i64,\n//! \tvalue: String\n//! }\n//!\n//! #[read_all]\n//! fn read_all(conn: &mut PgConnection) -> QueryResult> {\n//! \tfoo::table.load(conn)\n//! }\n//!\n//! type Repo = gotham_middleware_diesel::Repo;\n//!\n//! fn main() {\n//! \tlet repo = Repo::new(&env::var(\"DATABASE_URL\").unwrap());\n//! \tlet diesel = DieselMiddleware::new(repo);\n//!\n//! \tlet (chain, pipelines) = single_pipeline(new_pipeline().add(diesel).build());\n//! \tgotham::start(\n//! \t\t\"127.0.0.1:8080\",\n//! \t\tbuild_router(chain, pipelines, |route| {\n//! \t\t\troute.resource::(\"foo\");\n//! \t\t})\n//! \t)\n//! \t.expect(\"Failed to start gotham\");\n//! }\n//! # }\n//! ```\n//!\n//! ## OpenAPI Feature\n//!\n//! The OpenAPI feature is probably the most powerful one of this crate. Definitely read this section\n//! carefully both as a binary as well as a library author to avoid unwanted suprises.\n//!\n//! In order to automatically create an openapi specification, gotham-restful needs knowledge over\n//! all routes and the types returned. `serde` does a great job at serialization but doesn't give\n//! enough type information, so all types used in the router need to implement\n//! [`OpenapiType`](openapi_type::OpenapiType). This can be derived for almoust any type and there\n//! should be no need to implement it manually. A simple example looks like this:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # #[cfg(feature = \"openapi\")]\n//! # mod openapi_feature_enabled {\n//! # use gotham::{router::builder::*, state::State};\n//! # use gotham_restful::*;\n//! # use openapi_type::OpenapiType;\n//! # use serde::{Deserialize, Serialize};\n//! #[derive(Resource)]\n//! #[resource(read_all)]\n//! struct FooResource;\n//!\n//! #[derive(OpenapiType, Serialize)]\n//! struct Foo {\n//! \tbar: String\n//! }\n//!\n//! #[read_all]\n//! fn read_all() -> Success {\n//! \tFoo {\n//! \t\tbar: \"Hello World\".to_owned()\n//! \t}\n//! \t.into()\n//! }\n//!\n//! fn main() {\n//! \tgotham::start(\n//! \t\t\"127.0.0.1:8080\",\n//! \t\tbuild_simple_router(|route| {\n//! \t\t\tlet info = OpenapiInfo {\n//! \t\t\t\ttitle: \"My Foo API\".to_owned(),\n//! \t\t\t\tversion: \"0.1.0\".to_owned(),\n//! \t\t\t\turls: vec![\"https://example.org/foo/api/v1\".to_owned()]\n//! \t\t\t};\n//! \t\t\troute.with_openapi(info, |mut route| {\n//! \t\t\t\troute.resource::(\"foo\");\n//! \t\t\t\troute.openapi_spec(\"openapi\");\n//! \t\t\t\troute.openapi_doc(\"/\");\n//! \t\t\t});\n//! \t\t})\n//! \t)\n//! \t.expect(\"Failed to start gotham\");\n//! }\n//! # }\n//! ```\n//!\n//! Above example adds the resource as before, but adds two other endpoints as well: `/openapi` and `/`.\n//! The first one will return the generated openapi specification in JSON format, allowing you to easily\n//! generate clients in different languages without worying to exactly replicate your api in each of those\n//! languages. The second one will return documentation in HTML format, so you can easily view your\n//! api and share it with other people.\n//!\n//! ### Gotchas\n//!\n//! The openapi feature has some gotchas you should be aware of.\n//!\n//! - The name of a struct is used as a \"link\" in the openapi specification. Therefore, if you have two\n//! structs with the same name in your project, the openapi specification will be invalid as only one\n//! of the two will make it into the spec.\n//! - By default, the `without-openapi` feature of this crate is enabled. Disabling it in favour of the\n//! `openapi` feature will add additional type bounds and method requirements to some of the traits and\n//! \ttypes in this crate, for example instead of [`Endpoint`] you now have to implement\n//! \t[`EndpointWithSchema`]. This means that some code might only compile on either feature, but not\n//! on both. If you are writing a library that uses gotham-restful, it is strongly recommended to pass\n//! \tboth features through and conditionally enable the openapi code, like this:\n//!\n//! ```rust\n//! # #[macro_use] extern crate gotham_restful;\n//! # use serde::{Deserialize, Serialize};\n//! #[derive(Deserialize, Serialize)]\n//! #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n//! struct Foo;\n//! ```\n//!\n//! [diesel]: https://diesel.rs/\n//! [`State`]: gotham::state::State\n\n#[cfg(all(feature = \"openapi\", feature = \"without-openapi\"))]\ncompile_error!(\"The 'openapi' and 'without-openapi' features cannot be combined\");\n\n#[cfg(all(not(feature = \"openapi\"), not(feature = \"without-openapi\")))]\ncompile_error!(\"Either the 'openapi' or 'without-openapi' feature needs to be enabled\");\n\n// weird proc macro issue\nextern crate self as gotham_restful;\n\n#[macro_use]\nextern crate gotham_restful_derive;\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate serde;\n\n#[cfg(test)]\n#[macro_use]\nextern crate pretty_assertions;\n\n#[doc(no_inline)]\npub use gotham;\n\npub use gotham_restful_derive::*;\n\n/// Not public API\n#[doc(hidden)]\npub mod private {\n\tpub use crate::routing::PathExtractor as IdPlaceholder;\n\tpub use futures_util::future::{BoxFuture, FutureExt};\n\t#[cfg(feature = \"database\")]\n\tpub use gotham_middleware_diesel::Repo;\n\t#[cfg(feature = \"openapi\")]\n\tpub use openapi_type::{OpenapiSchema, OpenapiType, Visitor};\n\tpub use serde_json;\n\n\t#[cfg(feature = \"openapi\")]\n\tuse gotham::hyper::StatusCode;\n\t#[cfg(feature = \"auth\")]\n\tuse gotham::state::{FromState, State};\n\n\t/// This method is used by the endpoint macro to generate a good error message\n\t/// when the used AuthData type does not implement Clone.\n\t#[cfg(feature = \"auth\")]\n\t#[inline]\n\tpub fn clone_from_state(state: &State) -> T\n\twhere\n\t\tT: FromState + Clone\n\t{\n\t\tT::borrow_from(state).clone()\n\t}\n\n\t/// This trait is used by the endpoint macro to generate a good error message\n\t/// when the schema function has the wrong signature.\n\t#[cfg(feature = \"openapi\")]\n\tpub trait CustomSchema {\n\t\tfn schema(self, code: StatusCode) -> OpenapiSchema;\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\timpl CustomSchema for F\n\twhere\n\t\tF: FnOnce(StatusCode) -> OpenapiSchema\n\t{\n\t\t#[inline]\n\t\tfn schema(self, code: StatusCode) -> OpenapiSchema {\n\t\t\tself(code)\n\t\t}\n\t}\n\n\t/// This trait is used by the endpoint macro to generate a good error message\n\t/// when the status_codes function has the wrong signature.\n\t#[cfg(feature = \"openapi\")]\n\tpub trait CustomStatusCodes {\n\t\tfn status_codes(self) -> Vec;\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\timpl CustomStatusCodes for F\n\twhere\n\t\tF: FnOnce() -> Vec\n\t{\n\t\t#[inline]\n\t\tfn status_codes(self) -> Vec {\n\t\t\tself()\n\t\t}\n\t}\n}\n\n#[cfg(feature = \"auth\")]\nmod auth;\n#[cfg(feature = \"auth\")]\npub use auth::{\n\tAuthHandler, AuthMiddleware, AuthSource, AuthStatus, AuthValidation, StaticAuthHandler\n};\n\n#[cfg(feature = \"cors\")]\npub mod cors;\n#[cfg(feature = \"cors\")]\npub use cors::{handle_cors, CorsConfig, CorsRoute};\n\n#[cfg(feature = \"openapi\")]\nmod openapi;\n#[cfg(feature = \"openapi\")]\npub use openapi::{builder::OpenapiInfo, router::GetOpenapi};\n\nmod endpoint;\n#[cfg(feature = \"openapi\")]\npub use endpoint::EndpointWithSchema;\npub use endpoint::{Endpoint, NoopExtractor};\n\nmod response;\npub use response::{\n\tAuthError, AuthErrorOrOther, AuthResult, AuthSuccess, IntoResponse, IntoResponseError,\n\tNoContent, Raw, Redirect, Response, Success\n};\n#[cfg(feature = \"openapi\")]\npub use response::{IntoResponseWithSchema, ResponseSchema};\n\nmod routing;\npub use routing::{DrawResourceRoutes, DrawResources};\n#[cfg(feature = \"openapi\")]\npub use routing::{DrawResourceRoutesWithSchema, DrawResourcesWithSchema, WithOpenapi};\n\nmod types;\npub use types::{FromBody, RequestBody, ResponseBody};\n\n/// This trait must be implemented for every resource. It allows you to register the different\n/// endpoints that can be handled by this resource to be registered with the underlying router.\n///\n/// It is not recommended to implement this yourself, just use `#[derive(Resource)]`.\n#[_private_openapi_trait(ResourceWithSchema)]\npub trait Resource {\n\t/// Register all methods handled by this resource with the underlying router.\n\t#[openapi_bound(D: crate::DrawResourceRoutesWithSchema)]\n\t#[non_openapi_bound(D: crate::DrawResourceRoutes)]\n\tfn setup(route: D);\n}\n","traces":[{"line":463,"address":[15569646],"length":1,"stats":{"Line":9},"fn_name":null},{"line":464,"address":[16240502],"length":1,"stats":{"Line":9},"fn_name":null},{"line":466,"address":[11060432],"length":1,"stats":{"Line":0},"fn_name":null},{"line":467,"address":[15569712,15569808],"length":1,"stats":{"Line":0},"fn_name":"ok_or"},{"line":489,"address":[4033744],"length":1,"stats":{"Line":0},"fn_name":null},{"line":506,"address":[15618436],"length":1,"stats":{"Line":0},"fn_name":null},{"line":507,"address":[14689591],"length":1,"stats":{"Line":14},"fn_name":null},{"line":508,"address":[11061239],"length":1,"stats":{"Line":14},"fn_name":null},{"line":512,"address":[15570457],"length":1,"stats":{"Line":0},"fn_name":null},{"line":518,"address":[7415995,7415970,7415740,7415658,7415750,7415766],"length":1,"stats":{"Line":7},"fn_name":null},{"line":523,"address":[11061557],"length":1,"stats":{"Line":7},"fn_name":null},{"line":527,"address":[14689732],"length":1,"stats":{"Line":0},"fn_name":null},{"line":528,"address":[7396110],"length":1,"stats":{"Line":8},"fn_name":null},{"line":529,"address":[16243630],"length":1,"stats":{"Line":8},"fn_name":null},{"line":536,"address":[16243768],"length":1,"stats":{"Line":0},"fn_name":null},{"line":541,"address":[15570624],"length":1,"stats":{"Line":0},"fn_name":null},{"line":545,"address":[15618727],"length":1,"stats":{"Line":0},"fn_name":null},{"line":546,"address":[15618771],"length":1,"stats":{"Line":1},"fn_name":null},{"line":547,"address":[15618786],"length":1,"stats":{"Line":1},"fn_name":null},{"line":552,"address":[11061608],"length":1,"stats":{"Line":0},"fn_name":null},{"line":559,"address":[11061646],"length":1,"stats":{"Line":0},"fn_name":null},{"line":564,"address":[10347552,10347520],"length":1,"stats":{"Line":0},"fn_name":"read_i24"},{"line":595,"address":[15570888],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":10,"coverable":23},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","openapi","builder.rs"],"content":"use openapi_type::{\n\tindexmap::IndexMap,\n\topenapiv3::{\n\t\tself, Components, OpenAPI, PathItem, ReferenceOr,\n\t\tReferenceOr::{Item, Reference},\n\t\tSchema, Server\n\t},\n\tOpenapiSchema\n};\nuse parking_lot::RwLock;\nuse std::sync::Arc;\n\n#[derive(Clone, Debug)]\npub struct OpenapiInfo {\n\tpub title: String,\n\tpub version: String,\n\tpub urls: Vec\n}\n\n#[derive(Clone, Debug)]\npub(crate) struct OpenapiBuilder {\n\tpub(crate) openapi: Arc>\n}\n\nimpl OpenapiBuilder {\n\tpub(crate) fn new(info: OpenapiInfo) -> Self {\n\t\tSelf {\n\t\t\topenapi: Arc::new(RwLock::new(OpenAPI {\n\t\t\t\topenapi: \"3.0.2\".to_string(),\n\t\t\t\tinfo: openapiv3::Info {\n\t\t\t\t\ttitle: info.title,\n\t\t\t\t\tversion: info.version,\n\t\t\t\t\t..Default::default()\n\t\t\t\t},\n\t\t\t\tservers: info\n\t\t\t\t\t.urls\n\t\t\t\t\t.into_iter()\n\t\t\t\t\t.map(|url| Server {\n\t\t\t\t\t\turl,\n\t\t\t\t\t\t..Default::default()\n\t\t\t\t\t})\n\t\t\t\t\t.collect(),\n\t\t\t\t..Default::default()\n\t\t\t}))\n\t\t}\n\t}\n\n\t/// Remove path from the OpenAPI spec, or return an empty one if not included. This is handy if you need to\n\t/// modify the path and add it back after the modification\n\tpub(crate) fn remove_path(&mut self, path: &str) -> PathItem {\n\t\tlet mut openapi = self.openapi.write();\n\t\tmatch openapi.paths.paths.swap_remove(path) {\n\t\t\tSome(Item(item)) => item,\n\t\t\t_ => PathItem::default()\n\t\t}\n\t}\n\n\tpub(crate) fn add_path(&mut self, path: Path, item: PathItem) {\n\t\tlet mut openapi = self.openapi.write();\n\t\topenapi.paths.paths.insert(path.to_string(), Item(item));\n\t}\n\n\tfn add_schema_impl(&mut self, name: String, mut schema: OpenapiSchema) {\n\t\tself.add_schema_dependencies(&mut schema.dependencies);\n\n\t\tlet mut openapi = self.openapi.write();\n\t\tmatch &mut openapi.components {\n\t\t\tSome(comp) => {\n\t\t\t\tcomp.schemas.insert(name, Item(schema.schema));\n\t\t\t},\n\t\t\tNone => {\n\t\t\t\tlet mut comp = Components::default();\n\t\t\t\tcomp.schemas.insert(name, Item(schema.schema));\n\t\t\t\topenapi.components = Some(comp);\n\t\t\t}\n\t\t};\n\t}\n\n\tfn add_schema_dependencies(&mut self, dependencies: &mut IndexMap) {\n\t\tlet keys: Vec = dependencies.keys().map(|k| k.to_string()).collect();\n\t\tfor dep in keys {\n\t\t\tlet dep_schema = dependencies.swap_remove(&dep);\n\t\t\tif let Some(dep_schema) = dep_schema {\n\t\t\t\tself.add_schema_impl(dep, dep_schema);\n\t\t\t}\n\t\t}\n\t}\n\n\tpub(crate) fn add_schema(&mut self, mut schema: OpenapiSchema) -> ReferenceOr {\n\t\tmatch schema.schema.schema_data.title.clone() {\n\t\t\tSome(name) => {\n\t\t\t\tlet reference = Reference {\n\t\t\t\t\treference: format!(\"#/components/schemas/{name}\")\n\t\t\t\t};\n\t\t\t\tself.add_schema_impl(name, schema);\n\t\t\t\treference\n\t\t\t},\n\t\t\tNone => {\n\t\t\t\tself.add_schema_dependencies(&mut schema.dependencies);\n\t\t\t\tItem(schema.schema)\n\t\t\t}\n\t\t}\n\t}\n}\n\n#[cfg(test)]\n#[allow(dead_code)]\nmod test {\n\tuse super::*;\n\tuse openapi_type::OpenapiType;\n\n\t#[derive(OpenapiType)]\n\tstruct Message {\n\t\tmsg: String\n\t}\n\n\t#[derive(OpenapiType)]\n\tstruct Messages {\n\t\tmsgs: Vec\n\t}\n\n\tfn info() -> OpenapiInfo {\n\t\tOpenapiInfo {\n\t\t\ttitle: \"TEST CASE\".to_owned(),\n\t\t\tversion: \"1.2.3\".to_owned(),\n\t\t\turls: vec![\n\t\t\t\t\"http://localhost:1234\".to_owned(),\n\t\t\t\t\"https://example.org\".to_owned(),\n\t\t\t]\n\t\t}\n\t}\n\n\tfn openapi(builder: OpenapiBuilder) -> OpenAPI {\n\t\tArc::try_unwrap(builder.openapi).unwrap().into_inner()\n\t}\n\n\t#[test]\n\tfn new_builder() {\n\t\tlet info = info();\n\t\tlet builder = OpenapiBuilder::new(info.clone());\n\t\tlet openapi = openapi(builder);\n\n\t\tassert_eq!(info.title, openapi.info.title);\n\t\tassert_eq!(info.version, openapi.info.version);\n\t\tassert_eq!(info.urls.len(), openapi.servers.len());\n\t}\n\n\t#[test]\n\tfn add_schema() {\n\t\tlet mut builder = OpenapiBuilder::new(info());\n\t\tbuilder.add_schema(>::schema());\n\t\tlet openapi = openapi(builder);\n\n\t\tassert_eq!(\n\t\t\topenapi.components.clone().unwrap_or_default().schemas[\"Message\"],\n\t\t\tReferenceOr::Item(Message::schema().schema)\n\t\t);\n\t\tassert_eq!(\n\t\t\topenapi.components.clone().unwrap_or_default().schemas[\"Messages\"],\n\t\t\tReferenceOr::Item(Messages::schema().schema)\n\t\t);\n\t}\n}\n","traces":[{"line":26,"address":[7639362,7638204,7637504],"length":1,"stats":{"Line":3},"fn_name":null},{"line":28,"address":[8363131,8364013,8364546],"length":1,"stats":{"Line":9},"fn_name":null},{"line":50,"address":[8365376,8365662],"length":1,"stats":{"Line":2},"fn_name":null},{"line":51,"address":[8365499],"length":1,"stats":{"Line":2},"fn_name":null},{"line":52,"address":[8365533,8365688,8365606],"length":1,"stats":{"Line":5},"fn_name":null},{"line":53,"address":[8365723],"length":1,"stats":{"Line":1},"fn_name":null},{"line":54,"address":[7640217,7640116],"length":1,"stats":{"Line":4},"fn_name":null},{"line":58,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":59,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":60,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":63,"address":[7641786,7641640,7640480],"length":1,"stats":{"Line":2},"fn_name":null},{"line":64,"address":[7640553],"length":1,"stats":{"Line":2},"fn_name":null},{"line":66,"address":[8366264],"length":1,"stats":{"Line":2},"fn_name":null},{"line":67,"address":[8366399,8366326],"length":1,"stats":{"Line":4},"fn_name":null},{"line":68,"address":[8366481],"length":1,"stats":{"Line":2},"fn_name":null},{"line":69,"address":[8366489],"length":1,"stats":{"Line":2},"fn_name":null},{"line":72,"address":[7640850],"length":1,"stats":{"Line":2},"fn_name":null},{"line":73,"address":[7641101,7641306],"length":1,"stats":{"Line":4},"fn_name":null},{"line":74,"address":[7641325,7641618],"length":1,"stats":{"Line":2},"fn_name":null},{"line":79,"address":[7642525,7642482,7641824],"length":1,"stats":{"Line":3},"fn_name":null},{"line":80,"address":[7642579,7641870,7642544],"length":1,"stats":{"Line":7},"fn_name":"{closure#0}"},{"line":81,"address":[7641918,7642131,7642501],"length":1,"stats":{"Line":7},"fn_name":null},{"line":82,"address":[7642239,7642171],"length":1,"stats":{"Line":4},"fn_name":null},{"line":83,"address":[7642247],"length":1,"stats":{"Line":2},"fn_name":null},{"line":84,"address":[7642409,7642326],"length":1,"stats":{"Line":4},"fn_name":null},{"line":89,"address":[7643413,7642608,7643283],"length":1,"stats":{"Line":3},"fn_name":null},{"line":90,"address":[7642651,7642755],"length":1,"stats":{"Line":6},"fn_name":null},{"line":91,"address":[7642808],"length":1,"stats":{"Line":1},"fn_name":null},{"line":93,"address":[7642959],"length":1,"stats":{"Line":1},"fn_name":null},{"line":95,"address":[7643112],"length":1,"stats":{"Line":1},"fn_name":null},{"line":96,"address":[7643258],"length":1,"stats":{"Line":1},"fn_name":null},{"line":99,"address":[7642791],"length":1,"stats":{"Line":3},"fn_name":null},{"line":100,"address":[7643334],"length":1,"stats":{"Line":3},"fn_name":null}],"covered":33,"coverable":33},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","openapi","handler","mod.rs"],"content":"#![cfg_attr(not(feature = \"auth\"), allow(unused_imports))]\nuse super::SECURITY_NAME;\nuse base64::prelude::*;\nuse futures_util::{future, future::FutureExt};\nuse gotham::{\n\tanyhow,\n\thandler::{Handler, HandlerError, HandlerFuture, NewHandler},\n\thelpers::http::response::{create_empty_response, create_response},\n\thyper::{\n\t\theader::{\n\t\t\tHeaderMap, HeaderValue, CACHE_CONTROL, CONTENT_SECURITY_POLICY, ETAG, IF_NONE_MATCH,\n\t\t\tREFERRER_POLICY, X_CONTENT_TYPE_OPTIONS\n\t\t},\n\t\tBody, Response, StatusCode\n\t},\n\tmime::{APPLICATION_JSON, TEXT_HTML_UTF_8, TEXT_PLAIN_UTF_8},\n\tstate::State\n};\nuse gotham_restful_redoc::Redoc;\nuse openapi_type::{\n\tindexmap::IndexMap,\n\topenapiv3::{APIKeyLocation, OpenAPI, ReferenceOr, SecurityScheme}\n};\nuse parking_lot::RwLock;\nuse sha2::{Digest, Sha256};\nuse std::{panic::RefUnwindSafe, pin::Pin, sync::Arc};\n\n#[cfg(feature = \"auth\")]\nfn get_security(state: &State) -> IndexMap> {\n\tuse crate::AuthSource;\n\tuse gotham::state::FromState;\n\n\tlet source = match AuthSource::try_borrow_from(state) {\n\t\tSome(source) => source,\n\t\tNone => return Default::default()\n\t};\n\n\tlet security_scheme = match source {\n\t\tAuthSource::Cookie(name) => SecurityScheme::APIKey {\n\t\t\tlocation: APIKeyLocation::Cookie,\n\t\t\tname: name.to_string(),\n\t\t\tdescription: None\n\t\t},\n\t\tAuthSource::Header(name) => SecurityScheme::APIKey {\n\t\t\tlocation: APIKeyLocation::Header,\n\t\t\tname: name.to_string(),\n\t\t\tdescription: None\n\t\t},\n\t\tAuthSource::AuthorizationHeader => SecurityScheme::HTTP {\n\t\t\tscheme: \"bearer\".to_owned(),\n\t\t\tbearer_format: Some(\"JWT\".to_owned()),\n\t\t\tdescription: None\n\t\t}\n\t};\n\n\tlet mut security_schemes: IndexMap> = Default::default();\n\tsecurity_schemes.insert(SECURITY_NAME.to_owned(), ReferenceOr::Item(security_scheme));\n\n\tsecurity_schemes\n}\n\n#[cfg(not(feature = \"auth\"))]\nfn get_security(_state: &State) -> IndexMap> {\n\tDefault::default()\n}\n\nfn openapi_string(\n\tstate: &State,\n\topenapi: &Arc>\n) -> Result {\n\tlet openapi = openapi.read();\n\n\tlet mut openapi = openapi.clone();\n\tlet security_schemes = get_security(state);\n\tlet mut components = openapi.components.unwrap_or_default();\n\tcomponents.security_schemes = security_schemes;\n\topenapi.components = Some(components);\n\n\tserde_json::to_string(&openapi)\n}\n\nfn create_openapi_response(state: &State, openapi: &Arc>) -> Response {\n\tmatch openapi_string(state, openapi) {\n\t\tOk(body) => {\n\t\t\tlet mut res = create_response(state, StatusCode::OK, APPLICATION_JSON, body);\n\t\t\tlet headers = res.headers_mut();\n\t\t\theaders.insert(X_CONTENT_TYPE_OPTIONS, HeaderValue::from_static(\"nosniff\"));\n\t\t\tres\n\t\t},\n\t\tErr(e) => {\n\t\t\terror!(\"Unable to handle OpenAPI request due to error: {e}\");\n\t\t\tcreate_response(\n\t\t\t\tstate,\n\t\t\t\tStatusCode::INTERNAL_SERVER_ERROR,\n\t\t\t\tTEXT_PLAIN_UTF_8,\n\t\t\t\t\"\"\n\t\t\t)\n\t\t}\n\t}\n}\n\n#[derive(Clone)]\npub(crate) struct OpenapiSpecHandler {\n\topenapi: Arc>\n}\n\n// safety: the handler only ever aquires a read lock, so this usage of\n// RwLock is, in fact, unwind safe\nimpl RefUnwindSafe for OpenapiSpecHandler {}\n\nimpl OpenapiSpecHandler {\n\tpub(crate) fn new(openapi: Arc>) -> Self {\n\t\tSelf { openapi }\n\t}\n}\n\nimpl NewHandler for OpenapiSpecHandler {\n\ttype Instance = Self;\n\n\tfn new_handler(&self) -> anyhow::Result {\n\t\tOk(self.clone())\n\t}\n}\n\nimpl Handler for OpenapiSpecHandler {\n\tfn handle(self, mut state: State) -> Pin> {\n\t\tlet res = create_openapi_response(&mut state, &self.openapi);\n\t\tfuture::ok((state, res)).boxed()\n\t}\n}\n\n#[derive(Clone)]\npub(crate) struct OpenapiDocHandler {\n\topenapi: Arc>\n}\n\n// safety: the handler only ever aquires a read lock, so this usage of\n// RwLock is, in fact, unwind safe\nimpl RefUnwindSafe for OpenapiDocHandler {}\n\nimpl OpenapiDocHandler {\n\tpub(crate) fn new(openapi: Arc>) -> Self {\n\t\tSelf { openapi }\n\t}\n}\n\nimpl NewHandler for OpenapiDocHandler {\n\ttype Instance = Self;\n\n\tfn new_handler(&self) -> anyhow::Result {\n\t\tOk(self.clone())\n\t}\n}\n\nfn redoc_handler(\n\tstate: &State,\n\topenapi: &Arc>\n) -> Result, HandlerError> {\n\tlet spec = openapi_string(state, openapi)?;\n\tlet Redoc { html, script_hash } = gotham_restful_redoc::html(spec);\n\n\tlet mut etag = Sha256::new();\n\tetag.update(&html);\n\tlet etag = format!(\"\\\"{}\\\"\", BASE64_STANDARD.encode(etag.finalize()));\n\n\tif state\n\t\t.borrow::()\n\t\t.get(IF_NONE_MATCH)\n\t\t.map_or(false, |header| header.as_bytes() == etag.as_bytes())\n\t{\n\t\tlet res = create_empty_response(state, StatusCode::NOT_MODIFIED);\n\t\treturn Ok(res);\n\t}\n\n\tlet mut res = create_response(state, StatusCode::OK, TEXT_HTML_UTF_8, html);\n\tlet headers = res.headers_mut();\n\theaders.insert(\n\t\tCACHE_CONTROL,\n\t\tHeaderValue::from_static(\"public,max-age=2592000\")\n\t);\n\theaders.insert(\n\t\tCONTENT_SECURITY_POLICY,\n\t\tformat!(\n\t\t\t\"default-src 'none';base-uri 'none';script-src 'unsafe-inline' https://cdn.jsdelivr.net 'sha256-{script_hash}' 'strict-dynamic';style-src 'unsafe-inline' https://fonts.googleapis.com;font-src https://fonts.gstatic.com;connect-src 'self';img-src blob: data:\",\n\t\t).parse().unwrap()\n\t);\n\theaders.insert(ETAG, etag.parse().unwrap());\n\theaders.insert(REFERRER_POLICY, HeaderValue::from_static(\"no-referrer\"));\n\theaders.insert(X_CONTENT_TYPE_OPTIONS, HeaderValue::from_static(\"nosniff\"));\n\tOk(res)\n}\n\nimpl Handler for OpenapiDocHandler {\n\tfn handle(self, state: State) -> Pin> {\n\t\tmatch redoc_handler(&state, &self.openapi) {\n\t\t\tOk(res) => future::ok((state, res)).boxed(),\n\t\t\tErr(err) => future::err((state, err)).boxed()\n\t\t}\n\t}\n}\n","traces":[{"line":29,"address":[7822107,7821312],"length":1,"stats":{"Line":2},"fn_name":"get_security"},{"line":33,"address":[7821466,7821350],"length":1,"stats":{"Line":2},"fn_name":null},{"line":34,"address":[7821411],"length":1,"stats":{"Line":1},"fn_name":null},{"line":35,"address":[7821400],"length":1,"stats":{"Line":1},"fn_name":null},{"line":38,"address":[7821429],"length":1,"stats":{"Line":1},"fn_name":null},{"line":41,"address":[7821493],"length":1,"stats":{"Line":0},"fn_name":null},{"line":46,"address":[7821660],"length":1,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[7821803],"length":1,"stats":{"Line":1},"fn_name":null},{"line":51,"address":[7821899,7821828],"length":1,"stats":{"Line":2},"fn_name":null},{"line":56,"address":[7822133],"length":1,"stats":{"Line":1},"fn_name":null},{"line":57,"address":[7822258,7822190],"length":1,"stats":{"Line":2},"fn_name":null},{"line":59,"address":[7822400],"length":1,"stats":{"Line":1},"fn_name":null},{"line":67,"address":[7823375,7822464,7823875],"length":1,"stats":{"Line":2},"fn_name":"openapi_string"},{"line":71,"address":[7822530],"length":1,"stats":{"Line":2},"fn_name":null},{"line":73,"address":[7822660,7822588],"length":1,"stats":{"Line":4},"fn_name":null},{"line":74,"address":[7822688,7822736],"length":1,"stats":{"Line":4},"fn_name":null},{"line":75,"address":[7822744,7822852],"length":1,"stats":{"Line":4},"fn_name":null},{"line":76,"address":[7822868],"length":1,"stats":{"Line":2},"fn_name":null},{"line":77,"address":[7823124],"length":1,"stats":{"Line":2},"fn_name":null},{"line":79,"address":[7823251],"length":1,"stats":{"Line":2},"fn_name":null},{"line":82,"address":[7823904,7824759],"length":1,"stats":{"Line":2},"fn_name":"create_openapi_response"},{"line":83,"address":[7823942],"length":1,"stats":{"Line":2},"fn_name":null},{"line":84,"address":[7823995],"length":1,"stats":{"Line":2},"fn_name":null},{"line":85,"address":[7824018],"length":1,"stats":{"Line":2},"fn_name":null},{"line":86,"address":[7824844,7824153],"length":1,"stats":{"Line":4},"fn_name":null},{"line":87,"address":[7824852],"length":1,"stats":{"Line":2},"fn_name":null},{"line":88,"address":[7825056],"length":1,"stats":{"Line":2},"fn_name":null},{"line":90,"address":[7824172],"length":1,"stats":{"Line":0},"fn_name":null},{"line":91,"address":[7824185,7824621,7824275,7824333],"length":1,"stats":{"Line":0},"fn_name":null},{"line":95,"address":[7824409],"length":1,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[7825088],"length":1,"stats":{"Line":2},"fn_name":null},{"line":120,"address":[7825104],"length":1,"stats":{"Line":2},"fn_name":"new_handler"},{"line":121,"address":[7825113],"length":1,"stats":{"Line":2},"fn_name":null},{"line":126,"address":[7825498,7825152],"length":1,"stats":{"Line":2},"fn_name":"handle"},{"line":127,"address":[7825195],"length":1,"stats":{"Line":2},"fn_name":null},{"line":128,"address":[7825169,7825248],"length":1,"stats":{"Line":4},"fn_name":null},{"line":142,"address":[7825536],"length":1,"stats":{"Line":0},"fn_name":null},{"line":150,"address":[7825552],"length":1,"stats":{"Line":0},"fn_name":"new_handler"},{"line":151,"address":[7825561],"length":1,"stats":{"Line":0},"fn_name":null},{"line":155,"address":[7825600,7826576,7828924],"length":1,"stats":{"Line":0},"fn_name":"redoc_handler"},{"line":159,"address":[7825918,7825647],"length":1,"stats":{"Line":0},"fn_name":null},{"line":160,"address":[7825843,7826001],"length":1,"stats":{"Line":0},"fn_name":null},{"line":162,"address":[7826073],"length":1,"stats":{"Line":0},"fn_name":null},{"line":163,"address":[7826136],"length":1,"stats":{"Line":0},"fn_name":null},{"line":164,"address":[7826417,7826587,7826163],"length":1,"stats":{"Line":0},"fn_name":null},{"line":166,"address":[7826671,7826772,7826817],"length":1,"stats":{"Line":0},"fn_name":null},{"line":168,"address":[7826742],"length":1,"stats":{"Line":0},"fn_name":null},{"line":169,"address":[7826809,7828985,7828960],"length":1,"stats":{"Line":0},"fn_name":"{closure#0}"},{"line":171,"address":[7827045],"length":1,"stats":{"Line":0},"fn_name":null},{"line":172,"address":[7827112],"length":1,"stats":{"Line":0},"fn_name":null},{"line":175,"address":[7826864],"length":1,"stats":{"Line":0},"fn_name":null},{"line":176,"address":[7827339,7827266],"length":1,"stats":{"Line":0},"fn_name":null},{"line":177,"address":[7827454],"length":1,"stats":{"Line":0},"fn_name":null},{"line":178,"address":[7827347],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[7827385],"length":1,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[7827959],"length":1,"stats":{"Line":0},"fn_name":null},{"line":182,"address":[7827546],"length":1,"stats":{"Line":0},"fn_name":null},{"line":183,"address":[7827713,7827901],"length":1,"stats":{"Line":0},"fn_name":null},{"line":187,"address":[7828111],"length":1,"stats":{"Line":0},"fn_name":null},{"line":188,"address":[7828375],"length":1,"stats":{"Line":0},"fn_name":null},{"line":189,"address":[7828574],"length":1,"stats":{"Line":0},"fn_name":null},{"line":190,"address":[7828816],"length":1,"stats":{"Line":0},"fn_name":null},{"line":194,"address":[7829056,7829663],"length":1,"stats":{"Line":0},"fn_name":"handle"},{"line":195,"address":[7829073,7829528,7829147],"length":1,"stats":{"Line":0},"fn_name":null},{"line":196,"address":[7829214,7829580],"length":1,"stats":{"Line":0},"fn_name":null},{"line":197,"address":[7829344],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":31,"coverable":66},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","openapi","mod.rs"],"content":"const SECURITY_NAME: &str = \"authToken\";\n\npub(crate) mod builder;\npub(crate) mod handler;\npub(crate) mod operation;\npub(crate) mod router;\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","openapi","operation.rs"],"content":"use super::SECURITY_NAME;\nuse crate::{response::OrAllTypes, EndpointWithSchema, IntoResponse, RequestBody};\nuse gotham::{hyper::StatusCode, mime::Mime};\nuse openapi_type::{\n\tindexmap::IndexMap,\n\topenapiv3::{\n\t\tMediaType, Operation, Parameter, ParameterData, ParameterSchemaOrContent, ReferenceOr,\n\t\tReferenceOr::Item, RequestBody as OARequestBody, Response, Responses, Schema, SchemaKind,\n\t\tStatusCode as OAStatusCode, Type\n\t},\n\tOpenapiSchema\n};\nuse std::collections::HashMap;\n\nfn new_parameter_data(\n\tname: String,\n\trequired: bool,\n\tschema: ReferenceOr>\n) -> ParameterData {\n\tParameterData {\n\t\tname,\n\t\tdescription: None,\n\t\trequired,\n\t\tdeprecated: None,\n\t\tformat: ParameterSchemaOrContent::Schema(schema.unbox()),\n\t\texample: None,\n\t\texamples: Default::default(),\n\t\texplode: None,\n\t\textensions: Default::default()\n\t}\n}\n\n#[derive(Default)]\nstruct OperationParams {\n\tpath_params: Option,\n\tquery_params: Option\n}\n\nimpl OperationParams {\n\t// TODO shouldn't this be a custom openapi_type::Visitor\n\t// rather than this hacky code?\n\tfn add_path_params(\n\t\tpath_params: Option,\n\t\tparams: &mut Vec>\n\t) {\n\t\tlet path_params = match path_params {\n\t\t\tSome(pp) => pp.schema,\n\t\t\tNone => return\n\t\t};\n\t\tlet path_params = match path_params.schema_kind {\n\t\t\tSchemaKind::Type(Type::Object(ty)) => ty,\n\t\t\t_ => panic!(\"Path Parameters needs to be a plain struct\")\n\t\t};\n\t\tfor (name, schema) in path_params.properties {\n\t\t\tlet required = path_params.required.contains(&name);\n\t\t\tparams.push(Item(Parameter::Path {\n\t\t\t\tparameter_data: new_parameter_data(name, required, schema),\n\t\t\t\tstyle: Default::default()\n\t\t\t}))\n\t\t}\n\t}\n\n\t// TODO shouldn't this be a custom openapi_type::Visitor\n\t// rather than this hacky code?\n\tfn add_query_params(\n\t\tquery_params: Option,\n\t\tparams: &mut Vec>\n\t) {\n\t\tlet query_params = match query_params {\n\t\t\tSome(qp) => qp.schema,\n\t\t\tNone => return\n\t\t};\n\t\tlet query_params = match query_params.schema_kind {\n\t\t\tSchemaKind::Type(Type::Object(ty)) => ty,\n\t\t\t_ => panic!(\"Query Parameters needs to be a plain struct\")\n\t\t};\n\t\tfor (name, schema) in query_params.properties {\n\t\t\tlet required = query_params.required.contains(&name);\n\t\t\tparams.push(Item(Parameter::Query {\n\t\t\t\tparameter_data: new_parameter_data(name, required, schema),\n\t\t\t\tallow_reserved: false,\n\t\t\t\tstyle: Default::default(),\n\t\t\t\tallow_empty_value: None\n\t\t\t}))\n\t\t}\n\t}\n\n\tfn into_params(self) -> Vec> {\n\t\tlet mut params: Vec> = Vec::new();\n\t\tSelf::add_path_params(self.path_params, &mut params);\n\t\tSelf::add_query_params(self.query_params, &mut params);\n\t\tparams\n\t}\n}\n\npub(crate) struct OperationDescription {\n\toperation_id: Option,\n\tdescription: Option,\n\n\taccepted_types: Option>,\n\tresponses: HashMap>,\n\tparams: OperationParams,\n\tbody_schema: Option>,\n\tsupported_types: Option>,\n\trequires_auth: bool\n}\n\nimpl OperationDescription {\n\t/// Create a new operation description for the given endpoint type and schema. If the endpoint\n\t/// does not specify an operation id, the path is used to generate one.\n\tpub(crate) fn new(\n\t\tresponses: HashMap>,\n\t\tpath: &str\n\t) -> Self {\n\t\tlet operation_id = E::operation_id().or_else(|| {\n\t\t\tE::operation_verb()\n\t\t\t\t.map(|verb| format!(\"{verb}_{}\", path.replace(\"/\", \"_\").trim_start_matches('_')))\n\t\t});\n\t\tSelf {\n\t\t\toperation_id,\n\t\t\tdescription: E::description(),\n\n\t\t\taccepted_types: E::Output::accepted_types(),\n\t\t\tresponses,\n\t\t\tparams: Default::default(),\n\t\t\tbody_schema: None,\n\t\t\tsupported_types: None,\n\t\t\trequires_auth: E::wants_auth()\n\t\t}\n\t}\n\n\tpub(crate) fn set_path_params(&mut self, params: OpenapiSchema) {\n\t\tself.params.path_params = Some(params);\n\t}\n\n\tpub(crate) fn set_query_params(&mut self, params: OpenapiSchema) {\n\t\tself.params.query_params = Some(params);\n\t}\n\n\tpub(crate) fn set_body(&mut self, schema: ReferenceOr) {\n\t\tself.body_schema = Some(schema);\n\t\tself.supported_types = Body::supported_types();\n\t}\n\n\tfn schema_to_content(\n\t\ttypes: Vec,\n\t\tschema: ReferenceOr\n\t) -> IndexMap {\n\t\tlet mut content: IndexMap = IndexMap::new();\n\t\tfor ty in types {\n\t\t\tcontent.insert(ty.to_string(), MediaType {\n\t\t\t\tschema: Some(schema.clone()),\n\t\t\t\t..Default::default()\n\t\t\t});\n\t\t}\n\t\tcontent\n\t}\n\n\tpub(crate) fn into_operation(self) -> Operation {\n\t\t// this is unfortunately neccessary to prevent rust from complaining about partially moving self\n\t\tlet (\n\t\t\toperation_id,\n\t\t\tdescription,\n\t\t\taccepted_types,\n\t\t\tresponses,\n\t\t\tparams,\n\t\t\tbody_schema,\n\t\t\tsupported_types,\n\t\t\trequires_auth\n\t\t) = (\n\t\t\tself.operation_id,\n\t\t\tself.description,\n\t\t\tself.accepted_types,\n\t\t\tself.responses,\n\t\t\tself.params,\n\t\t\tself.body_schema,\n\t\t\tself.supported_types,\n\t\t\tself.requires_auth\n\t\t);\n\n\t\tlet responses: IndexMap> = responses\n\t\t\t.into_iter()\n\t\t\t.map(|(code, schema)| {\n\t\t\t\tlet content =\n\t\t\t\t\tSelf::schema_to_content(accepted_types.clone().or_all_types(), schema);\n\t\t\t\t(\n\t\t\t\t\tOAStatusCode::Code(code.as_u16()),\n\t\t\t\t\tItem(Response {\n\t\t\t\t\t\tdescription: code\n\t\t\t\t\t\t\t.canonical_reason()\n\t\t\t\t\t\t\t.map(|d| d.to_string())\n\t\t\t\t\t\t\t.unwrap_or_default(),\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\t..Default::default()\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t})\n\t\t\t.collect();\n\n\t\tlet request_body = body_schema.map(|schema| {\n\t\t\tItem(OARequestBody {\n\t\t\t\tcontent: Self::schema_to_content(supported_types.or_all_types(), schema),\n\t\t\t\trequired: true,\n\t\t\t\t..Default::default()\n\t\t\t})\n\t\t});\n\n\t\tlet mut security = None;\n\t\tif requires_auth {\n\t\t\tlet mut sec = IndexMap::new();\n\t\t\tsec.insert(SECURITY_NAME.to_owned(), Vec::new());\n\t\t\tsecurity = Some(vec![sec]);\n\t\t}\n\n\t\tOperation {\n\t\t\ttags: Vec::new(),\n\t\t\toperation_id,\n\t\t\tdescription,\n\t\t\tparameters: params.into_params(),\n\t\t\trequest_body,\n\t\t\tresponses: Responses {\n\t\t\t\tresponses,\n\t\t\t\t..Default::default()\n\t\t\t},\n\t\t\tdeprecated: false,\n\t\t\tsecurity,\n\t\t\t..Default::default()\n\t\t}\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse crate::{NoContent, Raw, ResponseSchema};\n\n\t#[test]\n\tfn no_content_schema_to_content() {\n\t\tlet types = NoContent::accepted_types();\n\t\tlet schema = ::schema(StatusCode::NO_CONTENT);\n\t\tlet content =\n\t\t\tOperationDescription::schema_to_content(types.or_all_types(), Item(schema.schema));\n\t\tassert!(content.is_empty());\n\t}\n\n\t#[test]\n\tfn raw_schema_to_content() {\n\t\tlet types = Raw::<&str>::accepted_types();\n\t\tlet schema = as ResponseSchema>::schema(StatusCode::OK);\n\t\tlet content =\n\t\t\tOperationDescription::schema_to_content(types.or_all_types(), Item(schema.schema));\n\t\tassert_eq!(content.len(), 1);\n\t\tlet json = serde_json::to_string(&content.values().nth(0).unwrap()).unwrap();\n\t\tassert_eq!(json, r#\"{\"schema\":{\"type\":\"string\",\"format\":\"binary\"}}\"#);\n\t}\n}\n","traces":[{"line":15,"address":[7758064,7758671,7758625],"length":1,"stats":{"Line":1},"fn_name":"new_parameter_data"},{"line":25,"address":[7758224,7758139],"length":1,"stats":{"Line":2},"fn_name":null},{"line":27,"address":[7758234],"length":1,"stats":{"Line":1},"fn_name":null},{"line":29,"address":[7758305],"length":1,"stats":{"Line":1},"fn_name":null},{"line":42,"address":[7758688,7759914,7760202],"length":1,"stats":{"Line":2},"fn_name":null},{"line":46,"address":[7758728],"length":1,"stats":{"Line":2},"fn_name":null},{"line":47,"address":[7758791],"length":1,"stats":{"Line":1},"fn_name":null},{"line":50,"address":[7758861],"length":1,"stats":{"Line":1},"fn_name":null},{"line":51,"address":[7758947],"length":1,"stats":{"Line":1},"fn_name":null},{"line":54,"address":[7759941,7759245,7759022],"length":1,"stats":{"Line":3},"fn_name":null},{"line":55,"address":[7759511,7759591],"length":1,"stats":{"Line":2},"fn_name":null},{"line":56,"address":[7759806],"length":1,"stats":{"Line":1},"fn_name":null},{"line":57,"address":[7759630],"length":1,"stats":{"Line":1},"fn_name":null},{"line":58,"address":[7759747],"length":1,"stats":{"Line":1},"fn_name":null},{"line":65,"address":[7761752,7762040,7760496],"length":1,"stats":{"Line":2},"fn_name":null},{"line":69,"address":[7760536],"length":1,"stats":{"Line":2},"fn_name":null},{"line":70,"address":[7760599],"length":1,"stats":{"Line":1},"fn_name":null},{"line":73,"address":[7760669],"length":1,"stats":{"Line":1},"fn_name":null},{"line":74,"address":[7760755],"length":1,"stats":{"Line":1},"fn_name":null},{"line":77,"address":[7761779,7760830,7761053],"length":1,"stats":{"Line":3},"fn_name":null},{"line":78,"address":[7761319,7761399],"length":1,"stats":{"Line":2},"fn_name":null},{"line":79,"address":[7761622],"length":1,"stats":{"Line":1},"fn_name":null},{"line":80,"address":[7761438],"length":1,"stats":{"Line":1},"fn_name":null},{"line":82,"address":[7761555],"length":1,"stats":{"Line":1},"fn_name":null},{"line":83,"address":[7761614],"length":1,"stats":{"Line":1},"fn_name":null},{"line":88,"address":[7762336,7762645,7762684],"length":1,"stats":{"Line":2},"fn_name":null},{"line":89,"address":[7762358],"length":1,"stats":{"Line":2},"fn_name":null},{"line":90,"address":[7762452],"length":1,"stats":{"Line":2},"fn_name":null},{"line":91,"address":[7762544],"length":1,"stats":{"Line":2},"fn_name":null},{"line":92,"address":[7762611],"length":1,"stats":{"Line":2},"fn_name":null},{"line":111,"address":[6632607,6635312,6636969,6635205,6637935,6633491,6634313,6636085,6637088,6637861,6632640,6634432,6634387,6635279,6636192,6636159,6633536,6632533,6631760,6637043,6633417],"length":1,"stats":{"Line":8},"fn_name":null},{"line":115,"address":[],"length":0,"stats":{"Line":22},"fn_name":null},{"line":116,"address":[],"length":0,"stats":{"Line":12},"fn_name":null},{"line":117,"address":[],"length":0,"stats":{"Line":22},"fn_name":null},{"line":121,"address":[],"length":0,"stats":{"Line":8},"fn_name":null},{"line":123,"address":[],"length":0,"stats":{"Line":8},"fn_name":null},{"line":125,"address":[],"length":0,"stats":{"Line":8},"fn_name":null},{"line":128,"address":[],"length":0,"stats":{"Line":8},"fn_name":null},{"line":132,"address":[7762736,7762815],"length":1,"stats":{"Line":1},"fn_name":null},{"line":133,"address":[7762857,7762773],"length":1,"stats":{"Line":2},"fn_name":null},{"line":136,"address":[7762880,7762973],"length":1,"stats":{"Line":1},"fn_name":null},{"line":137,"address":[7762917,7763015],"length":1,"stats":{"Line":2},"fn_name":null},{"line":140,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":141,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":142,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":145,"address":[7764333,7763056,7764106],"length":1,"stats":{"Line":3},"fn_name":null},{"line":149,"address":[7763098],"length":1,"stats":{"Line":3},"fn_name":null},{"line":150,"address":[7763269,7763176],"length":1,"stats":{"Line":6},"fn_name":null},{"line":151,"address":[7763796,7763542],"length":1,"stats":{"Line":6},"fn_name":null},{"line":152,"address":[7763618,7763706],"length":1,"stats":{"Line":6},"fn_name":null},{"line":153,"address":[7763716],"length":1,"stats":{"Line":3},"fn_name":null},{"line":156,"address":[7764231],"length":1,"stats":{"Line":3},"fn_name":null},{"line":159,"address":[7766026,7767366,7764368],"length":1,"stats":{"Line":2},"fn_name":null},{"line":161,"address":[7764789,7764435],"length":1,"stats":{"Line":4},"fn_name":null},{"line":162,"address":[7765036],"length":1,"stats":{"Line":2},"fn_name":null},{"line":163,"address":[7765076],"length":1,"stats":{"Line":2},"fn_name":null},{"line":164,"address":[7765116],"length":1,"stats":{"Line":2},"fn_name":null},{"line":165,"address":[7765148],"length":1,"stats":{"Line":2},"fn_name":null},{"line":166,"address":[7765204],"length":1,"stats":{"Line":2},"fn_name":null},{"line":167,"address":[7765237],"length":1,"stats":{"Line":2},"fn_name":null},{"line":168,"address":[7765255],"length":1,"stats":{"Line":2},"fn_name":null},{"line":169,"address":[7765295],"length":1,"stats":{"Line":2},"fn_name":null},{"line":171,"address":[7764515],"length":1,"stats":{"Line":2},"fn_name":null},{"line":172,"address":[7764545],"length":1,"stats":{"Line":2},"fn_name":null},{"line":173,"address":[7764575],"length":1,"stats":{"Line":2},"fn_name":null},{"line":174,"address":[7764605],"length":1,"stats":{"Line":2},"fn_name":null},{"line":175,"address":[7764685],"length":1,"stats":{"Line":2},"fn_name":null},{"line":176,"address":[7764697],"length":1,"stats":{"Line":2},"fn_name":null},{"line":177,"address":[7764749],"length":1,"stats":{"Line":2},"fn_name":null},{"line":178,"address":[7764779],"length":1,"stats":{"Line":2},"fn_name":null},{"line":181,"address":[7765453,7765313],"length":1,"stats":{"Line":4},"fn_name":null},{"line":183,"address":[7768768,7769961,7765445,7768811,7769822],"length":1,"stats":{"Line":6},"fn_name":"{closure#0}"},{"line":185,"address":[7768871,7768934],"length":1,"stats":{"Line":4},"fn_name":null},{"line":187,"address":[7769042,7769100],"length":1,"stats":{"Line":4},"fn_name":null},{"line":188,"address":[7769347],"length":1,"stats":{"Line":2},"fn_name":null},{"line":189,"address":[7769118],"length":1,"stats":{"Line":2},"fn_name":null},{"line":191,"address":[7770000,7770022],"length":1,"stats":{"Line":4},"fn_name":"{closure#0}"},{"line":193,"address":[7769200],"length":1,"stats":{"Line":2},"fn_name":null},{"line":194,"address":[7769296],"length":1,"stats":{"Line":2},"fn_name":null},{"line":200,"address":[7770048,7765523,7770524,7770548],"length":1,"stats":{"Line":3},"fn_name":"{closure#1}"},{"line":201,"address":[7770307,7770070],"length":1,"stats":{"Line":2},"fn_name":null},{"line":202,"address":[7770182,7770086],"length":1,"stats":{"Line":2},"fn_name":null},{"line":204,"address":[7770251],"length":1,"stats":{"Line":1},"fn_name":null},{"line":208,"address":[7765691],"length":1,"stats":{"Line":2},"fn_name":null},{"line":209,"address":[7766415,7765703],"length":1,"stats":{"Line":3},"fn_name":null},{"line":210,"address":[7765729],"length":1,"stats":{"Line":1},"fn_name":null},{"line":211,"address":[7765792,7765865],"length":1,"stats":{"Line":2},"fn_name":null},{"line":212,"address":[7766047],"length":1,"stats":{"Line":1},"fn_name":null},{"line":216,"address":[7765707],"length":1,"stats":{"Line":2},"fn_name":null},{"line":219,"address":[7766522],"length":1,"stats":{"Line":2},"fn_name":null},{"line":221,"address":[7766842],"length":1,"stats":{"Line":2},"fn_name":null}],"covered":91,"coverable":91},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","openapi","router.rs"],"content":"use super::{\n\tbuilder::OpenapiBuilder,\n\thandler::{OpenapiDocHandler, OpenapiSpecHandler},\n\toperation::OperationDescription\n};\nuse crate::{routing::*, EndpointWithSchema, ResourceWithSchema, ResponseSchema};\nuse gotham::{\n\thyper::{Method, StatusCode},\n\tpipeline::PipelineHandleChain,\n\tprelude::*,\n\trouter::builder::{RouterBuilder, ScopeBuilder}\n};\nuse lazy_regex::regex_replace_all;\nuse openapi_type::OpenapiType;\nuse std::{collections::HashMap, panic::RefUnwindSafe};\n\n/// This trait adds the `openapi_spec` and `openapi_doc` method to an OpenAPI-aware router.\npub trait GetOpenapi {\n\t/// Register a GET route to `path` that returns the OpenAPI specification in JSON format.\n\tfn openapi_spec(&mut self, path: &str);\n\n\t/// Register a GET route to `path` that returns the OpenAPI documentation in HTML format.\n\tfn openapi_doc(&mut self, path: &str);\n}\n\n#[derive(Debug)]\npub struct OpenapiRouter<'a, D> {\n\tpub(crate) router: &'a mut D,\n\tpub(crate) scope: Option<&'a str>,\n\tpub(crate) openapi_builder: &'a mut OpenapiBuilder\n}\n\nmacro_rules! implOpenapiRouter {\n\t($implType:ident) => {\n\t\timpl<'a, 'b, C, P> OpenapiRouter<'a, $implType<'b, C, P>>\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tpub fn scope(&mut self, path: &str, callback: F)\n\t\t\twhere\n\t\t\t\tF: FnOnce(&mut OpenapiRouter<'_, ScopeBuilder<'_, C, P>>)\n\t\t\t{\n\t\t\t\tlet mut openapi_builder = self.openapi_builder.clone();\n\t\t\t\tlet new_scope = self\n\t\t\t\t\t.scope\n\t\t\t\t\t.map(|scope| format!(\"{scope}/{path}\").replace(\"//\", \"/\"));\n\t\t\t\tself.router.scope(path, |router| {\n\t\t\t\t\tlet mut router = OpenapiRouter {\n\t\t\t\t\t\trouter,\n\t\t\t\t\t\tscope: Some(new_scope.as_ref().map(String::as_ref).unwrap_or(path)),\n\t\t\t\t\t\topenapi_builder: &mut openapi_builder\n\t\t\t\t\t};\n\t\t\t\t\tcallback(&mut router);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\timpl<'a, 'b, C, P> GetOpenapi for OpenapiRouter<'a, $implType<'b, C, P>>\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn openapi_spec(&mut self, path: &str) {\n\t\t\t\tself.router\n\t\t\t\t\t.get(path)\n\t\t\t\t\t.to_new_handler(OpenapiSpecHandler::new(\n\t\t\t\t\t\tself.openapi_builder.openapi.clone()\n\t\t\t\t\t));\n\t\t\t}\n\n\t\t\tfn openapi_doc(&mut self, path: &str) {\n\t\t\t\tself.router\n\t\t\t\t\t.get(path)\n\t\t\t\t\t.to_new_handler(OpenapiDocHandler::new(self.openapi_builder.openapi.clone()));\n\t\t\t}\n\t\t}\n\n\t\timpl<'a, 'b, C, P> DrawResourcesWithSchema for OpenapiRouter<'a, $implType<'b, C, P>>\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn resource(&mut self, mut path: &str) {\n\t\t\t\tif path.starts_with('/') {\n\t\t\t\t\tpath = &path[1..];\n\t\t\t\t}\n\t\t\t\tR::setup((self, path));\n\t\t\t}\n\t\t}\n\n\t\timpl<'a, 'b, C, P> DrawResourceRoutesWithSchema\n\t\t\tfor (&mut OpenapiRouter<'a, $implType<'b, C, P>>, &str)\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn endpoint(&mut self) {\n\t\t\t\tlet mut responses: HashMap = HashMap::new();\n\t\t\t\tfor code in E::Output::status_codes() {\n\t\t\t\t\tresponses.insert(\n\t\t\t\t\t\tcode,\n\t\t\t\t\t\t(self.0).openapi_builder.add_schema(E::Output::schema(code))\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlet mut path = format!(\"{}/{}\", self.0.scope.unwrap_or_default(), self.1);\n\t\t\t\tlet mut descr = OperationDescription::new::(responses, &path);\n\t\t\t\tif E::has_placeholders() {\n\t\t\t\t\tdescr.set_path_params(E::Placeholders::schema());\n\t\t\t\t}\n\t\t\t\tif E::needs_params() {\n\t\t\t\t\tdescr.set_query_params(E::Params::schema());\n\t\t\t\t}\n\t\t\t\tif E::needs_body() {\n\t\t\t\t\tlet body_schema = (self.0).openapi_builder.add_schema(E::Body::schema());\n\t\t\t\t\tdescr.set_body::(body_schema);\n\t\t\t\t}\n\n\t\t\t\tlet uri: &str = &E::uri();\n\t\t\t\tlet uri =\n\t\t\t\t\tregex_replace_all!(r#\"(^|/):([^/]+)(/|$)\"#, uri, |_, prefix, name, suffix| {\n\t\t\t\t\t\tformat!(\"{prefix}{{{name}}}{suffix}\")\n\t\t\t\t\t});\n\t\t\t\tif !uri.is_empty() {\n\t\t\t\t\tpath = format!(\"{path}/{uri}\");\n\t\t\t\t}\n\n\t\t\t\tlet op = descr.into_operation();\n\t\t\t\tlet mut item = (self.0).openapi_builder.remove_path(&path);\n\t\t\t\tmatch E::http_method() {\n\t\t\t\t\tMethod::GET => item.get = Some(op),\n\t\t\t\t\tMethod::PUT => item.put = Some(op),\n\t\t\t\t\tMethod::POST => item.post = Some(op),\n\t\t\t\t\tMethod::DELETE => item.delete = Some(op),\n\t\t\t\t\tMethod::OPTIONS => item.options = Some(op),\n\t\t\t\t\tMethod::HEAD => item.head = Some(op),\n\t\t\t\t\tMethod::PATCH => item.patch = Some(op),\n\t\t\t\t\tMethod::TRACE => item.trace = Some(op),\n\t\t\t\t\tmethod => {\n\t\t\t\t\t\twarn!(\"Ignoring unsupported method '{method}' in OpenAPI Specification\")\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\t(self.0).openapi_builder.add_path(path, item);\n\n\t\t\t\t(&mut *(self.0).router, self.1).endpoint::()\n\t\t\t}\n\t\t}\n\t};\n}\n\nimplOpenapiRouter!(RouterBuilder);\nimplOpenapiRouter!(ScopeBuilder);\n","traces":[{"line":40,"address":[6854480,6854813,6854778,6855552,6855850,6855885],"length":1,"stats":{"Line":2},"fn_name":null},{"line":44,"address":[6855653,6854507,6855579,6854581],"length":1,"stats":{"Line":4},"fn_name":null},{"line":45,"address":[6855658,6854586,6854604,6855676],"length":1,"stats":{"Line":4},"fn_name":null},{"line":47,"address":[6854977,6854832,6856192,6854599,6856284,6854924,6856337,6855671],"length":1,"stats":{"Line":5},"fn_name":"{closure#0}<(), (), openapi_supports_scope::openapi_supports_scope::{closure#0}::{closure#0}::{closure#0}::{closure_env#0}>"},{"line":48,"address":[6854664,6856161,6855521,6855904,6855264,6855736],"length":1,"stats":{"Line":4},"fn_name":"{closure#1}<(), (), openapi_supports_scope::openapi_supports_scope::{closure#0}::{closure#0}::{closure#0}::{closure_env#0}>"},{"line":49,"address":[6856096,6855289,6855456,6855929],"length":1,"stats":{"Line":4},"fn_name":null},{"line":51,"address":[6856001,6855299,6855939,6855361],"length":1,"stats":{"Line":4},"fn_name":null},{"line":52,"address":[6855452,6856092],"length":1,"stats":{"Line":2},"fn_name":null},{"line":54,"address":[6856126,6855486],"length":1,"stats":{"Line":2},"fn_name":null},{"line":64,"address":[6405644,6405668,6405488],"length":1,"stats":{"Line":2},"fn_name":"openapi_spec<(borrow_bag::handle::Handle, ())>, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::auth::AuthMiddleware, ())>, ())>"},{"line":65,"address":[6405518,6405617],"length":1,"stats":{"Line":4},"fn_name":null},{"line":67,"address":[6405596],"length":1,"stats":{"Line":2},"fn_name":null},{"line":68,"address":[6405546],"length":1,"stats":{"Line":2},"fn_name":null},{"line":84,"address":[6406112,6405824,6405968,6405680],"length":1,"stats":{"Line":6},"fn_name":"resource<(borrow_bag::handle::Handle, ())>, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::auth::AuthMiddleware, ())>, ()), openapi_specification::CustomResource>"},{"line":85,"address":[6406253,6405848,6405821,6406136,6406109,6405992,6405704,6405965],"length":1,"stats":{"Line":7},"fn_name":null},{"line":86,"address":[6405774,6406062,6406206,6405918],"length":1,"stats":{"Line":1},"fn_name":null},{"line":88,"address":[6405734,6405878,6406022,6406166],"length":1,"stats":{"Line":6},"fn_name":null},{"line":98,"address":[6413216,6431312,6438176,6443745,6443792,6412549,6406984,6425704,6437509,6444416,6418832,6406352,6431936,6418785,6425072,6412592,6425033,6431277,6419464,6437552,6449985],"length":1,"stats":{"Line":9},"fn_name":"endpoint<(borrow_bag::handle::Handle, ())>, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::auth::AuthMiddleware, ())>, ()), openapi_specification::set_image___gotham_restful_endpoint>"},{"line":99,"address":[6425125,6406405,6437605,6443845,6418885,6412645,6431365],"length":1,"stats":{"Line":9},"fn_name":null},{"line":100,"address":[6406622,6412858,6425342,6431514,6406546,6419102,6431578,6437754,6437818,6443994,6444058,6412794,6425266,6419026],"length":1,"stats":{"Line":18},"fn_name":null},{"line":101,"address":[6425648,6438120,6444360,6406928,6413160,6419408,6431880],"length":1,"stats":{"Line":9},"fn_name":null},{"line":103,"address":[6413084,6444284,6431804,6438044,6406848,6425568,6419328],"length":1,"stats":{"Line":9},"fn_name":null},{"line":106,"address":[6438380,6444620,6444727,6432140,6425723,6419483,6425908,6413235,6407003,6438195,6419775,6431955,6407295,6419668,6413527,6444435,6432247,6438487,6407188,6426015,6413420],"length":1,"stats":{"Line":27},"fn_name":null},{"line":107,"address":[6420134,6438621,6426149,6444861,6413661,6432606,6419909,6438846,6445086,6407429,6407654,6426374,6432381,6413886],"length":1,"stats":{"Line":9},"fn_name":null},{"line":108,"address":[6432670,6407621,6407718,6438813,6420198,6426438,6413950,6432573,6420101,6438910,6445053,6445150,6413853,6426341],"length":1,"stats":{"Line":18},"fn_name":null},{"line":109,"address":[6413978,6426458,6420226,6407746,6432690,6438938,6445170],"length":1,"stats":{"Line":4},"fn_name":null},{"line":111,"address":[6432745,6438981,6407789,6407724,6420269,6438916,6445156,6445225,6426444,6414021,6420204,6426513,6413956,6432676],"length":1,"stats":{"Line":18},"fn_name":null},{"line":112,"address":[6426533,6432765,6420289,6445253,6407809,6414041,6439001],"length":1,"stats":{"Line":1},"fn_name":null},{"line":114,"address":[6414096,6432751,6407795,6420275,6432820,6438987,6439056,6445231,6407864,6445296,6426519,6420344,6414027,6426588],"length":1,"stats":{"Line":18},"fn_name":null},{"line":115,"address":[6407896,6414128,6426620,6432852,6445328,6439088,6420376],"length":1,"stats":{"Line":2},"fn_name":null},{"line":116,"address":[6426725,6432957,6407997,6439193,6445433,6414233,6420481],"length":1,"stats":{"Line":2},"fn_name":null},{"line":119,"address":[6420606,6414263,6408122,6420511,6426850,6426755,6432834,6433082,6445310,6414358,6407878,6426602,6420358,6439070,6439318,6408027,6445463,6445558,6439223,6432987,6414110],"length":1,"stats":{"Line":27},"fn_name":null},{"line":121,"address":[8691885,8691785,8691723,8691465,8691760,8692043,8691565,8691440],"length":1,"stats":{"Line":62},"fn_name":"{closure#0}"},{"line":122,"address":[6453747,6455155,6454195,6454707,6455651,6456179,6456595],"length":1,"stats":{"Line":4},"fn_name":null},{"line":124,"address":[6427658,6433281,6439517,6414557,6408930,6420805,6426954,6433186,6427049,6420710,6445757,6445662,6408321,6414462,6433890,6439422,6440126,6446366,6415166,6421414,6408226],"length":1,"stats":{"Line":23},"fn_name":null},{"line":125,"address":[6408646,6414882,6427374,6439842,6446082,6421130,6433606],"length":1,"stats":{"Line":5},"fn_name":null},{"line":128,"address":[6420832,6408943,6439544,6408348,6421427,6415179,6427076,6440139,6414584,6433903,6427671,6445784,6446379,6433308],"length":1,"stats":{"Line":18},"fn_name":null},{"line":129,"address":[6427790,6446387,6427679,6408951,6446498,6434022,6421546,6440147,6409062,6415298,6421435,6433911,6415187,6440258],"length":1,"stats":{"Line":18},"fn_name":null},{"line":130,"address":[6434169,6427937,6421645,6440357,6434121,6446597,6446645,6421693,6409209,6415445,6440405,6409161,6415397,6427889],"length":1,"stats":{"Line":18},"fn_name":null},{"line":131,"address":[6430107,6434429,6440665,6448815,6436339,6442575,6446905,6417615,6415705,6409469,6411379,6421953,6423863,6428197],"length":1,"stats":{"Line":7},"fn_name":null},{"line":132,"address":[6417387,6434665,6436111,6415941,6448587,6423635,6409705,6422189,6429879,6440901,6442347,6447141,6411151,6428433],"length":1,"stats":{"Line":1},"fn_name":null},{"line":133,"address":[6436225,6440783,6422071,6447023,6428315,6409587,6423749,6417501,6411265,6415823,6434547,6442461,6448701,6429993],"length":1,"stats":{"Line":0},"fn_name":null},{"line":134,"address":[6447259,6417273,6409823,6416059,6422307,6411037,6423521,6428551,6429765,6441019,6435997,6434783,6442233,6448473],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[6428079,6440547,6442675,6411479,6436439,6448915,6423963,6434311,6421835,6446787,6415587,6417715,6430207,6409351],"length":1,"stats":{"Line":0},"fn_name":null},{"line":136,"address":[6447377,6416177,6448359,6423407,6442119,6417159,6422425,6410923,6429651,6434901,6435883,6409941,6441137,6428669],"length":1,"stats":{"Line":0},"fn_name":null},{"line":137,"address":[6410141,6416377,6428869,6422625,6429429,6416937,6435101,6441337,6410701,6423185,6435661,6447577,6448137,6441897],"length":1,"stats":{"Line":1},"fn_name":null},{"line":138,"address":[6422525,6410041,6441237,6435001,6448245,6428769,6410809,6416277,6423293,6429537,6435769,6442005,6447477,6417045],"length":1,"stats":{"Line":0},"fn_name":null},{"line":139,"address":[6440459,6421747,6427991,6434223,6409263,6446699,6415499],"length":1,"stats":{"Line":0},"fn_name":null},{"line":140,"address":[6447953,6429245,6416586,6422834,6435252,6428031,6441546,6447786,6423001,6416528,6410292,6410517,6421787,6429020,6410350,6434263,6435477,6440499,6435310,6415539,6422776,6409303,6441488,6429078,6441713,6416753,6446739,6447728],"length":1,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[6411631,6424115,6417867,6442827,6449067,6430359,6436591],"length":1,"stats":{"Line":9},"fn_name":null},{"line":145,"address":[6436805,6443041,6449281,6424329,6411845,6418081,6430573],"length":1,"stats":{"Line":9},"fn_name":null}],"covered":44,"coverable":51},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","auth_result.rs"],"content":"use crate::{IntoResponseError, Response};\nuse gotham::{hyper::StatusCode, mime::TEXT_PLAIN_UTF_8};\nuse gotham_restful_derive::ResourceError;\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiSchema, OpenapiType};\n\n/// This is an error type that always yields a _403 Forbidden_ response. This type\n/// is best used in combination with [`AuthSuccess`] or [`AuthResult`].\n#[derive(Clone, Debug)]\npub struct AuthError(String);\n\nimpl AuthError {\n\tpub fn new>(msg: T) -> Self {\n\t\tSelf(msg.into())\n\t}\n}\n\nimpl IntoResponseError for AuthError {\n\t// TODO why does this need to be serde_json::Error ?!?\n\ttype Err = serde_json::Error;\n\n\tfn into_response_error(self) -> Result {\n\t\tOk(Response::new(\n\t\t\tStatusCode::FORBIDDEN,\n\t\t\tself.0,\n\t\t\tSome(TEXT_PLAIN_UTF_8)\n\t\t))\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\tfn status_codes() -> Vec {\n\t\tvec![StatusCode::FORBIDDEN]\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::FORBIDDEN);\n\t\t as OpenapiType>::schema()\n\t}\n}\n\n/// This return type can be used to wrap any type implementing [IntoResponse](crate::IntoResponse)\n/// that can only be returned if the client is authenticated. Otherwise, an empty _403 Forbidden_\n/// response will be issued.\n///\n/// Use can look something like this (assuming the `auth` feature is enabled):\n///\n/// ```rust\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # #[cfg(feature = \"auth\")]\n/// # mod auth_feature_enabled {\n/// # use gotham::state::State;\n/// # use gotham_restful::*;\n/// # use serde::Deserialize;\n/// #\n/// # #[derive(Resource)]\n/// # #[resource(read_all)]\n/// # struct MyResource;\n/// #\n/// # #[derive(Clone, Deserialize)]\n/// # struct MyAuthData { exp : u64 }\n/// #\n/// #[read_all]\n/// fn read_all(auth: AuthStatus) -> AuthSuccess {\n/// \tlet auth_data = auth.ok()?;\n/// \t// do something\n/// \tOk(NoContent::default())\n/// }\n/// # }\n/// ```\npub type AuthSuccess = Result;\n\n/// This is an error type that either yields a _403 Forbidden_ response if produced\n/// from an authentication error, or delegates to another error type. This type is\n/// best used with [`AuthResult`].\n#[derive(Debug, Clone, ResourceError)]\npub enum AuthErrorOrOther {\n\tForbidden(#[from] AuthError),\n\n\t#[status(INTERNAL_SERVER_ERROR)]\n\t#[display(\"{0}\")]\n\tOther(E)\n}\n\nmod private {\n\tuse gotham::handler::HandlerError;\n\tpub trait Sealed {}\n\timpl> Sealed for E {}\n}\n\nimpl From for AuthErrorOrOther\nwhere\n\t// TODO https://github.com/msrd0/gotham_restful/issues/20\n\tF: private::Sealed + Into\n{\n\tfn from(err: F) -> Self {\n\t\tSelf::Other(err.into())\n\t}\n}\n\n/// This return type can be used to wrap any type implementing [IntoResponse](crate::IntoResponse)\n/// that can only be returned if the client is authenticated. Otherwise, an empty _403 Forbidden_\n/// response will be issued.\n///\n/// Use can look something like this (assuming the `auth` feature is enabled):\n///\n/// ```\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # #[cfg(feature = \"auth\")]\n/// # mod auth_feature_enabled {\n/// # use gotham::state::State;\n/// # use gotham_restful::*;\n/// # use serde::Deserialize;\n/// # use std::io;\n/// #\n/// # #[derive(Resource)]\n/// # #[resource(read_all)]\n/// # struct MyResource;\n/// #\n/// # #[derive(Clone, Deserialize)]\n/// # struct MyAuthData { exp : u64 }\n/// #\n/// #[read_all]\n/// fn read_all(auth: AuthStatus) -> AuthResult {\n/// \tlet auth_data = auth.ok()?;\n/// \t// do something\n/// \tOk(NoContent::default().into())\n/// }\n/// # }\n/// ```\npub type AuthResult = Result>;\n","traces":[{"line":13,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":14,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":22,"address":[6427760],"length":1,"stats":{"Line":0},"fn_name":"into_response_error"},{"line":23,"address":[7829799],"length":1,"stats":{"Line":0},"fn_name":null},{"line":25,"address":[6427777],"length":1,"stats":{"Line":0},"fn_name":null},{"line":26,"address":[8021604],"length":1,"stats":{"Line":0},"fn_name":null},{"line":31,"address":[7829872,7829983],"length":1,"stats":{"Line":1},"fn_name":"status_codes"},{"line":32,"address":[8021816,8021741],"length":1,"stats":{"Line":1},"fn_name":null},{"line":36,"address":[7830000],"length":1,"stats":{"Line":1},"fn_name":"schema"},{"line":38,"address":[7830096],"length":1,"stats":{"Line":1},"fn_name":null},{"line":96,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":4,"coverable":12},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","mod.rs"],"content":"use futures_util::future::{self, BoxFuture, FutureExt};\nuse gotham::{\n\thandler::HandlerError,\n\thyper::{\n\t\theader::{HeaderMap, HeaderName, HeaderValue},\n\t\tBody, StatusCode\n\t},\n\tmime::{Mime, APPLICATION_JSON, STAR_STAR}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiSchema, OpenapiType};\nuse serde::Serialize;\n#[cfg(feature = \"errorlog\")]\nuse std::fmt::Display;\nuse std::{convert::Infallible, fmt::Debug, future::Future, pin::Pin};\n\nmod auth_result;\n#[allow(unreachable_pub)]\npub use auth_result::{AuthError, AuthErrorOrOther, AuthResult, AuthSuccess};\n\nmod no_content;\n#[allow(unreachable_pub)]\npub use no_content::NoContent;\n\nmod raw;\n#[allow(unreachable_pub)]\npub use raw::Raw;\n\nmod redirect;\n#[allow(unreachable_pub)]\npub use redirect::Redirect;\n\nmod result;\n#[allow(unreachable_pub)]\npub use result::IntoResponseError;\n\nmod success;\n#[allow(unreachable_pub)]\npub use success::Success;\n\npub(crate) trait OrAllTypes {\n\tfn or_all_types(self) -> Vec;\n}\n\nimpl OrAllTypes for Option> {\n\tfn or_all_types(self) -> Vec {\n\t\tself.unwrap_or_else(|| vec![STAR_STAR])\n\t}\n}\n\n/// A response, used to create the final gotham response from.\n///\n/// This type is not meant to be used as the return type of endpoint handlers. While it can be\n/// freely used without the `openapi` feature, it is more complicated to use when you enable it,\n/// since this type does not store any schema information. You can attach schema information\n/// like so:\n///\n/// ```rust\n/// # #[cfg(feature = \"openapi\")] mod example {\n/// # use gotham::hyper::StatusCode;\n/// # use gotham_restful::*;\n/// # use openapi_type::*;\n/// fn schema(code: StatusCode) -> OpenapiSchema {\n/// \tassert_eq!(code, StatusCode::ACCEPTED);\n/// \t<()>::schema()\n/// }\n///\n/// fn status_codes() -> Vec {\n/// \tvec![StatusCode::ACCEPTED]\n/// }\n///\n/// #[create(schema = \"schema\", status_codes = \"status_codes\")]\n/// fn create(body: Raw>) {}\n/// # }\n/// ```\n#[derive(Debug)]\npub struct Response {\n\tpub(crate) status: StatusCode,\n\tpub(crate) body: Body,\n\tpub(crate) mime: Option,\n\tpub(crate) headers: HeaderMap\n}\n\nimpl Response {\n\t/// Create a new [Response] from raw data.\n\t#[must_use = \"Creating a response is pointless if you don't use it\"]\n\tpub fn new>(status: StatusCode, body: B, mime: Option) -> Self {\n\t\tSelf {\n\t\t\tstatus,\n\t\t\tbody: body.into(),\n\t\t\tmime,\n\t\t\theaders: Default::default()\n\t\t}\n\t}\n\n\t/// Create a [Response] with mime type json from already serialized data.\n\t#[must_use = \"Creating a response is pointless if you don't use it\"]\n\tpub fn json>(status: StatusCode, body: B) -> Self {\n\t\tSelf {\n\t\t\tstatus,\n\t\t\tbody: body.into(),\n\t\t\tmime: Some(APPLICATION_JSON),\n\t\t\theaders: Default::default()\n\t\t}\n\t}\n\n\t/// Create a _204 No Content_ [Response].\n\t#[must_use = \"Creating a response is pointless if you don't use it\"]\n\tpub fn no_content() -> Self {\n\t\tSelf {\n\t\t\tstatus: StatusCode::NO_CONTENT,\n\t\t\tbody: Body::empty(),\n\t\t\tmime: None,\n\t\t\theaders: Default::default()\n\t\t}\n\t}\n\n\t/// Create an empty _403 Forbidden_ [Response].\n\t#[must_use = \"Creating a response is pointless if you don't use it\"]\n\tpub fn forbidden() -> Self {\n\t\tSelf {\n\t\t\tstatus: StatusCode::FORBIDDEN,\n\t\t\tbody: Body::empty(),\n\t\t\tmime: None,\n\t\t\theaders: Default::default()\n\t\t}\n\t}\n\n\t/// Return the status code of this [Response].\n\tpub fn status(&self) -> StatusCode {\n\t\tself.status\n\t}\n\n\t/// Return the mime type of this [Response].\n\tpub fn mime(&self) -> Option<&Mime> {\n\t\tself.mime.as_ref()\n\t}\n\n\t/// Add an HTTP header to the [Response].\n\tpub fn header(&mut self, name: HeaderName, value: HeaderValue) {\n\t\tself.headers.insert(name, value);\n\t}\n\n\tpub(crate) fn with_headers(mut self, headers: HeaderMap) -> Self {\n\t\tself.headers = headers;\n\t\tself\n\t}\n\n\t#[cfg(test)]\n\tpub(crate) fn full_body(\n\t\tmut self\n\t) -> Result, ::Error> {\n\t\tuse futures_executor::block_on;\n\t\tuse gotham::hyper::body::to_bytes;\n\n\t\tlet bytes: &[u8] = &block_on(to_bytes(&mut self.body))?;\n\t\tOk(bytes.to_vec())\n\t}\n}\n\nimpl IntoResponse for Response {\n\ttype Err = Infallible;\n\n\tfn into_response(self) -> BoxFuture<'static, Result> {\n\t\tfuture::ok(self).boxed()\n\t}\n}\n\n/// This trait needs to be implemented by every type returned from an endpoint to\n/// to provide the response.\npub trait IntoResponse {\n\ttype Err: Into + Send + Sync + 'static;\n\n\t/// Turn this into a response that can be returned to the browser. This api will likely\n\t/// change in the future.\n\tfn into_response(self) -> BoxFuture<'static, Result>;\n\n\t/// Return a list of supported mime types.\n\tfn accepted_types() -> Option> {\n\t\tNone\n\t}\n}\n\n/// Additional details for [IntoResponse] to be used with an OpenAPI-aware router.\n#[cfg(feature = \"openapi\")]\npub trait ResponseSchema {\n\t/// All status codes returned by this response. Returns `[StatusCode::OK]` by default.\n\tfn status_codes() -> Vec {\n\t\tvec![StatusCode::OK]\n\t}\n\n\t/// Return the schema of the response for the given status code. The code may\n\t/// only be one that was previously returned by [Self::status_codes]. The\n\t/// implementation should panic if that is not the case.\n\tfn schema(code: StatusCode) -> OpenapiSchema;\n}\n\n#[cfg(feature = \"openapi\")]\nmod private {\n\tpub trait Sealed {}\n}\n\n/// A trait provided to convert a resource's result to json, and provide an OpenAPI schema to the\n/// router. This trait is implemented for all types that implement [IntoResponse] and\n/// [ResponseSchema].\n#[cfg(feature = \"openapi\")]\npub trait IntoResponseWithSchema: IntoResponse + ResponseSchema + private::Sealed {}\n\n#[cfg(feature = \"openapi\")]\nimpl private::Sealed for R {}\n\n#[cfg(feature = \"openapi\")]\nimpl IntoResponseWithSchema for R {}\n\n/// The default json returned on an 500 Internal Server Error.\n#[derive(Debug, Serialize)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\npub(crate) struct ResourceError {\n\t/// This is always `true` and can be used to detect an error response without looking at the\n\t/// HTTP status code.\n\terror: bool,\n\t/// The error message.\n\tmessage: String\n}\n\nimpl From for ResourceError {\n\tfn from(message: T) -> Self {\n\t\tSelf {\n\t\t\terror: true,\n\t\t\tmessage: message.to_string()\n\t\t}\n\t}\n}\n\n#[cfg(feature = \"errorlog\")]\nfn errorlog(e: E) {\n\terror!(\"The handler encountered an error: {e}\");\n}\n\n#[cfg(not(feature = \"errorlog\"))]\nfn errorlog(_e: E) {}\n\nfn handle_error(e: E) -> Pin> + Send>>\nwhere\n\tE: Debug + IntoResponseError\n{\n\tlet msg = format!(\"{e:?}\");\n\tlet res = e.into_response_error();\n\tmatch &res {\n\t\tOk(res) if res.status.is_server_error() => errorlog(msg),\n\t\tErr(err) => {\n\t\t\terrorlog(msg);\n\t\t\terrorlog(format!(\"{err:?}\"));\n\t\t},\n\t\t_ => {}\n\t};\n\tfuture::ready(res).boxed()\n}\n\nimpl IntoResponse for Pin + Send>>\nwhere\n\tRes: IntoResponse + 'static\n{\n\ttype Err = Res::Err;\n\n\tfn into_response(self) -> Pin> + Send>> {\n\t\tself.then(IntoResponse::into_response).boxed()\n\t}\n\n\tfn accepted_types() -> Option> {\n\t\tRes::accepted_types()\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Pin + Send>>\nwhere\n\tRes: ResponseSchema\n{\n\tfn status_codes() -> Vec {\n\t\tRes::status_codes()\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tRes::schema(code)\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse futures_executor::block_on;\n\tuse thiserror::Error;\n\n\t#[derive(Debug, Default, Deserialize, Serialize)]\n\t#[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n\tstruct Msg {\n\t\tmsg: String\n\t}\n\n\t#[derive(Debug, Default, Error)]\n\t#[error(\"An Error\")]\n\tstruct MsgError;\n\n\t#[test]\n\tfn result_from_future() {\n\t\tlet nc = NoContent::default();\n\t\tlet res = block_on(nc.into_response()).unwrap();\n\n\t\tlet fut_nc = async move { NoContent::default() }.boxed();\n\t\tlet fut_res = block_on(fut_nc.into_response()).unwrap();\n\n\t\tassert_eq!(res.status, fut_res.status);\n\t\tassert_eq!(res.mime, fut_res.mime);\n\t\tassert_eq!(res.full_body().unwrap(), fut_res.full_body().unwrap());\n\t}\n}\n","traces":[{"line":46,"address":[7804528],"length":1,"stats":{"Line":3},"fn_name":"or_all_types"},{"line":47,"address":[7804536],"length":1,"stats":{"Line":9},"fn_name":null},{"line":87,"address":[6304227,6303840,6304197],"length":1,"stats":{"Line":8},"fn_name":null},{"line":90,"address":[6303903],"length":1,"stats":{"Line":9},"fn_name":null},{"line":92,"address":[6304038],"length":1,"stats":{"Line":9},"fn_name":null},{"line":98,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":101,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":102,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":103,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":109,"address":[7615328,7615547,7615520],"length":1,"stats":{"Line":2},"fn_name":null},{"line":112,"address":[7615350],"length":1,"stats":{"Line":2},"fn_name":null},{"line":114,"address":[7615364],"length":1,"stats":{"Line":2},"fn_name":null},{"line":120,"address":[7615787,7615760,7615568],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[7615590],"length":1,"stats":{"Line":0},"fn_name":null},{"line":125,"address":[7615604],"length":1,"stats":{"Line":0},"fn_name":null},{"line":130,"address":[7615808],"length":1,"stats":{"Line":1},"fn_name":null},{"line":131,"address":[7615813],"length":1,"stats":{"Line":1},"fn_name":null},{"line":135,"address":[7615824],"length":1,"stats":{"Line":1},"fn_name":null},{"line":136,"address":[7615829],"length":1,"stats":{"Line":1},"fn_name":null},{"line":140,"address":[7615856],"length":1,"stats":{"Line":2},"fn_name":null},{"line":141,"address":[7615874],"length":1,"stats":{"Line":2},"fn_name":null},{"line":144,"address":[7616129,7615920],"length":1,"stats":{"Line":2},"fn_name":null},{"line":145,"address":[7616082,7615952],"length":1,"stats":{"Line":4},"fn_name":null},{"line":146,"address":[7616109],"length":1,"stats":{"Line":2},"fn_name":null},{"line":150,"address":[6158400,6158822,6158796],"length":1,"stats":{"Line":1},"fn_name":null},{"line":156,"address":[6158430,6158717,6158487],"length":1,"stats":{"Line":3},"fn_name":null},{"line":157,"address":[6158733],"length":1,"stats":{"Line":2},"fn_name":null},{"line":164,"address":[7616160],"length":1,"stats":{"Line":0},"fn_name":"into_response"},{"line":165,"address":[7616170],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[6827296],"length":1,"stats":{"Line":7},"fn_name":"accepted_types>>"},{"line":180,"address":[6827299],"length":1,"stats":{"Line":7},"fn_name":null},{"line":188,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":189,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":227,"address":[6354960,6354991],"length":1,"stats":{"Line":2},"fn_name":"from"},{"line":230,"address":[6354984],"length":1,"stats":{"Line":2},"fn_name":null},{"line":236,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":237,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":243,"address":[6945376,6946383,6946454],"length":1,"stats":{"Line":1},"fn_name":"handle_error"},{"line":247,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":248,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":249,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":250,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":251,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":252,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":253,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":255,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":257,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":266,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":267,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":270,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":271,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":280,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":281,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":284,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":285,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":40,"coverable":55},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","no_content.rs"],"content":"use super::{handle_error, IntoResponse};\n#[cfg(feature = \"openapi\")]\nuse crate::ResponseSchema;\nuse crate::{IntoResponseError, Response};\nuse futures_util::{future, future::FutureExt};\n#[cfg(feature = \"openapi\")]\nuse gotham::hyper::StatusCode;\nuse gotham::{\n\thyper::header::{HeaderMap, HeaderValue, IntoHeaderName},\n\tmime::Mime\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiSchema, OpenapiType};\nuse std::{fmt::Debug, future::Future, pin::Pin};\n\n/// This is the return type of a resource that doesn't actually return something. It will result\n/// in a _204 No Content_ answer by default. You don't need to use this type directly if using\n/// the function attributes:\n///\n/// ```\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # mod doc_tests_are_broken {\n/// # use gotham::state::State;\n/// # use gotham_restful::*;\n/// #\n/// # #[derive(Resource)]\n/// # #[resource(read_all)]\n/// # struct MyResource;\n/// #\n/// #[read_all]\n/// fn read_all() {\n/// \t// do something\n/// }\n/// # }\n/// ```\n#[derive(Clone, Debug, Default)]\npub struct NoContent {\n\theaders: HeaderMap\n}\n\nimpl From<()> for NoContent {\n\tfn from(_: ()) -> Self {\n\t\tSelf::default()\n\t}\n}\n\nimpl NoContent {\n\t/// Set a custom HTTP header. If a header with this name was set before, its value is being updated.\n\tpub fn header(&mut self, name: K, value: HeaderValue) {\n\t\tself.headers.insert(name, value);\n\t}\n\n\t/// Allow manipulating HTTP headers.\n\tpub fn headers_mut(&mut self) -> &mut HeaderMap {\n\t\t&mut self.headers\n\t}\n}\n\nimpl IntoResponse for NoContent {\n\t// TODO this shouldn't be a serde_json::Error\n\ttype Err = serde_json::Error; // just for easier handling of `Result`\n\n\t/// This will always be a _204 No Content_ together with an empty string.\n\tfn into_response(self) -> Pin> + Send>> {\n\t\tfuture::ok(Response::no_content().with_headers(self.headers)).boxed()\n\t}\n\n\tfn accepted_types() -> Option> {\n\t\tSome(Vec::new())\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for NoContent {\n\tfn status_codes() -> Vec {\n\t\tvec![StatusCode::NO_CONTENT]\n\t}\n\n\t/// Returns the schema of the `()` type.\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::NO_CONTENT);\n\t\t<()>::schema()\n\t}\n}\n\nimpl IntoResponse for Result\nwhere\n\tE: Debug + IntoResponseError\n{\n\ttype Err = serde_json::Error;\n\n\tfn into_response(\n\t\tself\n\t) -> Pin> + Send>> {\n\t\tmatch self {\n\t\t\tOk(nc) => nc.into_response(),\n\t\t\tErr(e) => handle_error(e)\n\t\t}\n\t}\n\n\tfn accepted_types() -> Option> {\n\t\tNoContent::accepted_types()\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Result\nwhere\n\tE: Debug + IntoResponseError\n{\n\tfn status_codes() -> Vec {\n\t\tlet mut status_codes = E::status_codes();\n\t\tstatus_codes.push(StatusCode::NO_CONTENT);\n\t\tstatus_codes\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tmatch code {\n\t\t\tStatusCode::NO_CONTENT => ::schema(StatusCode::NO_CONTENT),\n\t\t\tcode => E::schema(code)\n\t\t}\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse futures_executor::block_on;\n\tuse gotham::hyper::{header::ACCESS_CONTROL_ALLOW_ORIGIN, StatusCode};\n\tuse thiserror::Error;\n\n\t#[derive(Debug, Default, Error)]\n\t#[error(\"An Error\")]\n\tstruct MsgError;\n\n\t#[test]\n\tfn no_content_has_empty_response() {\n\t\tlet no_content = NoContent::default();\n\t\tlet res = block_on(no_content.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::NO_CONTENT);\n\t\tassert_eq!(res.mime, None);\n\t\tassert_eq!(res.full_body().unwrap(), &[] as &[u8]);\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(NoContent::status_codes(), vec![StatusCode::NO_CONTENT]);\n\t}\n\n\t#[test]\n\tfn no_content_result() {\n\t\tlet no_content: Result = Ok(NoContent::default());\n\t\tlet res = block_on(no_content.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::NO_CONTENT);\n\t\tassert_eq!(res.mime, None);\n\t\tassert_eq!(res.full_body().unwrap(), &[] as &[u8]);\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(>::status_codes(), vec![\n\t\t\tStatusCode::INTERNAL_SERVER_ERROR,\n\t\t\tStatusCode::NO_CONTENT\n\t\t]);\n\t}\n\n\t#[test]\n\tfn no_content_custom_headers() {\n\t\tlet mut no_content = NoContent::default();\n\t\tno_content.header(ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static(\"*\"));\n\t\tlet res = block_on(no_content.into_response()).expect(\"didn't expect error response\");\n\t\tlet cors = res.headers.get(ACCESS_CONTROL_ALLOW_ORIGIN);\n\t\tassert_eq!(cors.map(|value| value.to_str().unwrap()), Some(\"*\"));\n\t}\n}\n","traces":[{"line":42,"address":[6326416],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":43,"address":[8453964],"length":1,"stats":{"Line":0},"fn_name":null},{"line":49,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":50,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":54,"address":[8453984],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[7728400,7728673,7728704],"length":1,"stats":{"Line":2},"fn_name":"into_response"},{"line":65,"address":[6326557,6326476],"length":1,"stats":{"Line":4},"fn_name":null},{"line":68,"address":[8454320],"length":1,"stats":{"Line":4},"fn_name":"accepted_types"},{"line":69,"address":[6326797],"length":1,"stats":{"Line":4},"fn_name":null},{"line":75,"address":[7728784,7728895],"length":1,"stats":{"Line":2},"fn_name":"status_codes"},{"line":76,"address":[6326861,6326936],"length":1,"stats":{"Line":2},"fn_name":null},{"line":80,"address":[6326976],"length":1,"stats":{"Line":2},"fn_name":"schema"},{"line":82,"address":[6327072],"length":1,"stats":{"Line":2},"fn_name":null},{"line":92,"address":[6153104],"length":1,"stats":{"Line":1},"fn_name":"into_response"},{"line":95,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":96,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":97,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":102,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":111,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":112,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":113,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":114,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":117,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":119,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":120,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":17,"coverable":27},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","raw.rs"],"content":"use super::{handle_error, IntoResponse, IntoResponseError};\nuse crate::{types::ResourceType, FromBody, RequestBody, Response};\n#[cfg(feature = \"openapi\")]\nuse crate::{IntoResponseWithSchema, ResponseSchema};\nuse futures_core::future::Future;\nuse futures_util::{future, future::FutureExt};\nuse gotham::{\n\thyper::{\n\t\tbody::{Body, Bytes},\n\t\tStatusCode\n\t},\n\tmime::Mime\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiSchema, OpenapiType, Visitor};\nuse serde_json::error::Error as SerdeJsonError;\nuse std::{convert::Infallible, fmt::Debug, pin::Pin};\n\n/// This type can be used both as a raw request body, as well as as a raw response. However, all types\n/// of request bodies are accepted by this type. It is therefore recommended to derive your own type\n/// from [RequestBody] and only use this when you need to return a raw response. This is a usage\n/// example that simply returns its body:\n///\n/// ```rust,no_run\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # use gotham::router::builder::*;\n/// # use gotham_restful::*;\n/// #[derive(Resource)]\n/// #[resource(create)]\n/// struct ImageResource;\n///\n/// #[create]\n/// fn create(body: Raw>) -> Raw> {\n/// \tbody\n/// }\n/// # fn main() {\n/// # \tgotham::start(\"127.0.0.1:8080\", build_simple_router(|route| {\n/// # \t\troute.resource::(\"img\");\n/// # \t}));\n/// # }\n/// ```\n#[derive(Debug)]\npub struct Raw {\n\tpub raw: T,\n\tpub mime: Mime\n}\n\nimpl Raw {\n\tpub fn new(raw: T, mime: Mime) -> Self {\n\t\tSelf { raw, mime }\n\t}\n}\n\nimpl AsMut for Raw\nwhere\n\tT: AsMut\n{\n\tfn as_mut(&mut self) -> &mut U {\n\t\tself.raw.as_mut()\n\t}\n}\n\nimpl AsRef for Raw\nwhere\n\tT: AsRef\n{\n\tfn as_ref(&self) -> &U {\n\t\tself.raw.as_ref()\n\t}\n}\n\nimpl Clone for Raw {\n\tfn clone(&self) -> Self {\n\t\tSelf {\n\t\t\traw: self.raw.clone(),\n\t\t\tmime: self.mime.clone()\n\t\t}\n\t}\n}\n\nimpl From<&'a [u8]>> FromBody for Raw {\n\ttype Err = Infallible;\n\n\tfn from_body(body: Bytes, mime: Mime) -> Result {\n\t\tOk(Self::new(body.as_ref().into(), mime))\n\t}\n}\n\nimpl RequestBody for Raw where Raw: FromBody + ResourceType {}\n\n#[cfg(feature = \"openapi\")]\nimpl OpenapiType for Raw {\n\tfn visit_type(visitor: &mut V) {\n\t\tvisitor.visit_binary()\n\t}\n}\n\nimpl> IntoResponse for Raw\nwhere\n\tSelf: Send\n{\n\ttype Err = SerdeJsonError; // just for easier handling of `Result, E>`\n\n\tfn into_response(\n\t\tself\n\t) -> Pin> + Send>> {\n\t\tfuture::ok(Response::new(StatusCode::OK, self.raw, Some(self.mime))).boxed()\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl> ResponseSchema for Raw\nwhere\n\tSelf: Send\n{\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::OK);\n\t\t::schema()\n\t}\n}\n\nimpl IntoResponse for Result, E>\nwhere\n\tRaw: IntoResponse,\n\tE: Debug + IntoResponseError as IntoResponse>::Err>\n{\n\ttype Err = E::Err;\n\n\tfn into_response(self) -> Pin> + Send>> {\n\t\tmatch self {\n\t\t\tOk(raw) => raw.into_response(),\n\t\t\tErr(e) => handle_error(e)\n\t\t}\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Result, E>\nwhere\n\tRaw: IntoResponseWithSchema,\n\tE: Debug + IntoResponseError as IntoResponse>::Err>\n{\n\tfn status_codes() -> Vec {\n\t\tlet mut status_codes = E::status_codes();\n\t\tstatus_codes.push(StatusCode::OK);\n\t\tstatus_codes\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tmatch code {\n\t\t\tStatusCode::OK => as ResponseSchema>::schema(StatusCode::OK),\n\t\t\tcode => E::schema(code)\n\t\t}\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse futures_executor::block_on;\n\tuse gotham::mime::TEXT_PLAIN;\n\tuse thiserror::Error;\n\n\t#[derive(Debug, Default, Error)]\n\t#[error(\"An Error\")]\n\tstruct MsgError;\n\n\t#[test]\n\tfn raw_response() {\n\t\tlet msg = \"Test\";\n\t\tlet raw = Raw::new(msg, TEXT_PLAIN);\n\t\tlet res = block_on(raw.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::OK);\n\t\tassert_eq!(res.mime, Some(TEXT_PLAIN));\n\t\tassert_eq!(res.full_body().unwrap(), msg.as_bytes());\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(>::status_codes(), vec![StatusCode::OK]);\n\t}\n\n\t#[test]\n\tfn raw_result() {\n\t\tlet msg = \"Test\";\n\t\tlet raw: Result, MsgError> = Ok(Raw::new(msg, TEXT_PLAIN));\n\t\tlet res = block_on(raw.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::OK);\n\t\tassert_eq!(res.mime, Some(TEXT_PLAIN));\n\t\tassert_eq!(res.full_body().unwrap(), msg.as_bytes());\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(, MsgError>>::status_codes(), vec![\n\t\t\tStatusCode::INTERNAL_SERVER_ERROR,\n\t\t\tStatusCode::OK\n\t\t]);\n\t}\n}\n","traces":[{"line":49,"address":[6827312],"length":1,"stats":{"Line":7},"fn_name":null},{"line":58,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":59,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":67,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":68,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":75,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":76,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":84,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":85,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":93,"address":[6327152],"length":1,"stats":{"Line":4},"fn_name":"visit_type"},{"line":94,"address":[8454693],"length":1,"stats":{"Line":4},"fn_name":null},{"line":104,"address":[6627776],"length":1,"stats":{"Line":5},"fn_name":"into_response<&[u8]>"},{"line":107,"address":[6813719],"length":1,"stats":{"Line":5},"fn_name":null},{"line":116,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":118,"address":[6566528],"length":1,"stats":{"Line":3},"fn_name":null},{"line":129,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":130,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":131,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":132,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":144,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":145,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":146,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":149,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":150,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":151,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":152,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":16,"coverable":28},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","redirect.rs"],"content":"use super::{handle_error, IntoResponse};\nuse crate::{IntoResponseError, Response};\n#[cfg(feature = \"openapi\")]\nuse crate::{NoContent, ResponseSchema};\nuse futures_util::future::{BoxFuture, FutureExt, TryFutureExt};\nuse gotham::hyper::{\n\theader::{InvalidHeaderValue, LOCATION},\n\tBody, StatusCode\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiSchema;\nuse std::{error::Error as StdError, fmt::Debug};\nuse thiserror::Error;\n\n/// This is the return type of a resource that only returns a redirect. It will result\n/// in a _303 See Other_ answer, meaning the redirect will always result in a GET request\n/// on the target.\n///\n/// ```\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # mod doc_tests_are_broken {\n/// # use gotham::state::State;\n/// # use gotham_restful::*;\n/// #\n/// # #[derive(Resource)]\n/// # #[resource(read_all)]\n/// # struct MyResource;\n/// #\n/// #[read_all]\n/// fn read_all() -> Redirect {\n/// \tRedirect {\n/// \t\tto: \"http://localhost:8080/cool/new/location\".to_owned()\n/// \t}\n/// }\n/// # }\n/// ```\n#[derive(Clone, Debug, Default)]\npub struct Redirect {\n\tpub to: String\n}\n\nimpl IntoResponse for Redirect {\n\ttype Err = InvalidHeaderValue;\n\n\tfn into_response(self) -> BoxFuture<'static, Result> {\n\t\tasync move {\n\t\t\tlet mut res = Response::new(StatusCode::SEE_OTHER, Body::empty(), None);\n\t\t\tres.header(LOCATION, self.to.parse()?);\n\t\t\tOk(res)\n\t\t}\n\t\t.boxed()\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Redirect {\n\tfn status_codes() -> Vec {\n\t\tvec![StatusCode::SEE_OTHER]\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::SEE_OTHER);\n\t\t::schema(StatusCode::NO_CONTENT)\n\t}\n}\n\n// private type due to parent mod\n#[derive(Debug, Error)]\npub enum RedirectError {\n\t#[error(\"{0}\")]\n\tInvalidLocation(#[from] InvalidHeaderValue),\n\t#[error(\"{0}\")]\n\tOther(#[source] E)\n}\n\n#[allow(ambiguous_associated_items)] // an enum variant is not a type. never.\nimpl IntoResponse for Result\nwhere\n\tE: Debug + IntoResponseError,\n\t::Err: StdError + Sync\n{\n\ttype Err = RedirectError<::Err>;\n\n\tfn into_response(self) -> BoxFuture<'static, Result> {\n\t\tmatch self {\n\t\t\tOk(nc) => nc.into_response().map_err(Into::into).boxed(),\n\t\t\tErr(e) => handle_error(e).map_err(RedirectError::Other).boxed()\n\t\t}\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Result\nwhere\n\tE: Debug + IntoResponseError,\n\t::Err: StdError + Sync\n{\n\tfn status_codes() -> Vec {\n\t\tlet mut status_codes = E::status_codes();\n\t\tstatus_codes.push(StatusCode::SEE_OTHER);\n\t\tstatus_codes\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tmatch code {\n\t\t\tStatusCode::SEE_OTHER => ::schema(StatusCode::SEE_OTHER),\n\t\t\tcode => E::schema(code)\n\t\t}\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse futures_executor::block_on;\n\tuse gotham::hyper::StatusCode;\n\tuse thiserror::Error;\n\n\t#[derive(Debug, Default, Error)]\n\t#[error(\"An Error\")]\n\tstruct MsgError;\n\n\t#[test]\n\tfn redirect_response() {\n\t\tlet redir = Redirect {\n\t\t\tto: \"http://localhost/foo\".to_owned()\n\t\t};\n\t\tlet res = block_on(redir.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::SEE_OTHER);\n\t\tassert_eq!(res.mime, None);\n\t\tassert_eq!(\n\t\t\tres.headers.get(LOCATION).map(|hdr| hdr.to_str().unwrap()),\n\t\t\tSome(\"http://localhost/foo\")\n\t\t);\n\t\tassert_eq!(res.full_body().unwrap(), &[] as &[u8]);\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(Redirect::status_codes(), vec![StatusCode::SEE_OTHER]);\n\t}\n\n\t#[test]\n\tfn redirect_result() {\n\t\tlet redir: Result = Ok(Redirect {\n\t\t\tto: \"http://localhost/foo\".to_owned()\n\t\t});\n\t\tlet res = block_on(redir.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::SEE_OTHER);\n\t\tassert_eq!(res.mime, None);\n\t\tassert_eq!(\n\t\t\tres.headers.get(LOCATION).map(|hdr| hdr.to_str().unwrap()),\n\t\t\tSome(\"http://localhost/foo\")\n\t\t);\n\t\tassert_eq!(res.full_body().unwrap(), &[] as &[u8]);\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(>::status_codes(), vec![\n\t\t\tStatusCode::INTERNAL_SERVER_ERROR,\n\t\t\tStatusCode::SEE_OTHER\n\t\t]);\n\t}\n}\n","traces":[{"line":45,"address":[8528832],"length":1,"stats":{"Line":1},"fn_name":"into_response"},{"line":46,"address":[8529605,8528836,8528880,8528910,8529318,8528967],"length":1,"stats":{"Line":3},"fn_name":"{async_block#0}"},{"line":47,"address":[8850672,8850810],"length":1,"stats":{"Line":4},"fn_name":null},{"line":48,"address":[8528942,8529696,8529356,8529329,8529788,8529131],"length":1,"stats":{"Line":6},"fn_name":null},{"line":49,"address":[8851471],"length":1,"stats":{"Line":2},"fn_name":null},{"line":57,"address":[8529935,8529824],"length":1,"stats":{"Line":1},"fn_name":"status_codes"},{"line":58,"address":[8851565,8851640],"length":1,"stats":{"Line":1},"fn_name":null},{"line":61,"address":[8529952],"length":1,"stats":{"Line":0},"fn_name":"schema"},{"line":63,"address":[8851776],"length":1,"stats":{"Line":0},"fn_name":null},{"line":84,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":85,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":86,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":87,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":99,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":100,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":101,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":104,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":14,"coverable":21},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","result.rs"],"content":"use super::{handle_error, IntoResponse, ResourceError};\n#[cfg(feature = \"openapi\")]\nuse crate::ResponseSchema;\nuse crate::{Response, ResponseBody, Success};\nuse futures_core::future::Future;\nuse gotham::{\n\tanyhow::Error,\n\thyper::StatusCode,\n\tmime::{Mime, APPLICATION_JSON}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiSchema, OpenapiType};\nuse std::{fmt::Debug, pin::Pin};\n\npub trait IntoResponseError {\n\ttype Err: Debug + Send + 'static;\n\n\tfn into_response_error(self) -> Result;\n\n\t#[cfg(feature = \"openapi\")]\n\tfn status_codes() -> Vec;\n\n\t#[cfg(feature = \"openapi\")]\n\tfn schema(code: StatusCode) -> OpenapiSchema;\n}\n\nimpl IntoResponseError for E\nwhere\n\tE: Into\n{\n\ttype Err = serde_json::Error;\n\n\tfn into_response_error(self) -> Result {\n\t\tlet err: Error = self.into();\n\t\tlet err: ResourceError = err.into();\n\t\tOk(Response::json(\n\t\t\tStatusCode::INTERNAL_SERVER_ERROR,\n\t\t\tserde_json::to_string(&err)?\n\t\t))\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\tfn status_codes() -> Vec {\n\t\tvec![StatusCode::INTERNAL_SERVER_ERROR]\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::INTERNAL_SERVER_ERROR);\n\t\tResourceError::schema()\n\t}\n}\n\nimpl IntoResponse for Result\nwhere\n\tR: ResponseBody,\n\tE: Debug + IntoResponseError\n{\n\ttype Err = E::Err;\n\n\tfn into_response(self) -> Pin> + Send>> {\n\t\tmatch self {\n\t\t\tOk(r) => Success::from(r).into_response(),\n\t\t\tErr(e) => handle_error(e)\n\t\t}\n\t}\n\n\tfn accepted_types() -> Option> {\n\t\tSome(vec![APPLICATION_JSON])\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Result\nwhere\n\tR: ResponseBody,\n\tE: Debug + IntoResponseError\n{\n\tfn status_codes() -> Vec {\n\t\tlet mut status_codes = E::status_codes();\n\t\tstatus_codes.push(StatusCode::OK);\n\t\tstatus_codes\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tmatch code {\n\t\t\tStatusCode::OK => R::schema(),\n\t\t\tcode => E::schema(code)\n\t\t}\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse crate::response::OrAllTypes;\n\tuse futures_executor::block_on;\n\tuse thiserror::Error;\n\n\t#[derive(Debug, Default, Deserialize, Serialize)]\n\t#[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n\tstruct Msg {\n\t\tmsg: String\n\t}\n\n\t#[derive(Debug, Default, Error)]\n\t#[error(\"An Error\")]\n\tstruct MsgError;\n\n\t#[test]\n\tfn result_ok() {\n\t\tlet ok: Result = Ok(Msg::default());\n\t\tlet res = block_on(ok.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::OK);\n\t\tassert_eq!(res.mime, Some(APPLICATION_JSON));\n\t\tassert_eq!(res.full_body().unwrap(), br#\"{\"msg\":\"\"}\"#);\n\t}\n\n\t#[test]\n\tfn result_err() {\n\t\tlet err: Result = Err(MsgError::default());\n\t\tlet res = block_on(err.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::INTERNAL_SERVER_ERROR);\n\t\tassert_eq!(res.mime, Some(APPLICATION_JSON));\n\t\tassert_eq!(\n\t\t\tres.full_body().unwrap(),\n\t\t\tformat!(r#\"{{\"error\":true,\"message\":\"{}\"}}\"#, MsgError::default()).as_bytes()\n\t\t);\n\t}\n\n\t#[test]\n\tfn success_accepts_json() {\n\t\tassert!(\n\t\t\t>::accepted_types()\n\t\t\t\t.or_all_types()\n\t\t\t\t.contains(&APPLICATION_JSON)\n\t\t)\n\t}\n}\n","traces":[{"line":33,"address":[5720928,5721117],"length":1,"stats":{"Line":2},"fn_name":"into_response_error"},{"line":34,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":35,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":36,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":37,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":38,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":43,"address":[5720800,5720911],"length":1,"stats":{"Line":3},"fn_name":"status_codes"},{"line":44,"address":[5720813,5720888],"length":1,"stats":{"Line":3},"fn_name":null},{"line":48,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[5721440],"length":1,"stats":{"Line":0},"fn_name":null},{"line":61,"address":[6401264,6401424],"length":1,"stats":{"Line":2},"fn_name":"into_response"},{"line":62,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":63,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":64,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":68,"address":[6401792,6401778,6401584,6401986],"length":1,"stats":{"Line":3},"fn_name":"accepted_types"},{"line":69,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":79,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":80,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":81,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":82,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":85,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":86,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":87,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":88,"address":[],"length":0,"stats":{"Line":2},"fn_name":null}],"covered":21,"coverable":24},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","success.rs"],"content":"use super::IntoResponse;\n#[cfg(feature = \"openapi\")]\nuse crate::ResponseSchema;\nuse crate::{Response, ResponseBody};\nuse futures_util::future::{self, FutureExt};\nuse gotham::{\n\thyper::{\n\t\theader::{HeaderMap, HeaderValue, IntoHeaderName},\n\t\tStatusCode\n\t},\n\tmime::{Mime, APPLICATION_JSON}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiSchema;\nuse std::{fmt::Debug, future::Future, pin::Pin};\n\n/// This can be returned from a resource when there is no cause of an error.\n///\n/// Usage example:\n///\n/// ```\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # mod doc_tests_are_broken {\n/// # use gotham::state::State;\n/// # use gotham_restful::*;\n/// # use serde::{Deserialize, Serialize};\n/// #\n/// # #[derive(Resource)]\n/// # #[resource(read_all)]\n/// # struct MyResource;\n/// #\n/// #[derive(Deserialize, Serialize)]\n/// # #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n/// struct MyResponse {\n/// \tmessage: &'static str\n/// }\n///\n/// #[read_all]\n/// fn read_all() -> Success {\n/// \tlet res = MyResponse {\n/// \t\tmessage: \"I'm always happy\"\n/// \t};\n/// \tres.into()\n/// }\n/// # }\n/// ```\n#[derive(Clone, Debug, Default)]\npub struct Success {\n\tvalue: T,\n\theaders: HeaderMap\n}\n\nimpl From for Success {\n\tfn from(t: T) -> Self {\n\t\tSelf {\n\t\t\tvalue: t,\n\t\t\theaders: HeaderMap::new()\n\t\t}\n\t}\n}\n\nimpl Success {\n\t/// Set a custom HTTP header. If a header with this name was set before, its value is being updated.\n\tpub fn header(&mut self, name: K, value: HeaderValue) {\n\t\tself.headers.insert(name, value);\n\t}\n\n\t/// Allow manipulating HTTP headers.\n\tpub fn headers_mut(&mut self) -> &mut HeaderMap {\n\t\t&mut self.headers\n\t}\n}\n\nimpl IntoResponse for Success {\n\ttype Err = serde_json::Error;\n\n\tfn into_response(self) -> Pin> + Send>> {\n\t\tlet res = serde_json::to_string(&self.value)\n\t\t\t.map(|body| Response::json(StatusCode::OK, body).with_headers(self.headers));\n\t\tfuture::ready(res).boxed()\n\t}\n\n\tfn accepted_types() -> Option> {\n\t\tSome(vec![APPLICATION_JSON])\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Success {\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::OK);\n\t\tT::schema()\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse crate::response::OrAllTypes;\n\tuse futures_executor::block_on;\n\tuse gotham::hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN;\n\n\t#[derive(Debug, Default, Serialize)]\n\t#[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n\tstruct Msg {\n\t\tmsg: String\n\t}\n\n\t#[test]\n\tfn success_always_successfull() {\n\t\tlet success: Success = Msg::default().into();\n\t\tlet res = block_on(success.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::OK);\n\t\tassert_eq!(res.mime, Some(APPLICATION_JSON));\n\t\tassert_eq!(res.full_body().unwrap(), br#\"{\"msg\":\"\"}\"#);\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(>::status_codes(), vec![StatusCode::OK]);\n\t}\n\n\t#[test]\n\tfn success_custom_headers() {\n\t\tlet mut success: Success = Msg::default().into();\n\t\tsuccess.header(ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static(\"*\"));\n\t\tlet res = block_on(success.into_response()).expect(\"didn't expect error response\");\n\t\tlet cors = res.headers.get(ACCESS_CONTROL_ALLOW_ORIGIN);\n\t\tassert_eq!(cors.map(|value| value.to_str().unwrap()), Some(\"*\"));\n\t}\n\n\t#[test]\n\tfn success_accepts_json() {\n\t\tassert!(\n\t\t\t>::accepted_types()\n\t\t\t\t.or_all_types()\n\t\t\t\t.contains(&APPLICATION_JSON)\n\t\t)\n\t}\n}\n","traces":[{"line":54,"address":[7580944,7580992,7581088,7581230],"length":1,"stats":{"Line":3},"fn_name":"from"},{"line":57,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":64,"address":[6444752],"length":1,"stats":{"Line":1},"fn_name":null},{"line":65,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":69,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":70,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":77,"address":[6445168,6445478,6445499,6445131,6445110,6444800],"length":1,"stats":{"Line":2},"fn_name":"into_response"},{"line":78,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":79,"address":[],"length":0,"stats":{"Line":9},"fn_name":null},{"line":80,"address":[],"length":0,"stats":{"Line":5},"fn_name":null},{"line":83,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":84,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":90,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":10,"coverable":14},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","routing.rs"],"content":"#[cfg(feature = \"openapi\")]\nuse crate::openapi::{\n\tbuilder::{OpenapiBuilder, OpenapiInfo},\n\trouter::OpenapiRouter\n};\nuse crate::{response::ResourceError, Endpoint, FromBody, IntoResponse, Resource, Response};\n#[cfg(feature = \"cors\")]\nuse gotham::router::route::matcher::AccessControlRequestMethodMatcher;\nuse gotham::{\n\thandler::HandlerError,\n\thelpers::http::response::{create_empty_response, create_response},\n\thyper::{body::to_bytes, header::CONTENT_TYPE, Body, HeaderMap, Method, StatusCode},\n\tmime::{Mime, APPLICATION_JSON},\n\tpipeline::PipelineHandleChain,\n\tprelude::*,\n\trouter::{\n\t\tbuilder::{RouterBuilder, ScopeBuilder},\n\t\troute::matcher::{AcceptHeaderRouteMatcher, ContentTypeHeaderRouteMatcher, RouteMatcher},\n\t\tRouteNonMatch\n\t},\n\tstate::{FromState, State}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiType;\nuse std::{any::TypeId, panic::RefUnwindSafe};\n\n/// Allow us to extract an id from a path.\n#[derive(Clone, Copy, Debug, Deserialize, StateData, StaticResponseExtender)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\npub struct PathExtractor {\n\tpub id: ID\n}\n\n/// This trait adds the `with_openapi` method to gotham's routing. It turns the default\n/// router into one that will only allow RESTful resources, but record them and generate\n/// an OpenAPI specification on request.\n#[cfg(feature = \"openapi\")]\npub trait WithOpenapi {\n\tfn with_openapi(&mut self, info: OpenapiInfo, block: F)\n\twhere\n\t\tF: FnOnce(OpenapiRouter<'_, D>);\n}\n\n/// This trait adds the `resource` method to gotham's routing. It allows you to register\n/// any RESTful [Resource] with a path.\n#[_private_openapi_trait(DrawResourcesWithSchema)]\npub trait DrawResources {\n\t#[openapi_bound(R: crate::ResourceWithSchema)]\n\t#[non_openapi_bound(R: crate::Resource)]\n\tfn resource(&mut self, path: &str);\n}\n\n/// This trait allows to draw routes within an resource. Use this only inside the\n/// [Resource::setup] method.\n#[_private_openapi_trait(DrawResourceRoutesWithSchema)]\npub trait DrawResourceRoutes {\n\t#[openapi_bound(E: crate::EndpointWithSchema)]\n\t#[non_openapi_bound(E: crate::Endpoint)]\n\tfn endpoint(&mut self);\n}\n\nfn response_from(res: Response, state: &State) -> gotham::hyper::Response {\n\tlet mut r = create_empty_response(state, res.status);\n\tlet headers = r.headers_mut();\n\tif let Some(mime) = res.mime {\n\t\theaders.insert(CONTENT_TYPE, mime.as_ref().parse().unwrap());\n\t}\n\tlet mut last_name = None;\n\tfor (name, value) in res.headers {\n\t\tif name.is_some() {\n\t\t\tlast_name = name;\n\t\t}\n\t\t// this unwrap is safe: the first item will always be Some\n\t\tlet name = last_name.clone().unwrap();\n\t\theaders.insert(name, value);\n\t}\n\n\tlet method = Method::borrow_from(state);\n\tif method != Method::HEAD {\n\t\t*r.body_mut() = res.body;\n\t}\n\n\t#[cfg(feature = \"cors\")]\n\tcrate::cors::handle_cors(state, &mut r);\n\n\tr\n}\n\nasync fn endpoint_handler(\n\tstate: &mut State\n) -> Result, HandlerError>\nwhere\n\tE: Endpoint,\n\t::Err: Into\n{\n\ttrace!(\"entering endpoint_handler\");\n\tlet placeholders = E::Placeholders::take_from(state);\n\t// workaround for E::Placeholders and E::Param being the same type\n\t// when fixed remove `Clone` requirement on endpoint\n\tif TypeId::of::() == TypeId::of::() {\n\t\tstate.put(placeholders.clone());\n\t}\n\tlet params = E::Params::take_from(state);\n\n\tlet body = match E::needs_body() {\n\t\ttrue => {\n\t\t\tlet body = to_bytes(Body::take_from(state)).await?;\n\n\t\t\tlet content_type: Mime = match HeaderMap::borrow_from(state).get(CONTENT_TYPE) {\n\t\t\t\tSome(content_type) => content_type.to_str().unwrap().parse().unwrap(),\n\t\t\t\tNone => {\n\t\t\t\t\tdebug!(\"Missing Content-Type: Returning 415 Response\");\n\t\t\t\t\tlet res = create_empty_response(state, StatusCode::UNSUPPORTED_MEDIA_TYPE);\n\t\t\t\t\treturn Ok(res);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tmatch E::Body::from_body(body, content_type) {\n\t\t\t\tOk(body) => Some(body),\n\t\t\t\tErr(e) => {\n\t\t\t\t\tdebug!(\"Invalid Body: Returning 400 Response\");\n\t\t\t\t\tlet error: ResourceError = e.into();\n\t\t\t\t\tlet json = serde_json::to_string(&error)?;\n\t\t\t\t\tlet res =\n\t\t\t\t\t\tcreate_response(state, StatusCode::BAD_REQUEST, APPLICATION_JSON, json);\n\t\t\t\t\treturn Ok(res);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tfalse => None\n\t};\n\n\tlet out = E::handle(state, placeholders, params, body).await;\n\tlet res = out.into_response().await.map_err(Into::into)?;\n\tdebug!(\"Returning response {res:?}\");\n\tOk(response_from(res, state))\n}\n\n#[derive(Clone)]\nstruct MaybeMatchAcceptHeader {\n\tmatcher: Option\n}\n\nimpl RouteMatcher for MaybeMatchAcceptHeader {\n\tfn is_match(&self, state: &State) -> Result<(), RouteNonMatch> {\n\t\tmatch &self.matcher {\n\t\t\tSome(matcher) => matcher.is_match(state),\n\t\t\tNone => Ok(())\n\t\t}\n\t}\n}\n\nimpl MaybeMatchAcceptHeader {\n\tfn new(types: Option>) -> Self {\n\t\tlet types = match types {\n\t\t\tSome(types) if types.is_empty() => None,\n\t\t\ttypes => types\n\t\t};\n\t\tSelf {\n\t\t\tmatcher: types.map(AcceptHeaderRouteMatcher::new)\n\t\t}\n\t}\n}\n\nimpl From>> for MaybeMatchAcceptHeader {\n\tfn from(types: Option>) -> Self {\n\t\tSelf::new(types)\n\t}\n}\n\n#[derive(Clone)]\nstruct MaybeMatchContentTypeHeader {\n\tmatcher: Option\n}\n\nimpl RouteMatcher for MaybeMatchContentTypeHeader {\n\tfn is_match(&self, state: &State) -> Result<(), RouteNonMatch> {\n\t\tmatch &self.matcher {\n\t\t\tSome(matcher) => matcher.is_match(state),\n\t\t\tNone => Ok(())\n\t\t}\n\t}\n}\n\nimpl MaybeMatchContentTypeHeader {\n\tfn new(types: Option>) -> Self {\n\t\tSelf {\n\t\t\tmatcher: types.map(|types| ContentTypeHeaderRouteMatcher::new(types).allow_no_type())\n\t\t}\n\t}\n}\n\nimpl From>> for MaybeMatchContentTypeHeader {\n\tfn from(types: Option>) -> Self {\n\t\tSelf::new(types)\n\t}\n}\n\nmacro_rules! implDrawResourceRoutes {\n\t($implType:ident) => {\n\t\t#[cfg(feature = \"openapi\")]\n\t\timpl<'a, C, P> WithOpenapi for $implType<'a, C, P>\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn with_openapi(&mut self, info: OpenapiInfo, block: F)\n\t\t\twhere\n\t\t\t\tF: FnOnce(OpenapiRouter<'_, $implType<'a, C, P>>)\n\t\t\t{\n\t\t\t\tlet router = OpenapiRouter {\n\t\t\t\t\trouter: self,\n\t\t\t\t\tscope: None,\n\t\t\t\t\topenapi_builder: &mut OpenapiBuilder::new(info)\n\t\t\t\t};\n\t\t\t\tblock(router);\n\t\t\t}\n\t\t}\n\n\t\timpl<'a, C, P> DrawResources for $implType<'a, C, P>\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn resource(&mut self, mut path: &str) {\n\t\t\t\tif path.starts_with('/') {\n\t\t\t\t\tpath = &path[1..];\n\t\t\t\t}\n\t\t\t\tR::setup((self, path));\n\t\t\t}\n\t\t}\n\n\t\timpl<'a, C, P> DrawResourceRoutes for (&mut $implType<'a, C, P>, &str)\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn endpoint(&mut self) {\n\t\t\t\tlet uri = format!(\"{}/{}\", self.1, E::uri());\n\t\t\t\tdebug!(\"Registering endpoint for {uri}\");\n\t\t\t\tself.0.associate(&uri, |assoc| {\n\t\t\t\t\tassoc\n\t\t\t\t\t\t.request(vec![E::http_method()])\n\t\t\t\t\t\t.add_route_matcher(MaybeMatchAcceptHeader::new(E::Output::accepted_types()))\n\t\t\t\t\t\t.with_path_extractor::()\n\t\t\t\t\t\t.with_query_string_extractor::()\n\t\t\t\t\t\t.to_async_borrowing(endpoint_handler::);\n\n\t\t\t\t\t#[cfg(feature = \"cors\")]\n\t\t\t\t\tif E::http_method() != Method::GET {\n\t\t\t\t\t\tassoc\n\t\t\t\t\t\t\t.options()\n\t\t\t\t\t\t\t.add_route_matcher(AccessControlRequestMethodMatcher::new(\n\t\t\t\t\t\t\t\tE::http_method()\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t.to(crate::cors::cors_preflight_handler);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t};\n}\n\nimplDrawResourceRoutes!(RouterBuilder);\nimplDrawResourceRoutes!(ScopeBuilder);\n","traces":[{"line":62,"address":[7831000,7832442,7830176],"length":1,"stats":{"Line":5},"fn_name":"response_from"},{"line":63,"address":[7830370,7830219],"length":1,"stats":{"Line":10},"fn_name":null},{"line":64,"address":[7830378,7830447],"length":1,"stats":{"Line":10},"fn_name":null},{"line":65,"address":[7830455],"length":1,"stats":{"Line":5},"fn_name":null},{"line":66,"address":[7830780,7830981,7830569],"length":1,"stats":{"Line":4},"fn_name":null},{"line":68,"address":[7830640],"length":1,"stats":{"Line":5},"fn_name":null},{"line":69,"address":[7831881,7831099,7830652],"length":1,"stats":{"Line":10},"fn_name":null},{"line":70,"address":[7831421,7831611,7831353],"length":1,"stats":{"Line":0},"fn_name":null},{"line":71,"address":[7831457],"length":1,"stats":{"Line":0},"fn_name":null},{"line":74,"address":[7831427,7831616],"length":1,"stats":{"Line":0},"fn_name":null},{"line":75,"address":[7831655],"length":1,"stats":{"Line":0},"fn_name":null},{"line":78,"address":[7831910],"length":1,"stats":{"Line":5},"fn_name":null},{"line":79,"address":[7832231,7831939],"length":1,"stats":{"Line":10},"fn_name":null},{"line":80,"address":[7832236,7832008],"length":1,"stats":{"Line":5},"fn_name":null},{"line":84,"address":[7831984],"length":1,"stats":{"Line":5},"fn_name":null},{"line":86,"address":[7832263],"length":1,"stats":{"Line":5},"fn_name":null},{"line":89,"address":[6821840],"length":1,"stats":{"Line":21},"fn_name":"endpoint_handler"},{"line":96,"address":[6822600,6822447,6822511,6822059],"length":1,"stats":{"Line":63},"fn_name":null},{"line":97,"address":[6822579,6822730],"length":1,"stats":{"Line":42},"fn_name":null},{"line":100,"address":[6822742,6822807],"length":1,"stats":{"Line":42},"fn_name":null},{"line":101,"address":[6822907],"length":1,"stats":{"Line":13},"fn_name":null},{"line":103,"address":[6822889,6822989],"length":1,"stats":{"Line":42},"fn_name":null},{"line":105,"address":[6823064,6823001],"length":1,"stats":{"Line":42},"fn_name":null},{"line":106,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[6662236],"length":1,"stats":{"Line":24},"fn_name":null},{"line":109,"address":[6823503,6823648],"length":1,"stats":{"Line":16},"fn_name":null},{"line":110,"address":[6823798],"length":1,"stats":{"Line":8},"fn_name":null},{"line":111,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[6825327,6823750,6825494,6825391],"length":1,"stats":{"Line":0},"fn_name":null},{"line":113,"address":[6825459],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[6825634],"length":1,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[6823960],"length":1,"stats":{"Line":8},"fn_name":null},{"line":119,"address":[6824127],"length":1,"stats":{"Line":8},"fn_name":null},{"line":120,"address":[6824184],"length":1,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[6824349,6824440,6824285,6824192],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[6824417],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[6824919,6824606,6824547],"length":1,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[6824738],"length":1,"stats":{"Line":0},"fn_name":null},{"line":125,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":126,"address":[6825027],"length":1,"stats":{"Line":0},"fn_name":null},{"line":130,"address":[6823070],"length":1,"stats":{"Line":13},"fn_name":null},{"line":133,"address":[6662255],"length":1,"stats":{"Line":64},"fn_name":null},{"line":134,"address":[6662271],"length":1,"stats":{"Line":42},"fn_name":null},{"line":135,"address":[6826823,6826506,6827047,6826765],"length":1,"stats":{"Line":63},"fn_name":null},{"line":136,"address":[6826885,6827169],"length":1,"stats":{"Line":42},"fn_name":null},{"line":145,"address":[7832656],"length":1,"stats":{"Line":6},"fn_name":"is_match"},{"line":146,"address":[7832689],"length":1,"stats":{"Line":6},"fn_name":null},{"line":147,"address":[7832738],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[7832718],"length":1,"stats":{"Line":6},"fn_name":null},{"line":154,"address":[7833153,7833223,7832768],"length":1,"stats":{"Line":7},"fn_name":null},{"line":155,"address":[7832790],"length":1,"stats":{"Line":7},"fn_name":null},{"line":156,"address":[7832851,7832998],"length":1,"stats":{"Line":6},"fn_name":null},{"line":157,"address":[7832879],"length":1,"stats":{"Line":5},"fn_name":null},{"line":160,"address":[7833062],"length":1,"stats":{"Line":7},"fn_name":null},{"line":166,"address":[7833248],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":167,"address":[7833256],"length":1,"stats":{"Line":0},"fn_name":null},{"line":177,"address":[7833280],"length":1,"stats":{"Line":0},"fn_name":"is_match"},{"line":178,"address":[7833313],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[7833359],"length":1,"stats":{"Line":0},"fn_name":null},{"line":180,"address":[7833339],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[7833392],"length":1,"stats":{"Line":0},"fn_name":null},{"line":188,"address":[7833469,7833456,7833406],"length":1,"stats":{"Line":0},"fn_name":"{closure#0}"},{"line":194,"address":[7833520],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":195,"address":[7833528],"length":1,"stats":{"Line":0},"fn_name":null},{"line":207,"address":[7613544,7613280,7613525],"length":1,"stats":{"Line":2},"fn_name":"with_openapi<(borrow_bag::handle::Handle, ())>, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::auth::AuthMiddleware, ())>, ()), openapi_specification::openapi_specification::{closure#0}::{closure_env#0}>"},{"line":211,"address":[7613308,7613396],"length":1,"stats":{"Line":4},"fn_name":null},{"line":213,"address":[7613318],"length":1,"stats":{"Line":2},"fn_name":null},{"line":214,"address":[7613327,7613391],"length":1,"stats":{"Line":4},"fn_name":null},{"line":216,"address":[7613431],"length":1,"stats":{"Line":2},"fn_name":null},{"line":225,"address":[6359488],"length":1,"stats":{"Line":5},"fn_name":"resource<(), (), custom_request_body::FooResource>"},{"line":226,"address":[6359512,6359629],"length":1,"stats":{"Line":7},"fn_name":null},{"line":227,"address":[6359582],"length":1,"stats":{"Line":2},"fn_name":null},{"line":229,"address":[6359542],"length":1,"stats":{"Line":5},"fn_name":null},{"line":238,"address":[6362068,6361712],"length":1,"stats":{"Line":31},"fn_name":"endpoint<(), (), custom_request_body::create___gotham_restful_endpoint>"},{"line":239,"address":[6362094,6361814,6361891,6361732],"length":1,"stats":{"Line":62},"fn_name":null},{"line":240,"address":[6362309,6362487,6362251,6362164],"length":1,"stats":{"Line":93},"fn_name":null},{"line":241,"address":[6362376,6362624],"length":1,"stats":{"Line":93},"fn_name":null},{"line":242,"address":[6821076,6821406,6821473,6821280],"length":1,"stats":{"Line":124},"fn_name":null},{"line":243,"address":[6821285,6821194,6821102,6821216,6821313],"length":1,"stats":{"Line":93},"fn_name":null},{"line":244,"address":[6821560,6821445,6821338,6821301],"length":1,"stats":{"Line":62},"fn_name":null},{"line":250,"address":[6821520,6821570],"length":1,"stats":{"Line":31},"fn_name":null},{"line":251,"address":[6821649,6821796,6821749],"length":1,"stats":{"Line":45},"fn_name":null},{"line":253,"address":[6821722],"length":1,"stats":{"Line":15},"fn_name":null},{"line":254,"address":[6821678],"length":1,"stats":{"Line":15},"fn_name":null}],"covered":57,"coverable":84},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","types.rs"],"content":"use gotham::{\n\thyper::body::Bytes,\n\tmime::{Mime, APPLICATION_JSON}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiType;\nuse serde::{de::DeserializeOwned, Serialize};\nuse std::error::Error;\n\n#[cfg(not(feature = \"openapi\"))]\npub trait ResourceType {}\n\n#[cfg(not(feature = \"openapi\"))]\nimpl ResourceType for T {}\n\n#[cfg(feature = \"openapi\")]\npub trait ResourceType: OpenapiType {}\n\n#[cfg(feature = \"openapi\")]\nimpl ResourceType for T {}\n\n/// A type that can be used inside a response body. Implemented for every type that is\n/// serializable with serde. If the `openapi` feature is used, it must also be of type\n/// [OpenapiType].\npub trait ResponseBody: ResourceType + Serialize {}\n\nimpl ResponseBody for T {}\n\n/// This trait should be implemented for every type that can be built from an HTTP request body\n/// plus its media type.\n///\n/// For most use cases it is sufficient to derive this trait, you usually don't need to manually\n/// implement this. Therefore, make sure that the first variable of your struct can be built from\n/// [Bytes], and the second one can be build from [Mime]. If you have any additional variables, they\n/// need to be [Default]. This is an example of such a struct:\n///\n/// ```rust\n/// # use gotham::mime::{self, Mime};\n/// # use gotham_restful::{FromBody, RequestBody};\n/// #[derive(FromBody, RequestBody)]\n/// #[supported_types(mime::IMAGE_GIF, mime::IMAGE_JPEG, mime::IMAGE_PNG)]\n/// struct RawImage {\n/// \tcontent: Vec,\n/// \tcontent_type: Mime\n/// }\n/// ```\npub trait FromBody: Sized {\n\t/// The error type returned by the conversion if it was unsuccessfull. When using the derive\n\t/// macro, there is no way to trigger an error, so [std::convert::Infallible] is used here.\n\t/// However, this might change in the future.\n\ttype Err: Error;\n\n\t/// Perform the conversion.\n\tfn from_body(body: Bytes, content_type: Mime) -> Result;\n}\n\nimpl FromBody for T {\n\ttype Err = serde_json::Error;\n\n\tfn from_body(body: Bytes, _content_type: Mime) -> Result {\n\t\tserde_json::from_slice(&body)\n\t}\n}\n\n/// A type that can be used inside a request body. Implemented for every type that is deserializable\n/// with serde. If the `openapi` feature is used, it must also be of type [OpenapiType].\n///\n/// If you want a non-deserializable type to be used as a request body, e.g. because you'd like to\n/// get the raw data, you can derive it for your own type. All you need is to have a type implementing\n/// [FromBody] and optionally a list of supported media types:\n///\n/// ```rust\n/// # use gotham::mime::{self, Mime};\n/// # use gotham_restful::{FromBody, RequestBody};\n/// #[derive(FromBody, RequestBody)]\n/// #[supported_types(mime::IMAGE_GIF, mime::IMAGE_JPEG, mime::IMAGE_PNG)]\n/// struct RawImage {\n/// \tcontent: Vec,\n/// \tcontent_type: Mime\n/// }\n/// ```\npub trait RequestBody: ResourceType + FromBody {\n\t/// Return all types that are supported as content types. Use `None` if all types are supported.\n\tfn supported_types() -> Option> {\n\t\tNone\n\t}\n}\n\nimpl RequestBody for T {\n\tfn supported_types() -> Option> {\n\t\tSome(vec![APPLICATION_JSON])\n\t}\n}\n","traces":[{"line":60,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":61,"address":[6927758,6927817],"length":1,"stats":{"Line":4},"fn_name":null},{"line":84,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":85,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":90,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":91,"address":[],"length":0,"stats":{"Line":1},"fn_name":null}],"covered":4,"coverable":6},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","async_methods.rs"],"content":"use gotham::{\n\thyper::{HeaderMap, Method},\n\tmime::{APPLICATION_JSON, TEXT_PLAIN},\n\tprelude::*,\n\trouter::build_simple_router,\n\tstate::State,\n\ttest::TestServer\n};\nuse gotham_restful::*;\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiType;\nuse serde::Deserialize;\nuse tokio::time::{sleep, Duration};\n\nmod util {\n\tinclude!(\"util/mod.rs\");\n}\nuse util::{test_delete_response, test_get_response, test_post_response, test_put_response};\n\n#[derive(Resource)]\n#[resource(\n\tread_all, read, search, create, update_all, update, delete_all, delete, state_test\n)]\nstruct FooResource;\n\n#[derive(Deserialize)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\n#[allow(dead_code)]\nstruct FooBody {\n\tdata: String\n}\n\n#[derive(Clone, Deserialize, StateData, StaticResponseExtender)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\n#[allow(dead_code)]\nstruct FooSearch {\n\tquery: String\n}\n\nconst READ_ALL_RESPONSE: &[u8] = b\"1ARwwSPVyOKpJKrYwqGgECPVWDl1BqajAAj7g7WJ3e\";\n#[read_all]\nasync fn read_all() -> Raw<&'static [u8]> {\n\tRaw::new(READ_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst READ_RESPONSE: &[u8] = b\"FEReHoeBKU17X2bBpVAd1iUvktFL43CDu0cFYHdaP9\";\n#[read]\nasync fn read(_id: u64) -> Raw<&'static [u8]> {\n\tRaw::new(READ_RESPONSE, TEXT_PLAIN)\n}\n\nconst SEARCH_RESPONSE: &[u8] = b\"AWqcQUdBRHXKh3at4u79mdupOAfEbnTcx71ogCVF0E\";\n#[search]\nasync fn search(_body: FooSearch) -> Raw<&'static [u8]> {\n\tRaw::new(SEARCH_RESPONSE, TEXT_PLAIN)\n}\n\nconst CREATE_RESPONSE: &[u8] = b\"y6POY7wOMAB0jBRBw0FJT7DOpUNbhmT8KdpQPLkI83\";\n#[create]\nasync fn create(_body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(CREATE_RESPONSE, TEXT_PLAIN)\n}\n\nconst UPDATE_ALL_RESPONSE: &[u8] = b\"QlbYg8gHE9OQvvk3yKjXJLTSXlIrg9mcqhfMXJmQkv\";\n#[update_all]\nasync fn update_all(_body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(UPDATE_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst UPDATE_RESPONSE: &[u8] = b\"qGod55RUXkT1lgPO8h0uVM6l368O2S0GrwENZFFuRu\";\n#[update]\nasync fn update(_id: u64, _body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(UPDATE_RESPONSE, TEXT_PLAIN)\n}\n\nconst DELETE_ALL_RESPONSE: &[u8] = b\"Y36kZ749MRk2Nem4BedJABOZiZWPLOtiwLfJlGTwm5\";\n#[delete_all]\nasync fn delete_all() -> Raw<&'static [u8]> {\n\tRaw::new(DELETE_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst DELETE_RESPONSE: &[u8] = b\"CwRzBrKErsVZ1N7yeNfjZuUn1MacvgBqk4uPOFfDDq\";\n#[delete]\nasync fn delete(_id: u64) -> Raw<&'static [u8]> {\n\tRaw::new(DELETE_RESPONSE, TEXT_PLAIN)\n}\n\nconst STATE_TEST_RESPONSE: &[u8] = b\"xxJbxOuwioqR5DfzPuVqvaqRSfpdNQGluIvHU4n1LM\";\n#[endpoint(method = \"Method::GET\", uri = \"state_test\")]\nasync fn state_test(state: &mut State) -> Raw<&'static [u8]> {\n\tsleep(Duration::from_nanos(1)).await;\n\tstate.borrow::();\n\tsleep(Duration::from_nanos(1)).await;\n\tRaw::new(STATE_TEST_RESPONSE, TEXT_PLAIN)\n}\n\n#[test]\nfn async_methods() {\n\tlet _ = pretty_env_logger::try_init_timed();\n\n\tlet server = TestServer::new(build_simple_router(|router| {\n\t\trouter.resource::(\"foo\");\n\t}))\n\t.unwrap();\n\n\ttest_get_response(&server, \"http://localhost/foo\", READ_ALL_RESPONSE);\n\ttest_get_response(&server, \"http://localhost/foo/1\", READ_RESPONSE);\n\ttest_get_response(\n\t\t&server,\n\t\t\"http://localhost/foo/search?query=hello+world\",\n\t\tSEARCH_RESPONSE\n\t);\n\ttest_post_response(\n\t\t&server,\n\t\t\"http://localhost/foo\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tCREATE_RESPONSE\n\t);\n\ttest_put_response(\n\t\t&server,\n\t\t\"http://localhost/foo\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tUPDATE_ALL_RESPONSE\n\t);\n\ttest_put_response(\n\t\t&server,\n\t\t\"http://localhost/foo/1\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tUPDATE_RESPONSE\n\t);\n\ttest_delete_response(&server, \"http://localhost/foo\", DELETE_ALL_RESPONSE);\n\ttest_delete_response(&server, \"http://localhost/foo/1\", DELETE_RESPONSE);\n\ttest_get_response(\n\t\t&server,\n\t\t\"http://localhost/foo/state_test\",\n\t\tSTATE_TEST_RESPONSE\n\t);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","cors_handling.rs"],"content":"#![cfg(feature = \"cors\")]\nuse gotham::{\n\thyper::{body::Body, client::connect::Connect, header::*, StatusCode},\n\tmime::TEXT_PLAIN,\n\tpipeline::{new_pipeline, single_pipeline},\n\trouter::build_router,\n\ttest::{Server, TestRequest, TestServer}\n};\nuse gotham_restful::{\n\tcors::{Headers, Origin},\n\tread_all, update_all, CorsConfig, DrawResources, Raw, Resource\n};\n\n#[derive(Resource)]\n#[resource(read_all, update_all)]\nstruct FooResource;\n\n#[read_all]\nfn read_all() {}\n\n#[update_all]\nfn update_all(_body: Raw>) {}\n\nfn test_server(cfg: CorsConfig) -> TestServer {\n\tlet (chain, pipeline) = single_pipeline(new_pipeline().add(cfg).build());\n\tTestServer::new(build_router(chain, pipeline, |router| {\n\t\trouter.resource::(\"/foo\")\n\t}))\n\t.unwrap()\n}\n\nfn test_response(\n\treq: TestRequest,\n\torigin: Option<&str>,\n\tvary: Option<&str>,\n\tcredentials: bool\n) where\n\tTS: Server + 'static,\n\tC: Connect + Clone + Send + Sync + 'static\n{\n\tlet res = req\n\t\t.with_header(ORIGIN, \"http://example.org\".parse().unwrap())\n\t\t.perform()\n\t\t.unwrap();\n\tassert_eq!(res.status(), StatusCode::NO_CONTENT);\n\tlet headers = res.headers();\n\tprintln!(\n\t\t\"{}\",\n\t\theaders\n\t\t\t.keys()\n\t\t\t.map(|name| name.as_str())\n\t\t\t.collect::>()\n\t\t\t.join(\",\")\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_ALLOW_ORIGIN)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\torigin\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(VARY)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\tvary\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_ALLOW_CREDENTIALS)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.map(|value| value == \"true\")\n\t\t\t.unwrap_or(false),\n\t\tcredentials\n\t);\n\tassert!(headers.get(ACCESS_CONTROL_MAX_AGE).is_none());\n}\n\nfn test_preflight(\n\tserver: &TestServer,\n\tmethod: &str,\n\torigin: Option<&str>,\n\tvary: &str,\n\tcredentials: bool,\n\tmax_age: u64\n) {\n\tlet res = server\n\t\t.client()\n\t\t.options(\"http://example.org/foo\")\n\t\t.with_header(ACCESS_CONTROL_REQUEST_METHOD, method.parse().unwrap())\n\t\t.with_header(ORIGIN, \"http://example.org\".parse().unwrap())\n\t\t.perform()\n\t\t.unwrap();\n\tassert_eq!(res.status(), StatusCode::NO_CONTENT);\n\tlet headers = res.headers();\n\tprintln!(\n\t\t\"{}\",\n\t\theaders\n\t\t\t.keys()\n\t\t\t.map(|name| name.as_str())\n\t\t\t.collect::>()\n\t\t\t.join(\",\")\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_ALLOW_METHODS)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\tSome(method)\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_ALLOW_ORIGIN)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\torigin\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(VARY)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\tSome(vary)\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_ALLOW_CREDENTIALS)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.map(|value| value == \"true\")\n\t\t\t.unwrap_or(false),\n\t\tcredentials\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_MAX_AGE)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.and_then(|value| value.parse().ok()),\n\t\tSome(max_age)\n\t);\n}\n\nfn test_preflight_headers(\n\tserver: &TestServer,\n\tmethod: &str,\n\trequest_headers: Option<&str>,\n\tallowed_headers: Option<&str>,\n\tvary: &str\n) {\n\tlet client = server.client();\n\tlet mut res = client\n\t\t.options(\"http://example.org/foo\")\n\t\t.with_header(ACCESS_CONTROL_REQUEST_METHOD, method.parse().unwrap())\n\t\t.with_header(ORIGIN, \"http://example.org\".parse().unwrap());\n\tif let Some(hdr) = request_headers {\n\t\tres = res.with_header(ACCESS_CONTROL_REQUEST_HEADERS, hdr.parse().unwrap());\n\t}\n\tlet res = res.perform().unwrap();\n\tassert_eq!(res.status(), StatusCode::NO_CONTENT);\n\tlet headers = res.headers();\n\tprintln!(\n\t\t\"{}\",\n\t\theaders\n\t\t\t.keys()\n\t\t\t.map(|name| name.as_str())\n\t\t\t.collect::>()\n\t\t\t.join(\",\")\n\t);\n\tif let Some(hdr) = allowed_headers {\n\t\tassert_eq!(\n\t\t\theaders\n\t\t\t\t.get(ACCESS_CONTROL_ALLOW_HEADERS)\n\t\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t\t.as_deref(),\n\t\t\tSome(hdr)\n\t\t)\n\t} else {\n\t\tassert!(!headers.contains_key(ACCESS_CONTROL_ALLOW_HEADERS));\n\t}\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(VARY)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\tSome(vary)\n\t);\n}\n\n#[test]\nfn cors_origin_none() {\n\tlet cfg = Default::default();\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tNone,\n\t\t\"access-control-request-method\",\n\t\tfalse,\n\t\t0\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tNone,\n\t\tNone,\n\t\tfalse\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tNone,\n\t\tNone,\n\t\tfalse\n\t);\n}\n\n#[test]\nfn cors_origin_star() {\n\tlet cfg = CorsConfig {\n\t\torigin: Origin::Star,\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"*\"),\n\t\t\"access-control-request-method\",\n\t\tfalse,\n\t\t0\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tSome(\"*\"),\n\t\tNone,\n\t\tfalse\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tSome(\"*\"),\n\t\tNone,\n\t\tfalse\n\t);\n}\n\n#[test]\nfn cors_origin_single() {\n\tlet cfg = CorsConfig {\n\t\torigin: Origin::Single(\"https://foo.com\".to_owned()),\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"https://foo.com\"),\n\t\t\"access-control-request-method\",\n\t\tfalse,\n\t\t0\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tSome(\"https://foo.com\"),\n\t\tNone,\n\t\tfalse\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tSome(\"https://foo.com\"),\n\t\tNone,\n\t\tfalse\n\t);\n}\n\n#[test]\nfn cors_origin_copy() {\n\tlet cfg = CorsConfig {\n\t\torigin: Origin::Copy,\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"http://example.org\"),\n\t\t\"access-control-request-method,origin\",\n\t\tfalse,\n\t\t0\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tSome(\"http://example.org\"),\n\t\tSome(\"origin\"),\n\t\tfalse\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tSome(\"http://example.org\"),\n\t\tSome(\"origin\"),\n\t\tfalse\n\t);\n}\n\n#[test]\nfn cors_headers_none() {\n\tlet cfg = Default::default();\n\tlet server = test_server(cfg);\n\n\ttest_preflight_headers(&server, \"PUT\", None, None, \"access-control-request-method\");\n\ttest_preflight_headers(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"Content-Type\"),\n\t\tNone,\n\t\t\"access-control-request-method\"\n\t);\n}\n\n#[test]\nfn cors_headers_list() {\n\tlet cfg = CorsConfig {\n\t\theaders: Headers::List(vec![CONTENT_TYPE]),\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight_headers(\n\t\t&server,\n\t\t\"PUT\",\n\t\tNone,\n\t\tSome(\"content-type\"),\n\t\t\"access-control-request-method\"\n\t);\n\ttest_preflight_headers(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"content-type\"),\n\t\tSome(\"content-type\"),\n\t\t\"access-control-request-method\"\n\t);\n}\n\n#[test]\nfn cors_headers_copy() {\n\tlet cfg = CorsConfig {\n\t\theaders: Headers::Copy,\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight_headers(\n\t\t&server,\n\t\t\"PUT\",\n\t\tNone,\n\t\tNone,\n\t\t\"access-control-request-method,access-control-request-headers\"\n\t);\n\ttest_preflight_headers(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"content-type\"),\n\t\tSome(\"content-type\"),\n\t\t\"access-control-request-method,access-control-request-headers\"\n\t);\n}\n\n#[test]\nfn cors_credentials() {\n\tlet cfg = CorsConfig {\n\t\torigin: Origin::None,\n\t\tcredentials: true,\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tNone,\n\t\t\"access-control-request-method\",\n\t\ttrue,\n\t\t0\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tNone,\n\t\tNone,\n\t\ttrue\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tNone,\n\t\tNone,\n\t\ttrue\n\t);\n}\n\n#[test]\nfn cors_max_age() {\n\tlet cfg = CorsConfig {\n\t\torigin: Origin::None,\n\t\tmax_age: 31536000,\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tNone,\n\t\t\"access-control-request-method\",\n\t\tfalse,\n\t\t31536000\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tNone,\n\t\tNone,\n\t\tfalse\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tNone,\n\t\tNone,\n\t\tfalse\n\t);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","custom_request_body.rs"],"content":"use gotham::{\n\thyper::header::CONTENT_TYPE,\n\tmime::{Mime, TEXT_PLAIN},\n\trouter::builder::*,\n\ttest::TestServer\n};\nuse gotham_restful::{create, DrawResources, FromBody, Raw, RequestBody, Resource};\n\nconst RESPONSE: &[u8] = b\"This is the only valid response.\";\n\n#[derive(Resource)]\n#[resource(create)]\nstruct FooResource;\n\n#[derive(FromBody, RequestBody)]\n#[supported_types(TEXT_PLAIN)]\nstruct Foo {\n\tcontent: Vec,\n\tcontent_type: Mime\n}\n\n#[create]\nfn create(body: Foo) -> Raw> {\n\tRaw::new(body.content, body.content_type)\n}\n\n#[test]\nfn custom_request_body() {\n\tlet server = TestServer::new(build_simple_router(|router| {\n\t\trouter.resource::(\"foo\");\n\t}))\n\t.unwrap();\n\n\tlet res = server\n\t\t.client()\n\t\t.post(\"http://localhost/foo\", RESPONSE, TEXT_PLAIN)\n\t\t.perform()\n\t\t.unwrap();\n\tassert_eq!(\n\t\tres.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap(),\n\t\t\"text/plain\"\n\t);\n\tlet res = res.read_body().unwrap();\n\tlet body: &[u8] = res.as_ref();\n\tassert_eq!(body, RESPONSE);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","openapi_specification.rs"],"content":"#![cfg(all(feature = \"auth\", feature = \"openapi\"))]\n#![allow(clippy::approx_constant)]\n\n#[macro_use]\nextern crate pretty_assertions;\n\nuse gotham::{\n\thyper::{Method, StatusCode},\n\tmime::{IMAGE_PNG, TEXT_PLAIN_UTF_8},\n\tpipeline::{new_pipeline, single_pipeline},\n\tprelude::*,\n\trouter::build_router,\n\ttest::TestServer\n};\nuse gotham_restful::*;\nuse openapi_type::{OpenapiSchema, OpenapiType, Visitor};\nuse serde::{Deserialize, Serialize};\n\n#[allow(dead_code)]\nmod util {\n\tinclude!(\"util/mod.rs\");\n}\nuse util::test_openapi_response;\n\nconst IMAGE_RESPONSE : &[u8] = b\"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUA/wA0XsCoAAAAAXRSTlN/gFy0ywAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=\";\n\n#[derive(Resource)]\n#[resource(get_image, set_image)]\nstruct ImageResource;\n\n#[derive(FromBody, RequestBody)]\n#[supported_types(IMAGE_PNG)]\nstruct Image(Vec);\n\n#[read(operation_id = \"getImage\")]\nfn get_image(_id: u64) -> Raw<&'static [u8]> {\n\tRaw::new(IMAGE_RESPONSE, \"image/png;base64\".parse().unwrap())\n}\n\n#[update(operation_id = \"setImage\")]\nfn set_image(_id: u64, _image: Image) {}\n\n#[derive(Resource)]\n#[resource(read_secret, search_secret)]\nstruct SecretResource;\n\n#[derive(Deserialize, Clone)]\nstruct AuthData {\n\tsub: String,\n\tiat: u64,\n\texp: u64\n}\n\ntype AuthStatus = gotham_restful::AuthStatus;\n\n#[derive(OpenapiType, Serialize)]\nstruct Secret {\n\tcode: f32\n}\n\n#[derive(OpenapiType, Serialize)]\nstruct Secrets {\n\tsecrets: Vec\n}\n\n#[derive(Clone, Deserialize, OpenapiType, StateData, StaticResponseExtender)]\nstruct SecretQuery {\n\tdate: String,\n\thour: Option,\n\tminute: Option\n}\n\n/// This endpoint gives access to the secret.\n///\n/// You need to be authenticated to call this endpoint.\n#[read]\nfn read_secret(auth: AuthStatus, _id: String) -> AuthSuccess {\n\tauth.ok()?;\n\tOk(Secret { code: 4.2 })\n}\n\n#[search]\nfn search_secret(auth: AuthStatus, _query: SecretQuery) -> AuthSuccess {\n\tauth.ok()?;\n\tOk(Secrets {\n\t\tsecrets: vec![Secret { code: 4.2 }, Secret { code: 3.14 }]\n\t})\n}\n\n#[derive(Resource)]\n#[resource(coffee_read_all)]\nstruct CoffeeResource;\n\nfn teapot_status_codes() -> Vec {\n\tvec![StatusCode::IM_A_TEAPOT]\n}\n\nfn teapot_schema(code: StatusCode) -> OpenapiSchema {\n\tassert_eq!(code, StatusCode::IM_A_TEAPOT);\n\n\tstruct Binary;\n\n\timpl OpenapiType for Binary {\n\t\tfn visit_type(visitor: &mut V) {\n\t\t\tvisitor.visit_binary();\n\t\t}\n\t}\n\n\tBinary::schema()\n}\n\n#[read_all(status_codes = \"teapot_status_codes\", schema = \"teapot_schema\")]\nfn coffee_read_all() -> Response {\n\tResponse::new(\n\t\tStatusCode::IM_A_TEAPOT,\n\t\t\"Sorry, this is just your fancy grandma's teapot. Can't make coffee.\",\n\t\tSome(TEXT_PLAIN_UTF_8)\n\t)\n}\n\n#[derive(Resource)]\n#[resource(custom_read_with, custom_patch)]\nstruct CustomResource;\n\n#[derive(Clone, Deserialize, OpenapiType, StateData, StaticResponseExtender)]\nstruct ReadWithPath {\n\tfrom: String,\n\tid: u64\n}\n\n#[endpoint(method = \"Method::GET\", uri = \"read/:from/with/:id\")]\nfn custom_read_with(_path: ReadWithPath) {}\n\n#[endpoint(method = \"Method::PATCH\", uri = \"\", body = true)]\nfn custom_patch(_body: String) {}\n\n#[test]\nfn openapi_specification() {\n\tlet info = OpenapiInfo {\n\t\ttitle: \"This is just a test\".to_owned(),\n\t\tversion: \"1.2.3\".to_owned(),\n\t\turls: vec![\"http://localhost:12345/api/v1\".to_owned()]\n\t};\n\tlet auth: AuthMiddleware = AuthMiddleware::new(\n\t\tAuthSource::AuthorizationHeader,\n\t\tAuthValidation::default(),\n\t\tStaticAuthHandler::from_array(b\"zlBsA2QXnkmpe0QTh8uCvtAEa4j33YAc\")\n\t);\n\tlet (chain, pipelines) = single_pipeline(new_pipeline().add(auth).build());\n\tlet server = TestServer::new(build_router(chain, pipelines, |router| {\n\t\trouter.with_openapi(info, |mut router| {\n\t\t\t// the leading slash tests that the spec doesn't contain '//img' nonsense\n\t\t\trouter.resource::(\"/img\");\n\t\t\trouter.resource::(\"secret\");\n\t\t\trouter.resource::(\"coffee\");\n\t\t\trouter.resource::(\"custom\");\n\t\t\trouter.openapi_spec(\"openapi\");\n\t\t});\n\t}))\n\t.unwrap();\n\n\ttest_openapi_response(\n\t\t&server,\n\t\t\"http://localhost/openapi\",\n\t\t\"tests/openapi_specification.json\"\n\t);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","openapi_supports_scope.rs"],"content":"#![cfg(feature = \"openapi\")]\n\n#[macro_use]\nextern crate pretty_assertions;\n\nuse gotham::{mime::TEXT_PLAIN, router::builder::*, test::TestServer};\nuse gotham_restful::*;\n\n#[allow(dead_code)]\nmod util {\n\tinclude!(\"util/mod.rs\");\n}\nuse util::{test_get_response, test_openapi_response};\n\nconst RESPONSE: &[u8] = b\"This is the only valid response.\";\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all]\nfn read_all() -> Raw<&'static [u8]> {\n\tRaw::new(RESPONSE, TEXT_PLAIN)\n}\n\n#[test]\nfn openapi_supports_scope() {\n\tlet info = OpenapiInfo {\n\t\ttitle: \"Test\".to_owned(),\n\t\tversion: \"1.2.3\".to_owned(),\n\t\turls: Vec::new()\n\t};\n\tlet server = TestServer::new(build_simple_router(|router| {\n\t\trouter.with_openapi(info, |mut router| {\n\t\t\trouter.openapi_spec(\"openapi\");\n\t\t\trouter.resource::(\"foo1\");\n\t\t\trouter.scope(\"/bar\", |router| {\n\t\t\t\trouter.resource::(\"foo2\");\n\t\t\t\trouter.scope(\"/baz\", |router| {\n\t\t\t\t\trouter.resource::(\"foo3\");\n\t\t\t\t})\n\t\t\t});\n\t\t\trouter.resource::(\"foo4\");\n\t\t});\n\t}))\n\t.unwrap();\n\n\ttest_get_response(&server, \"http://localhost/foo1\", RESPONSE);\n\ttest_get_response(&server, \"http://localhost/bar/foo2\", RESPONSE);\n\ttest_get_response(&server, \"http://localhost/bar/baz/foo3\", RESPONSE);\n\ttest_get_response(&server, \"http://localhost/foo4\", RESPONSE);\n\ttest_openapi_response(\n\t\t&server,\n\t\t\"http://localhost/openapi\",\n\t\t\"tests/openapi_supports_scope.json\"\n\t);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","resource_error.rs"],"content":"use gotham_restful::ResourceError;\n\n#[derive(ResourceError)]\nenum Error {\n\t#[display(\"I/O Error: {0}\")]\n\tIoError(#[from] std::io::Error),\n\n\t#[status(INTERNAL_SERVER_ERROR)]\n\t#[display(\"Internal Server Error: {0}\")]\n\tInternalServerError(String)\n}\n\n#[allow(deprecated)]\nmod resource_error {\n\tuse super::Error;\n\tuse gotham::{hyper::StatusCode, mime::APPLICATION_JSON};\n\tuse gotham_restful::IntoResponseError;\n\n\t#[test]\n\tfn io_error() {\n\t\tlet err = Error::IoError(std::io::Error::last_os_error());\n\t\tlet res = err.into_response_error().unwrap();\n\t\tassert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);\n\t\tassert_eq!(res.mime(), Some(&APPLICATION_JSON));\n\t}\n\n\t#[test]\n\tfn internal_server_error() {\n\t\tlet err = Error::InternalServerError(\"Brocken\".to_owned());\n\t\tassert_eq!(&format!(\"{err}\"), \"Internal Server Error: Brocken\");\n\n\t\tlet res = err.into_response_error().unwrap();\n\t\tassert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);\n\t\tassert_eq!(res.mime(), None); // TODO shouldn't this be a json error message?\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","sync_methods.rs"],"content":"use gotham::{\n\tmime::{APPLICATION_JSON, TEXT_PLAIN},\n\tprelude::*,\n\trouter::build_simple_router,\n\ttest::TestServer\n};\nuse gotham_restful::*;\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiType;\nuse serde::Deserialize;\n\nmod util {\n\tinclude!(\"util/mod.rs\");\n}\nuse util::{test_delete_response, test_get_response, test_post_response, test_put_response};\n\n#[derive(Resource)]\n#[resource(read_all, read, search, create, update_all, update, delete_all, delete)]\nstruct FooResource;\n\n#[derive(Deserialize)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\n#[allow(dead_code)]\nstruct FooBody {\n\tdata: String\n}\n\n#[derive(Clone, Deserialize, StateData, StaticResponseExtender)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\n#[allow(dead_code)]\nstruct FooSearch {\n\tquery: String\n}\n\nconst READ_ALL_RESPONSE: &[u8] = b\"1ARwwSPVyOKpJKrYwqGgECPVWDl1BqajAAj7g7WJ3e\";\n#[read_all]\nfn read_all() -> Raw<&'static [u8]> {\n\tRaw::new(READ_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst READ_RESPONSE: &[u8] = b\"FEReHoeBKU17X2bBpVAd1iUvktFL43CDu0cFYHdaP9\";\n#[read]\nfn read(_id: u64) -> Raw<&'static [u8]> {\n\tRaw::new(READ_RESPONSE, TEXT_PLAIN)\n}\n\nconst SEARCH_RESPONSE: &[u8] = b\"AWqcQUdBRHXKh3at4u79mdupOAfEbnTcx71ogCVF0E\";\n#[search]\nfn search(_body: FooSearch) -> Raw<&'static [u8]> {\n\tRaw::new(SEARCH_RESPONSE, TEXT_PLAIN)\n}\n\nconst CREATE_RESPONSE: &[u8] = b\"y6POY7wOMAB0jBRBw0FJT7DOpUNbhmT8KdpQPLkI83\";\n#[create]\nfn create(_body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(CREATE_RESPONSE, TEXT_PLAIN)\n}\n\nconst UPDATE_ALL_RESPONSE: &[u8] = b\"QlbYg8gHE9OQvvk3yKjXJLTSXlIrg9mcqhfMXJmQkv\";\n#[update_all]\nfn update_all(_body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(UPDATE_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst UPDATE_RESPONSE: &[u8] = b\"qGod55RUXkT1lgPO8h0uVM6l368O2S0GrwENZFFuRu\";\n#[update]\nfn update(_id: u64, _body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(UPDATE_RESPONSE, TEXT_PLAIN)\n}\n\nconst DELETE_ALL_RESPONSE: &[u8] = b\"Y36kZ749MRk2Nem4BedJABOZiZWPLOtiwLfJlGTwm5\";\n#[delete_all]\nfn delete_all() -> Raw<&'static [u8]> {\n\tRaw::new(DELETE_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst DELETE_RESPONSE: &[u8] = b\"CwRzBrKErsVZ1N7yeNfjZuUn1MacvgBqk4uPOFfDDq\";\n#[delete]\nfn delete(_id: u64) -> Raw<&'static [u8]> {\n\tRaw::new(DELETE_RESPONSE, TEXT_PLAIN)\n}\n\n#[test]\nfn sync_methods() {\n\tlet _ = pretty_env_logger::try_init_timed();\n\n\tlet server = TestServer::new(build_simple_router(|router| {\n\t\trouter.resource::(\"foo\");\n\t}))\n\t.unwrap();\n\n\ttest_get_response(&server, \"http://localhost/foo\", READ_ALL_RESPONSE);\n\ttest_get_response(&server, \"http://localhost/foo/1\", READ_RESPONSE);\n\ttest_get_response(\n\t\t&server,\n\t\t\"http://localhost/foo/search?query=hello+world\",\n\t\tSEARCH_RESPONSE\n\t);\n\ttest_post_response(\n\t\t&server,\n\t\t\"http://localhost/foo\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tCREATE_RESPONSE\n\t);\n\ttest_put_response(\n\t\t&server,\n\t\t\"http://localhost/foo\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tUPDATE_ALL_RESPONSE\n\t);\n\ttest_put_response(\n\t\t&server,\n\t\t\"http://localhost/foo/1\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tUPDATE_RESPONSE\n\t);\n\ttest_delete_response(&server, \"http://localhost/foo\", DELETE_ALL_RESPONSE);\n\ttest_delete_response(&server, \"http://localhost/foo/1\", DELETE_RESPONSE);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","trybuild_ui.rs"],"content":"use trybuild::TestCases;\n\n#[test]\n#[ignore]\nfn trybuild_ui() {\n\tlet t = TestCases::new();\n\tt.compile_fail(\"tests/ui/endpoint/*.rs\");\n\tt.compile_fail(\"tests/ui/from_body/*.rs\");\n\tt.compile_fail(\"tests/ui/resource/*.rs\");\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","async_state.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\nuse gotham::state::State;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all]\nasync fn read_all(state: &State) {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","auth_data_non_clone.rs"],"content":"use gotham_restful::*;\nuse serde::Deserialize;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[derive(Deserialize)]\nstruct AuthData {\n\tiat: u64,\n\texp: u64\n}\n\n#[read_all]\nasync fn read_all(auth: AuthStatus) -> Result {\n\tauth.ok()?;\n\tOk(NoContent::default())\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","custom_method_invalid_expr.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[endpoint(method = \"I like pizza\", uri = \"custom_read\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","custom_method_invalid_type.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[endpoint(method = \"String::new()\", uri = \"custom_read\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","custom_method_missing.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[endpoint(uri = \"custom_read\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","custom_uri_missing.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[endpoint(method = \"gotham_restful::gotham::hyper::Method::GET\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","dynamic_schema_missing_schema.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\nuse gotham::hyper::StatusCode;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\nfn status_codes() -> Vec {\n\tunimplemented!()\n}\n\n#[read_all(status_codes = \"status_codes\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","dynamic_schema_missing_status_codes.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\nuse gotham::hyper::StatusCode;\nuse gotham_restful::private::OpenapiSchema;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\nfn schema(_: StatusCode) -> OpenapiSchema {\n\tunimplemented!()\n}\n\n#[read_all(schema = \"schema\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","dynamic_schema_wrong_type.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\nfn schema(_: u16) -> String {\n\tunimplemented!()\n}\n\nfn status_codes() -> Vec {\n\tunimplemented!()\n}\n\n#[read_all(schema = \"schema\", status_codes = \"status_codes\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","invalid_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(FooResource)]\nfn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","invalid_body_ty.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\nuse gotham_restful::gotham::hyper::Method;\n\n#[derive(Resource)]\n#[resource(endpoint)]\nstruct FooResource;\n\n#[derive(Debug)]\nstruct FooBody {\n\tfoo: String\n}\n\n#[endpoint(method = \"Method::GET\", uri = \"\", body = true)]\nfn endpoint(_: FooBody) {\n\tunimplemented!()\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","invalid_params_ty.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\nuse gotham_restful::gotham::hyper::Method;\n\n#[derive(Resource)]\n#[resource(endpoint)]\nstruct FooResource;\n\n#[derive(Debug)]\nstruct FooParams {\n\tfoo: String\n}\n\n#[endpoint(method = \"Method::GET\", uri = \"\", params = true)]\nfn endpoint(_: FooParams) {\n\tunimplemented!()\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","invalid_placeholders_ty.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\nuse gotham_restful::gotham::hyper::Method;\n\n#[derive(Resource)]\n#[resource(endpoint)]\nstruct FooResource;\n\n#[derive(Debug)]\nstruct FooPlaceholders {\n\tfoo: String\n}\n\n#[endpoint(method = \"Method::GET\", uri = \":foo\")]\nfn endpoint(_: FooPlaceholders) {\n\tunimplemented!()\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","invalid_return_type.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\nuse gotham_restful::gotham::hyper::Method;\n\n#[derive(Resource)]\n#[resource(endpoint)]\nstruct FooResource;\n\nstruct FooResponse;\n\n#[endpoint(method = \"Method::GET\", uri = \"\")]\nfn endpoint() -> FooResponse {\n\tunimplemented!()\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","non_custom_body_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(body = false)]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","non_custom_method_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(method = \"gotham_restful::gotham::hyper::Method::GET\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","non_custom_params_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(params = true)]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","non_custom_uri_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(uri = \"custom_read\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","self.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all]\nfn read_all(self) {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","too_few_args.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read)]\nstruct FooResource;\n\n#[read]\nfn read() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","too_many_args.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all]\nfn read_all(_id: u64) {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","unknown_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(pineapple = \"on pizza\")]\nfn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","unsafe.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all]\nunsafe fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","wants_auth_non_bool.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(wants_auth = \"yes, please\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","from_body","enum.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(FromBody)]\nenum FromBodyEnum {\n\tSomeVariant(Vec),\n\tOtherVariant(String)\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","resource","unknown_method.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_any)]\nstruct FooResource;\n\n#[read_all]\nfn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","util","mod.rs"],"content":"use gotham::{\n\thyper::Body,\n\ttest::TestServer\n};\nuse log::info;\nuse gotham::mime::Mime;\n#[allow(unused_imports)]\nuse std::{fs::File, io::{Read, Write}, str};\n\npub fn test_get_response(server : &TestServer, path : &str, expected : &[u8])\n{\n\tinfo!(\"GET {path}\");\n\tlet res = server.client().get(path).perform().unwrap().read_body().unwrap();\n\tlet body : &[u8] = res.as_ref();\n\tassert_eq!(body, expected);\n}\n\npub fn test_post_response(server : &TestServer, path : &str, body : B, mime : Mime, expected : &[u8])\nwhere\n\tB : Into\n{\n\tinfo!(\"POST {path}\");\n\tlet res = server.client().post(path, body, mime).perform().unwrap().read_body().unwrap();\n\tlet body : &[u8] = res.as_ref();\n\tassert_eq!(body, expected);\n}\n\npub fn test_put_response(server : &TestServer, path : &str, body : B, mime : Mime, expected : &[u8])\nwhere\n\tB : Into\n{\n\tinfo!(\"PUT {path}\");\n\tlet res = server.client().put(path, body, mime).perform().unwrap().read_body().unwrap();\n\tlet body : &[u8] = res.as_ref();\n\tassert_eq!(body, expected);\n}\n\npub fn test_delete_response(server : &TestServer, path : &str, expected : &[u8])\n{\n\tinfo!(\"DELETE {path}\");\n\tlet res = server.client().delete(path).perform().unwrap().read_body().unwrap();\n\tlet body : &[u8] = res.as_ref();\n\tassert_eq!(body, expected);\n}\n\n#[cfg(feature = \"openapi\")]\npub fn test_openapi_response(server : &TestServer, path : &str, output_file : &str)\n{\n\tinfo!(\"GET {path}\");\n\tlet res = server.client().get(path).perform().unwrap().read_body().unwrap();\n\tlet body: serde_json::Value = serde_json::from_slice(&res).unwrap();\n\n\tlet mut file = File::open(output_file).unwrap();\n\tlet expected: serde_json::Value = serde_json::from_reader(&mut file).unwrap();\n\n\t//eprintln!(\"{body}\");\n\tassert_eq!(body, expected);\n}\n","traces":[],"covered":0,"coverable":0}]} \ No newline at end of file +{"files":[{"path":["/","home","runner","work","gotham_restful","gotham_restful","redoc","src","lib.rs"],"content":"#![forbid(elided_lifetimes_in_paths, unsafe_code)]\n\n//! Private implementation detail of the `gotham_restful` crate.\n\nuse base64::prelude::*;\nuse either::Either;\nuse sha2::{Digest, Sha256};\nuse std::{io::Write, iter};\n\n#[doc(hidden)]\npub struct Redoc {\n\t/// HTML code.\n\tpub html: Vec,\n\n\t/// JS hash base64 encoded.\n\tpub script_hash: String\n}\n\n#[doc(hidden)]\npub fn html(spec: String) -> Redoc {\n\tlet encoded_spec = spec\n\t\t.chars()\n\t\t.flat_map(|c| match c {\n\t\t\t'&' => Either::Left(\"&\".chars()),\n\t\t\t'<' => Either::Left(\"<\".chars()),\n\t\t\t'>' => Either::Left(\">\".chars()),\n\t\t\tc => Either::Right(iter::once(c))\n\t\t})\n\t\t.collect::();\n\n\tlet script = include_str!(\"script.min.js\");\n\tlet mut script_hash = Sha256::new();\n\tscript_hash.update(script);\n\tlet script_hash = BASE64_STANDARD.encode(script_hash.finalize());\n\n\tlet mut html = Vec::::new();\n\twrite!(\n\t\thtml,\n\t\tconcat!(\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\tr#\"\"#,\n\t\t\tr#\"\"#,\n\t\t\t\"\",\n\t\t\tr#\"\"#,\n\t\t\tr#\"

{}
\"#,\n\t\t\tr#\"
\"#,\n\t\t\tr#\"\"#,\n\t\t\t\"\",\n\t\t\t\"\"\n\t\t),\n\t\tencoded_spec, script\n\t)\n\t.unwrap();\n\n\tRedoc { html, script_hash }\n}\n","traces":[{"line":20,"address":[8042736,8043782,8043817],"length":1,"stats":{"Line":0},"fn_name":"html"},{"line":21,"address":[8042855,8042787],"length":1,"stats":{"Line":0},"fn_name":null},{"line":23,"address":[6837627,6837600],"length":1,"stats":{"Line":0},"fn_name":"{closure#0}"},{"line":24,"address":[8043925],"length":1,"stats":{"Line":0},"fn_name":null},{"line":25,"address":[6837719],"length":1,"stats":{"Line":0},"fn_name":null},{"line":26,"address":[6837753],"length":1,"stats":{"Line":0},"fn_name":null},{"line":27,"address":[6837660],"length":1,"stats":{"Line":0},"fn_name":null},{"line":31,"address":[8042766,8042928],"length":1,"stats":{"Line":0},"fn_name":null},{"line":32,"address":[6836715],"length":1,"stats":{"Line":0},"fn_name":null},{"line":33,"address":[7936026],"length":1,"stats":{"Line":0},"fn_name":null},{"line":34,"address":[8043053],"length":1,"stats":{"Line":0},"fn_name":null},{"line":36,"address":[8043226],"length":1,"stats":{"Line":0},"fn_name":null},{"line":37,"address":[8043521],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":13},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","auth.rs"],"content":"use crate::AuthError;\n\nuse base64::prelude::*;\nuse cookie::CookieJar;\nuse futures_util::{\n\tfuture,\n\tfuture::{FutureExt, TryFutureExt}\n};\nuse gotham::{\n\tanyhow,\n\thandler::HandlerFuture,\n\thyper::header::{HeaderMap, HeaderName, AUTHORIZATION},\n\tmiddleware::{cookie::CookieParser, Middleware, NewMiddleware},\n\tprelude::*,\n\tstate::State\n};\nuse jsonwebtoken::DecodingKey;\nuse serde::de::DeserializeOwned;\nuse std::{marker::PhantomData, panic::RefUnwindSafe, pin::Pin};\n\npub type AuthValidation = jsonwebtoken::Validation;\n\n/// The authentication status returned by the auth middleware for each request.\n#[derive(Debug, StateData)]\npub enum AuthStatus {\n\t/// The auth status is unknown. This is likely because no secret was provided\n\t/// that could be used to verify the token of the client.\n\tUnknown,\n\n\t/// The request has been performed without any kind of authentication.\n\tUnauthenticated,\n\n\t/// The request has been performed with an invalid authentication. This\n\t/// includes expired tokens. Further details can be obtained from the\n\t/// included error.\n\tInvalid(jsonwebtoken::errors::Error),\n\n\t/// The request has been performed with a valid authentication. The claims\n\t/// that were decoded from the token are attached.\n\tAuthenticated(T)\n}\n\nimpl Clone for AuthStatus\nwhere\n\tT: Clone + Send + 'static\n{\n\tfn clone(&self) -> Self {\n\t\t// TODO why is this manually implemented?\n\t\tmatch self {\n\t\t\tSelf::Unknown => Self::Unknown,\n\t\t\tSelf::Unauthenticated => Self::Unauthenticated,\n\t\t\tSelf::Invalid(err) => Self::Invalid(err.clone()),\n\t\t\tSelf::Authenticated(data) => Self::Authenticated(data.clone())\n\t\t}\n\t}\n}\n\nimpl AuthStatus {\n\tpub fn ok(self) -> Result {\n\t\tmatch self {\n\t\t\tSelf::Unknown => Err(AuthError::new(\"The authentication could not be determined\")),\n\t\t\tSelf::Unauthenticated => Err(AuthError::new(\"Missing token\")),\n\t\t\tSelf::Invalid(err) => Err(AuthError::new(format!(\"Invalid token: {err}\"))),\n\t\t\tSelf::Authenticated(data) => Ok(data)\n\t\t}\n\t}\n}\n\n/// The source of the authentication token in the request.\n#[derive(Clone, Debug, StateData)]\npub enum AuthSource {\n\t/// Take the token from a cookie with the given name.\n\tCookie(String),\n\t/// Take the token from a header with the given name.\n\tHeader(HeaderName),\n\t/// Take the token from the HTTP Authorization header. This is different from `Header(\"Authorization\")`\n\t/// as it will follow the `scheme param` format from the HTTP specification. The `scheme` will\n\t/// be discarded, so its value doesn't matter.\n\tAuthorizationHeader\n}\n\n/// This trait will help the auth middleware to determine the validity of an authentication token.\n///\n/// A very basic implementation could look like this:\n///\n/// ```\n/// # use gotham_restful::{AuthHandler, gotham::state::State};\n/// #\n/// const SECRET: &'static [u8; 32] = b\"zlBsA2QXnkmpe0QTh8uCvtAEa4j33YAc\";\n///\n/// struct CustomAuthHandler;\n/// impl AuthHandler for CustomAuthHandler {\n/// \tfn jwt_secret Option>(\n/// \t\t&self,\n/// \t\t_state: &mut State,\n/// \t\t_decode_data: F\n/// \t) -> Option> {\n/// \t\tSome(SECRET.to_vec())\n/// \t}\n/// }\n/// ```\npub trait AuthHandler {\n\t/// Return the SHA256-HMAC secret used to verify the JWT token.\n\tfn jwt_secret Option>(\n\t\t&self,\n\t\tstate: &mut State,\n\t\tdecode_data: F\n\t) -> Option>;\n}\n\n/// An [AuthHandler] returning always the same secret. See [AuthMiddleware] for a usage example.\n#[derive(Clone, Debug)]\npub struct StaticAuthHandler {\n\tsecret: Vec\n}\n\nimpl StaticAuthHandler {\n\tpub fn from_vec(secret: Vec) -> Self {\n\t\tSelf { secret }\n\t}\n\n\tpub fn from_array(secret: &[u8]) -> Self {\n\t\tSelf::from_vec(secret.to_vec())\n\t}\n}\n\nimpl AuthHandler for StaticAuthHandler {\n\tfn jwt_secret Option>(\n\t\t&self,\n\t\t_state: &mut State,\n\t\t_decode_data: F\n\t) -> Option> {\n\t\tSome(self.secret.clone())\n\t}\n}\n\n/// This is the auth middleware. To use it, first make sure you have the `auth` feature enabled. Then\n/// simply add it to your pipeline and request it inside your handler:\n///\n/// ```rust,no_run\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # use gotham::{router::builder::*, pipeline::*, state::State};\n/// # use gotham_restful::*;\n/// # use serde::{Deserialize, Serialize};\n/// #\n/// #[derive(Resource)]\n/// #[resource(read_all)]\n/// struct AuthResource;\n///\n/// #[derive(Debug, Deserialize, Clone)]\n/// struct AuthData {\n/// \tsub: String,\n/// \texp: u64\n/// }\n///\n/// #[read_all]\n/// fn read_all(auth: &AuthStatus) -> Success {\n/// \tformat!(\"{auth:?}\").into()\n/// }\n///\n/// fn main() {\n/// \tlet auth: AuthMiddleware = AuthMiddleware::new(\n/// \t\tAuthSource::AuthorizationHeader,\n/// \t\tAuthValidation::default(),\n/// \t\tStaticAuthHandler::from_array(b\"zlBsA2QXnkmpe0QTh8uCvtAEa4j33YAc\")\n/// \t);\n/// \tlet (chain, pipelines) = single_pipeline(new_pipeline().add(auth).build());\n/// \tgotham::start(\n/// \t\t\"127.0.0.1:8080\",\n/// \t\tbuild_router(chain, pipelines, |route| {\n/// \t\t\troute.resource::(\"auth\");\n/// \t\t})\n/// \t);\n/// }\n/// ```\n#[derive(Debug)]\npub struct AuthMiddleware {\n\tsource: AuthSource,\n\tvalidation: AuthValidation,\n\thandler: Handler,\n\t_data: PhantomData\n}\n\nimpl Clone for AuthMiddleware\nwhere\n\tHandler: Clone\n{\n\tfn clone(&self) -> Self {\n\t\tSelf {\n\t\t\tsource: self.source.clone(),\n\t\t\tvalidation: self.validation.clone(),\n\t\t\thandler: self.handler.clone(),\n\t\t\t_data: self._data\n\t\t}\n\t}\n}\n\nimpl AuthMiddleware\nwhere\n\tData: DeserializeOwned + Send,\n\tHandler: AuthHandler + Default\n{\n\tpub fn from_source(source: AuthSource) -> Self {\n\t\tSelf {\n\t\t\tsource,\n\t\t\tvalidation: Default::default(),\n\t\t\thandler: Default::default(),\n\t\t\t_data: Default::default()\n\t\t}\n\t}\n}\n\nimpl AuthMiddleware\nwhere\n\tData: DeserializeOwned + Send,\n\tHandler: AuthHandler\n{\n\tpub fn new(source: AuthSource, validation: AuthValidation, handler: Handler) -> Self {\n\t\tSelf {\n\t\t\tsource,\n\t\t\tvalidation,\n\t\t\thandler,\n\t\t\t_data: Default::default()\n\t\t}\n\t}\n\n\tfn auth_status(&self, state: &mut State) -> AuthStatus {\n\t\t// extract the provided token, if any\n\t\tlet token = match &self.source {\n\t\t\tAuthSource::Cookie(name) => CookieJar::try_borrow_from(&state)\n\t\t\t\t.map(|jar| jar.get(&name).map(|cookie| cookie.value().to_owned()))\n\t\t\t\t.unwrap_or_else(|| {\n\t\t\t\t\tCookieParser::from_state(&state)\n\t\t\t\t\t\t.get(&name)\n\t\t\t\t\t\t.map(|cookie| cookie.value().to_owned())\n\t\t\t\t}),\n\t\t\tAuthSource::Header(name) => HeaderMap::try_borrow_from(&state)\n\t\t\t\t.and_then(|map| map.get(name))\n\t\t\t\t.and_then(|header| header.to_str().ok())\n\t\t\t\t.map(|value| value.to_owned()),\n\t\t\tAuthSource::AuthorizationHeader => HeaderMap::try_borrow_from(&state)\n\t\t\t\t.and_then(|map| map.get(AUTHORIZATION))\n\t\t\t\t.and_then(|header| header.to_str().ok())\n\t\t\t\t.and_then(|value| value.split_whitespace().nth(1))\n\t\t\t\t.map(|value| value.to_owned())\n\t\t};\n\n\t\t// unauthed if no token\n\t\tlet token = match token {\n\t\t\tSome(token) => token,\n\t\t\tNone => return AuthStatus::Unauthenticated\n\t\t};\n\n\t\t// get the secret from the handler, possibly decoding claims ourselves\n\t\tlet secret = self.handler.jwt_secret(state, || {\n\t\t\tlet b64 = token.split('.').nth(1)?;\n\t\t\tlet raw = BASE64_URL_SAFE_NO_PAD.decode(b64).ok()?;\n\t\t\tserde_json::from_slice(&raw).ok()?\n\t\t});\n\n\t\t// unknown if no secret\n\t\tlet secret = match secret {\n\t\t\tSome(secret) => secret,\n\t\t\tNone => return AuthStatus::Unknown\n\t\t};\n\n\t\t// validate the token\n\t\tlet data: Data = match jsonwebtoken::decode(\n\t\t\t&token,\n\t\t\t&DecodingKey::from_secret(&secret),\n\t\t\t&self.validation\n\t\t) {\n\t\t\tOk(data) => data.claims,\n\t\t\tErr(e) => return AuthStatus::Invalid(e)\n\t\t};\n\n\t\t// we found a valid token\n\t\tAuthStatus::Authenticated(data)\n\t}\n}\n\nimpl Middleware for AuthMiddleware\nwhere\n\tData: DeserializeOwned + Send + 'static,\n\tHandler: AuthHandler\n{\n\tfn call(self, mut state: State, chain: Chain) -> Pin>\n\twhere\n\t\tChain: FnOnce(State) -> Pin>\n\t{\n\t\t// put the source in our state, required for e.g. openapi\n\t\tstate.put(self.source.clone());\n\n\t\t// put the status in our state\n\t\tlet status = self.auth_status(&mut state);\n\t\tstate.put(status);\n\n\t\t// call the rest of the chain\n\t\tchain(state)\n\t\t\t.and_then(|(state, res)| future::ok((state, res)))\n\t\t\t.boxed()\n\t}\n}\n\nimpl NewMiddleware for AuthMiddleware\nwhere\n\tSelf: Clone + Middleware + Sync + RefUnwindSafe\n{\n\ttype Instance = Self;\n\n\tfn new_middleware(&self) -> anyhow::Result {\n\t\tlet c: Self = self.clone();\n\t\tOk(c)\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse cookie::Cookie;\n\tuse gotham::hyper::header::COOKIE;\n\tuse jsonwebtoken::errors::ErrorKind;\n\tuse std::fmt::Debug;\n\n\t// 256-bit random string\n\tconst JWT_SECRET: &'static [u8; 32] = b\"Lyzsfnta0cdxyF0T9y6VGxp3jpgoMUuW\";\n\n\t// some known tokens\n\tconst VALID_TOKEN: &'static str = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjQxMDI0NDQ4MDB9.8h8Ax-nnykqEQ62t7CxmM3ja6NzUQ4L0MLOOzddjLKk\";\n\tconst EXPIRED_TOKEN: &'static str = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjE1Nzc4MzcxMDB9.eV1snaGLYrJ7qUoMk74OvBY3WUU9M0Je5HTU2xtX1v0\";\n\tconst INVALID_TOKEN: &'static str = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtc3JkMCIsInN1YiI6ImdvdGhhbS1yZXN0ZnVsIiwiaWF0IjoxNTc3ODM2ODAwLCJleHAiOjQxMDI0NDQ4MDB9\";\n\n\t#[derive(Debug, Deserialize, PartialEq)]\n\tstruct TestData {\n\t\tiss: String,\n\t\tsub: String,\n\t\tiat: u64,\n\t\texp: u64\n\t}\n\n\timpl Default for TestData {\n\t\tfn default() -> Self {\n\t\t\tSelf {\n\t\t\t\tiss: \"msrd0\".to_owned(),\n\t\t\t\tsub: \"gotham-restful\".to_owned(),\n\t\t\t\tiat: 1577836800,\n\t\t\t\texp: 4102444800\n\t\t\t}\n\t\t}\n\t}\n\n\t#[derive(Default)]\n\tstruct NoneAuthHandler;\n\timpl AuthHandler for NoneAuthHandler {\n\t\tfn jwt_secret Option>(\n\t\t\t&self,\n\t\t\t_state: &mut State,\n\t\t\t_decode_data: F\n\t\t) -> Option> {\n\t\t\tNone\n\t\t}\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_none_secret() {\n\t\tlet middleware = >::from_source(\n\t\t\tAuthSource::AuthorizationHeader\n\t\t);\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tAUTHORIZATION,\n\t\t\t\tformat!(\"Bearer {VALID_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tmiddleware.auth_status(&mut state);\n\t\t});\n\t}\n\n\t#[derive(Default)]\n\tstruct TestAssertingHandler;\n\timpl AuthHandler for TestAssertingHandler\n\twhere\n\t\tT: Debug + Default + PartialEq\n\t{\n\t\tfn jwt_secret Option>(\n\t\t\t&self,\n\t\t\t_state: &mut State,\n\t\t\tdecode_data: F\n\t\t) -> Option> {\n\t\t\tassert_eq!(decode_data(), Some(T::default()));\n\t\t\tSome(JWT_SECRET.to_vec())\n\t\t}\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_decode_data() {\n\t\tlet middleware = >::from_source(\n\t\t\tAuthSource::AuthorizationHeader\n\t\t);\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tAUTHORIZATION,\n\t\t\t\tformat!(\"Bearer {VALID_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tmiddleware.auth_status(&mut state);\n\t\t});\n\t}\n\n\tfn new_middleware(source: AuthSource) -> AuthMiddleware\n\twhere\n\t\tT: DeserializeOwned + Send\n\t{\n\t\tAuthMiddleware::new(\n\t\t\tsource,\n\t\t\tDefault::default(),\n\t\t\tStaticAuthHandler::from_array(JWT_SECRET)\n\t\t)\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_no_token() {\n\t\tlet middleware = new_middleware::(AuthSource::AuthorizationHeader);\n\t\tState::with_new(|mut state| {\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Unauthenticated => {},\n\t\t\t\t_ => panic!(\"Expected AuthStatus::Unauthenticated, got {status:?}\")\n\t\t\t};\n\t\t});\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_expired_token() {\n\t\tlet middleware = new_middleware::(AuthSource::AuthorizationHeader);\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tAUTHORIZATION,\n\t\t\t\tformat!(\"Bearer {EXPIRED_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Invalid(err) if *err.kind() == ErrorKind::ExpiredSignature => {},\n\t\t\t\t_ => panic!(\n\t\t\t\t\t\"Expected AuthStatus::Invalid(..) with ErrorKind::ExpiredSignature, got {status:?}\"\n\t\t\t\t)\n\t\t\t};\n\t\t});\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_invalid_token() {\n\t\tlet middleware = new_middleware::(AuthSource::AuthorizationHeader);\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tAUTHORIZATION,\n\t\t\t\tformat!(\"Bearer {INVALID_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Invalid(err) if *err.kind() == ErrorKind::InvalidToken => {},\n\t\t\t\t_ => panic!(\n\t\t\t\t\t\"Expected AuthStatus::Invalid(..) with ErrorKind::InvalidToken, got {status:?}\"\n\t\t\t\t)\n\t\t\t};\n\t\t});\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_auth_header_token() {\n\t\tlet middleware = new_middleware::(AuthSource::AuthorizationHeader);\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tAUTHORIZATION,\n\t\t\t\tformat!(\"Bearer {VALID_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Authenticated(data) => assert_eq!(data, TestData::default()),\n\t\t\t\t_ => panic!(\"Expected AuthStatus::Authenticated, got {status:?}\")\n\t\t\t};\n\t\t})\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_header_token() {\n\t\tlet header_name = \"x-znoiprwmvfexju\";\n\t\tlet middleware =\n\t\t\tnew_middleware::(AuthSource::Header(HeaderName::from_static(header_name)));\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(header_name, VALID_TOKEN.parse().unwrap());\n\t\t\tstate.put(headers);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Authenticated(data) => assert_eq!(data, TestData::default()),\n\t\t\t\t_ => panic!(\"Expected AuthStatus::Authenticated, got {status:?}\")\n\t\t\t};\n\t\t})\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_cookie_token() {\n\t\tlet cookie_name = \"znoiprwmvfexju\";\n\t\tlet middleware = new_middleware::(AuthSource::Cookie(cookie_name.to_owned()));\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut jar = CookieJar::new();\n\t\t\tjar.add_original(Cookie::new(cookie_name, VALID_TOKEN));\n\t\t\tstate.put(jar);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Authenticated(data) => assert_eq!(data, TestData::default()),\n\t\t\t\t_ => panic!(\"Expected AuthStatus::Authenticated, got {status:?}\")\n\t\t\t};\n\t\t})\n\t}\n\n\t#[test]\n\tfn test_auth_middleware_cookie_no_jar() {\n\t\tlet cookie_name = \"znoiprwmvfexju\";\n\t\tlet middleware = new_middleware::(AuthSource::Cookie(cookie_name.to_owned()));\n\t\tState::with_new(|mut state| {\n\t\t\tlet mut headers = HeaderMap::new();\n\t\t\theaders.insert(\n\t\t\t\tCOOKIE,\n\t\t\t\tformat!(\"{cookie_name}={VALID_TOKEN}\").parse().unwrap()\n\t\t\t);\n\t\t\tstate.put(headers);\n\t\t\tlet status = middleware.auth_status(&mut state);\n\t\t\tmatch status {\n\t\t\t\tAuthStatus::Authenticated(data) => assert_eq!(data, TestData::default()),\n\t\t\t\t_ => panic!(\"Expected AuthStatus::Authenticated, got {status:?}\")\n\t\t\t};\n\t\t})\n\t}\n}\n","traces":[{"line":47,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":49,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":51,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":52,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":53,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":59,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":60,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":61,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":62,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[7949776],"length":1,"stats":{"Line":3},"fn_name":null},{"line":122,"address":[7949808],"length":1,"stats":{"Line":3},"fn_name":null},{"line":123,"address":[7949831],"length":1,"stats":{"Line":3},"fn_name":null},{"line":128,"address":[6229197,6229152],"length":1,"stats":{"Line":2},"fn_name":"jwt_secret>"},{"line":133,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":188,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":190,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":191,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":192,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":193,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":203,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":206,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":207,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":208,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":218,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":223,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":227,"address":[],"length":0,"stats":{"Line":5},"fn_name":null},{"line":229,"address":[],"length":0,"stats":{"Line":5},"fn_name":null},{"line":230,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":231,"address":[],"length":0,"stats":{"Line":5},"fn_name":null},{"line":232,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":233,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":234,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":235,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":237,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":238,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":239,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":240,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":241,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":242,"address":[],"length":0,"stats":{"Line":8},"fn_name":null},{"line":243,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":244,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":245,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":249,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":250,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":251,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":255,"address":[],"length":0,"stats":{"Line":5},"fn_name":null},{"line":256,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":257,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":258,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":262,"address":[],"length":0,"stats":{"Line":4},"fn_name":null},{"line":263,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":264,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":268,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":269,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":270,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":271,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":273,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":274,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":278,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":287,"address":[7242014,7244384,7243920,7242512,7242976,7243899,7243860,7245278,7244814,7242478,7245312,7242048,7244350,7245742,7243406,7241584,7242942,7243440,7244848],"length":1,"stats":{"Line":1},"fn_name":"call, gotham_restful::routing::MaybeMatchAcceptHeader>, (borrow_bag::handle::Handle, ())>, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::auth::AuthMiddleware, ())>, ()), gotham_restful::endpoint::NoopExtractor, gotham_restful::endpoint::NoopExtractor, fn(&mut gotham::state::State) -> gotham_restful::routing::endpoint_handler::{async_fn_env#0}>, (borrow_bag::handle::Handle, ())>, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::auth::AuthMiddleware, ())>, ())>>"},{"line":292,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":295,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":296,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":299,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":300,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":311,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":312,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":313,"address":[],"length":0,"stats":{"Line":1},"fn_name":null}],"covered":58,"coverable":71},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","cors.rs"],"content":"use gotham::{\n\thandler::HandlerFuture,\n\thelpers::http::response::create_empty_response,\n\thyper::{\n\t\theader::{\n\t\t\tHeaderMap, HeaderName, HeaderValue, ACCESS_CONTROL_ALLOW_CREDENTIALS,\n\t\t\tACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS,\n\t\t\tACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_MAX_AGE, ACCESS_CONTROL_REQUEST_HEADERS,\n\t\t\tACCESS_CONTROL_REQUEST_METHOD, ORIGIN, VARY\n\t\t},\n\t\tBody, Method, Response, StatusCode\n\t},\n\tmiddleware::Middleware,\n\tpipeline::PipelineHandleChain,\n\tprelude::*,\n\trouter::{builder::ExtendRouteMatcher, route::matcher::AccessControlRequestMethodMatcher},\n\tstate::State\n};\nuse std::{panic::RefUnwindSafe, pin::Pin};\n\n/// Specify the allowed origins of the request. It is up to the browser to check the validity of the\n/// origin. This, when sent to the browser, will indicate whether or not the request's origin was\n/// allowed to make the request.\n#[derive(Clone, Debug)]\npub enum Origin {\n\t/// Do not send any `Access-Control-Allow-Origin` headers.\n\tNone,\n\t/// Send `Access-Control-Allow-Origin: *`. Note that browser will not send credentials.\n\tStar,\n\t/// Set the `Access-Control-Allow-Origin` header to a single origin.\n\tSingle(String),\n\t/// Copy the `Origin` header into the `Access-Control-Allow-Origin` header.\n\tCopy\n}\n\nimpl Default for Origin {\n\tfn default() -> Self {\n\t\tSelf::None\n\t}\n}\n\nimpl Origin {\n\t/// Get the header value for the `Access-Control-Allow-Origin` header.\n\tfn header_value(&self, state: &State) -> Option {\n\t\tmatch self {\n\t\t\tSelf::None => None,\n\t\t\tSelf::Star => Some(\"*\".parse().unwrap()),\n\t\t\tSelf::Single(origin) => Some(origin.parse().unwrap()),\n\t\t\tSelf::Copy => {\n\t\t\t\tlet headers = HeaderMap::borrow_from(state);\n\t\t\t\theaders.get(ORIGIN).map(Clone::clone)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns true if the `Vary` header has to include `Origin`.\n\tfn varies(&self) -> bool {\n\t\tmatches!(self, Self::Copy)\n\t}\n}\n\n/// Specify the allowed headers of the request. It is up to the browser to check that only the allowed\n/// headers are sent with the request.\n#[derive(Clone, Debug)]\npub enum Headers {\n\t/// Do not send any `Access-Control-Allow-Headers` headers.\n\tNone,\n\t/// Set the `Access-Control-Allow-Headers` header to the following header list. If empty, this\n\t/// is treated as if it was [None].\n\tList(Vec),\n\t/// Copy the `Access-Control-Request-Headers` header into the `Access-Control-Allow-Header`\n\t/// header.\n\tCopy\n}\n\nimpl Default for Headers {\n\tfn default() -> Self {\n\t\tSelf::None\n\t}\n}\n\nimpl Headers {\n\t/// Get the header value for the `Access-Control-Allow-Headers` header.\n\tfn header_value(&self, state: &State) -> Option {\n\t\tmatch self {\n\t\t\tSelf::None => None,\n\t\t\tSelf::List(list) => Some(list.join(\",\").parse().unwrap()),\n\t\t\tSelf::Copy => {\n\t\t\t\tlet headers = HeaderMap::borrow_from(state);\n\t\t\t\theaders\n\t\t\t\t\t.get(ACCESS_CONTROL_REQUEST_HEADERS)\n\t\t\t\t\t.map(Clone::clone)\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Returns true if the `Vary` header has to include `Origin`.\n\tfn varies(&self) -> bool {\n\t\tmatches!(self, Self::Copy)\n\t}\n}\n\n/// This is the configuration that the CORS handler will follow. Its default configuration is basically\n/// not to touch any responses, resulting in the browser's default behaviour.\n///\n/// To change settings, you need to put this type into gotham's [State]:\n///\n/// ```rust,no_run\n/// # use gotham::{router::builder::*, pipeline::*, state::State};\n/// # use gotham_restful::{*, cors::Origin};\n/// # #[cfg_attr(feature = \"cargo-clippy\", allow(clippy::needless_doctest_main))]\n/// fn main() {\n/// \tlet cors = CorsConfig {\n/// \t\torigin: Origin::Star,\n/// \t\t..Default::default()\n/// \t};\n/// \tlet (chain, pipelines) = single_pipeline(new_pipeline().add(cors).build());\n/// \tgotham::start(\n/// \t\t\"127.0.0.1:8080\",\n/// \t\tbuild_router(chain, pipelines, |route| {\n/// \t\t\t// your routing logic\n/// \t\t})\n/// \t);\n/// }\n/// ```\n///\n/// This easy approach allows you to have one global cors configuration. If you prefer to have separate\n/// configurations for different scopes, you need to register the middleware inside your routing logic:\n///\n/// ```rust,no_run\n/// # use gotham::{router::builder::*, pipeline::*, state::State};\n/// # use gotham_restful::{*, cors::Origin};\n/// let pipelines = new_pipeline_set();\n///\n/// // The first cors configuration\n/// let cors_a = CorsConfig {\n/// \torigin: Origin::Star,\n/// \t..Default::default()\n/// };\n/// let (pipelines, chain_a) = pipelines.add(new_pipeline().add(cors_a).build());\n///\n/// // The second cors configuration\n/// let cors_b = CorsConfig {\n/// \torigin: Origin::Copy,\n/// \t..Default::default()\n/// };\n/// let (pipelines, chain_b) = pipelines.add(new_pipeline().add(cors_b).build());\n///\n/// let pipeline_set = finalize_pipeline_set(pipelines);\n/// gotham::start(\n/// \t\"127.0.0.1:8080\",\n/// \tbuild_router((), pipeline_set, |route| {\n/// \t\t// routing without any cors config\n/// \t\troute.with_pipeline_chain((chain_a, ()), |route| {\n/// \t\t\t// routing with cors config a\n/// \t\t});\n/// \t\troute.with_pipeline_chain((chain_b, ()), |route| {\n/// \t\t\t// routing with cors config b\n/// \t\t});\n/// \t})\n/// );\n/// ```\n#[derive(Clone, Debug, Default, NewMiddleware, StateData)]\npub struct CorsConfig {\n\t/// The allowed origins.\n\tpub origin: Origin,\n\t/// The allowed headers.\n\tpub headers: Headers,\n\t/// The amount of seconds that the preflight request can be cached.\n\tpub max_age: u64,\n\t/// Whether or not the request may be made with supplying credentials.\n\tpub credentials: bool\n}\n\nimpl Middleware for CorsConfig {\n\tfn call(self, mut state: State, chain: Chain) -> Pin>\n\twhere\n\t\tChain: FnOnce(State) -> Pin>\n\t{\n\t\tstate.put(self);\n\t\tchain(state)\n\t}\n}\n\n/// Handle CORS for a non-preflight request. This means manipulating the `res` HTTP headers so that\n/// the response is aligned with the `state`'s [CorsConfig].\n///\n/// If you are using the [Resource](crate::Resource) type (which is the recommended way), you'll never\n/// have to call this method. However, if you are writing your own handler method, you might want to\n/// call this after your request to add the required CORS headers.\n///\n/// For further information on CORS, read .\npub fn handle_cors(state: &State, res: &mut Response) {\n\tlet config = CorsConfig::try_borrow_from(state);\n\tif let Some(cfg) = config {\n\t\tlet headers = res.headers_mut();\n\n\t\t// non-preflight requests require the Access-Control-Allow-Origin header\n\t\tif let Some(header) = cfg.origin.header_value(state) {\n\t\t\theaders.insert(ACCESS_CONTROL_ALLOW_ORIGIN, header);\n\t\t}\n\n\t\t// if the origin is copied over, we should tell the browser by specifying the Vary header\n\t\tif cfg.origin.varies() {\n\t\t\tlet vary = headers\n\t\t\t\t.get(VARY)\n\t\t\t\t.map(|vary| format!(\"{},origin\", vary.to_str().unwrap()));\n\t\t\theaders.insert(VARY, vary.as_deref().unwrap_or(\"origin\").parse().unwrap());\n\t\t}\n\n\t\t// if we allow credentials, tell the browser\n\t\tif cfg.credentials {\n\t\t\theaders.insert(\n\t\t\t\tACCESS_CONTROL_ALLOW_CREDENTIALS,\n\t\t\t\tHeaderValue::from_static(\"true\")\n\t\t\t);\n\t\t}\n\t}\n}\n\n/// Add CORS routing for your path. This is required for handling preflight requests.\n///\n/// Example:\n///\n/// ```rust,no_run\n/// # use gotham::{hyper::{Body, Method, Response}, router::builder::*};\n/// # use gotham_restful::*;\n/// build_simple_router(|router| {\n/// \t// The handler that needs preflight handling\n/// \trouter.post(\"/foo\").to(|state| {\n/// \t\tlet mut res: Response = unimplemented!();\n/// \t\thandle_cors(&state, &mut res);\n/// \t\t(state, res)\n/// \t});\n/// \t// Add preflight handling\n/// \trouter.cors(\"/foo\", Method::POST);\n/// });\n/// ```\npub trait CorsRoute\nwhere\n\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\tP: RefUnwindSafe + Send + Sync + 'static\n{\n\t/// Handle a preflight request on `path` for `method`. To configure the behaviour, use\n\t/// [CorsConfig].\n\tfn cors(&mut self, path: &str, method: Method);\n}\n\npub(crate) fn cors_preflight_handler(state: State) -> (State, Response) {\n\tlet config = CorsConfig::try_borrow_from(&state);\n\n\t// prepare the response\n\tlet mut res = create_empty_response(&state, StatusCode::NO_CONTENT);\n\tlet headers = res.headers_mut();\n\tlet mut vary: Vec = Vec::new();\n\n\t// copy the request method over to the response\n\tlet method = HeaderMap::borrow_from(&state)\n\t\t.get(ACCESS_CONTROL_REQUEST_METHOD)\n\t\t.unwrap()\n\t\t.clone();\n\theaders.insert(ACCESS_CONTROL_ALLOW_METHODS, method);\n\tvary.push(ACCESS_CONTROL_REQUEST_METHOD);\n\n\tif let Some(cfg) = config {\n\t\t// if we allow any headers, copy them over\n\t\tif let Some(header) = cfg.headers.header_value(&state) {\n\t\t\theaders.insert(ACCESS_CONTROL_ALLOW_HEADERS, header);\n\t\t}\n\n\t\t// if the headers are copied over, we should tell the browser by specifying the Vary header\n\t\tif cfg.headers.varies() {\n\t\t\tvary.push(ACCESS_CONTROL_REQUEST_HEADERS);\n\t\t}\n\n\t\t// set the max age for the preflight cache\n\t\tif let Some(age) = config.map(|cfg| cfg.max_age) {\n\t\t\theaders.insert(ACCESS_CONTROL_MAX_AGE, age.into());\n\t\t}\n\t}\n\n\t// make sure the browser knows that this request was based on the method\n\theaders.insert(VARY, vary.join(\",\").parse().unwrap());\n\n\thandle_cors(&state, &mut res);\n\t(state, res)\n}\n\nimpl CorsRoute for D\nwhere\n\tD: DrawRoutes,\n\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\tP: RefUnwindSafe + Send + Sync + 'static\n{\n\tfn cors(&mut self, path: &str, method: Method) {\n\t\tlet matcher = AccessControlRequestMethodMatcher::new(method);\n\t\tself.options(path)\n\t\t\t.extend_route_matcher(matcher)\n\t\t\t.to(cors_preflight_handler);\n\t}\n}\n","traces":[{"line":37,"address":[7758688],"length":1,"stats":{"Line":2},"fn_name":"default"},{"line":38,"address":[7758691],"length":1,"stats":{"Line":2},"fn_name":null},{"line":44,"address":[7758704],"length":1,"stats":{"Line":1},"fn_name":null},{"line":45,"address":[7758746],"length":1,"stats":{"Line":1},"fn_name":null},{"line":46,"address":[7758782],"length":1,"stats":{"Line":1},"fn_name":null},{"line":47,"address":[7758791],"length":1,"stats":{"Line":1},"fn_name":null},{"line":48,"address":[7758867],"length":1,"stats":{"Line":1},"fn_name":null},{"line":50,"address":[7758953],"length":1,"stats":{"Line":1},"fn_name":null},{"line":51,"address":[7758970],"length":1,"stats":{"Line":1},"fn_name":null},{"line":57,"address":[7759072],"length":1,"stats":{"Line":1},"fn_name":null},{"line":58,"address":[7759077],"length":1,"stats":{"Line":1},"fn_name":null},{"line":77,"address":[7759120],"length":1,"stats":{"Line":2},"fn_name":"default"},{"line":78,"address":[7759123],"length":1,"stats":{"Line":2},"fn_name":null},{"line":84,"address":[7759136,7759569],"length":1,"stats":{"Line":1},"fn_name":null},{"line":85,"address":[7759179],"length":1,"stats":{"Line":1},"fn_name":null},{"line":86,"address":[7759216],"length":1,"stats":{"Line":1},"fn_name":null},{"line":87,"address":[7759435,7759230],"length":1,"stats":{"Line":1},"fn_name":null},{"line":89,"address":[7759317],"length":1,"stats":{"Line":1},"fn_name":null},{"line":90,"address":[7759394],"length":1,"stats":{"Line":1},"fn_name":null},{"line":91,"address":[7759334],"length":1,"stats":{"Line":1},"fn_name":null},{"line":98,"address":[7759600],"length":1,"stats":{"Line":1},"fn_name":null},{"line":99,"address":[7759605],"length":1,"stats":{"Line":1},"fn_name":null},{"line":176,"address":[6413296,6412848,6413072,6413253,6413029,6413057,6413477,6413281,6413505],"length":1,"stats":{"Line":3},"fn_name":"call, gotham_restful::routing::MaybeMatchAcceptHeader>, (borrow_bag::handle::Handle, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::cors::CorsConfig, ())>, ()), gotham_restful::endpoint::NoopExtractor, gotham_restful::endpoint::NoopExtractor, fn(&mut gotham::state::State) -> gotham_restful::routing::endpoint_handler::{async_fn_env#0}>, (borrow_bag::handle::Handle, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::cors::CorsConfig, ())>, ())>>"},{"line":180,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":181,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":193,"address":[7760529,7759648,7760477],"length":1,"stats":{"Line":5},"fn_name":"handle_cors"},{"line":194,"address":[7759681],"length":1,"stats":{"Line":5},"fn_name":null},{"line":195,"address":[7759716],"length":1,"stats":{"Line":5},"fn_name":null},{"line":196,"address":[7759765],"length":1,"stats":{"Line":1},"fn_name":null},{"line":199,"address":[7759851,7759794],"length":1,"stats":{"Line":2},"fn_name":null},{"line":200,"address":[7759884],"length":1,"stats":{"Line":1},"fn_name":null},{"line":204,"address":[7760030],"length":1,"stats":{"Line":1},"fn_name":null},{"line":205,"address":[7760113],"length":1,"stats":{"Line":1},"fn_name":null},{"line":206,"address":[7760083],"length":1,"stats":{"Line":1},"fn_name":null},{"line":207,"address":[7760752,7760790,7760894],"length":1,"stats":{"Line":3},"fn_name":"{closure#0}"},{"line":208,"address":[7760458,7760160],"length":1,"stats":{"Line":1},"fn_name":null},{"line":212,"address":[7760063],"length":1,"stats":{"Line":1},"fn_name":null},{"line":213,"address":[7760648],"length":1,"stats":{"Line":1},"fn_name":null},{"line":214,"address":[7760544],"length":1,"stats":{"Line":1},"fn_name":null},{"line":215,"address":[7760582],"length":1,"stats":{"Line":1},"fn_name":null},{"line":249,"address":[7763023,7762412,7761008],"length":1,"stats":{"Line":1},"fn_name":"cors_preflight_handler"},{"line":250,"address":[7761044,7761165],"length":1,"stats":{"Line":2},"fn_name":null},{"line":253,"address":[7761173],"length":1,"stats":{"Line":1},"fn_name":null},{"line":254,"address":[7761205,7761275],"length":1,"stats":{"Line":2},"fn_name":null},{"line":255,"address":[7761283],"length":1,"stats":{"Line":1},"fn_name":null},{"line":258,"address":[7761307,7761399],"length":1,"stats":{"Line":2},"fn_name":null},{"line":259,"address":[7761369],"length":1,"stats":{"Line":1},"fn_name":null},{"line":262,"address":[7761476],"length":1,"stats":{"Line":1},"fn_name":null},{"line":263,"address":[7761608],"length":1,"stats":{"Line":1},"fn_name":null},{"line":265,"address":[7761665],"length":1,"stats":{"Line":1},"fn_name":null},{"line":267,"address":[7761728,7761805],"length":1,"stats":{"Line":2},"fn_name":null},{"line":268,"address":[7761899,7761991],"length":1,"stats":{"Line":2},"fn_name":null},{"line":272,"address":[7762033,7762067],"length":1,"stats":{"Line":2},"fn_name":null},{"line":273,"address":[7762102],"length":1,"stats":{"Line":1},"fn_name":null},{"line":277,"address":[7762073,7763056,7763061,7762171],"length":1,"stats":{"Line":4},"fn_name":"{closure#0}"},{"line":278,"address":[7762218],"length":1,"stats":{"Line":1},"fn_name":null},{"line":283,"address":[7761735,7762423],"length":1,"stats":{"Line":1},"fn_name":null},{"line":285,"address":[7762800],"length":1,"stats":{"Line":1},"fn_name":null},{"line":286,"address":[7762824],"length":1,"stats":{"Line":1},"fn_name":null},{"line":295,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":296,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":297,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":298,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":299,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":59,"coverable":64},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","endpoint.rs"],"content":"use crate::{IntoResponse, RequestBody};\nuse futures_util::future::BoxFuture;\nuse gotham::{\n\textractor::{PathExtractor, QueryStringExtractor},\n\thyper::{Body, Method, Response},\n\trouter::response::StaticResponseExtender,\n\tstate::{State, StateData}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiType, Visitor};\nuse serde::{Deserialize, Deserializer};\nuse std::borrow::Cow;\n\n/// A no-op extractor that can be used as a default type for [Endpoint::Placeholders] and\n/// [Endpoint::Params].\n#[derive(Debug, Clone, Copy)]\npub struct NoopExtractor;\n\nimpl<'de> Deserialize<'de> for NoopExtractor {\n\tfn deserialize>(_: D) -> Result {\n\t\tOk(Self)\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl OpenapiType for NoopExtractor {\n\tfn visit_type(visitor: &mut V) {\n\t\twarn!(\n\t\t\t\"You're asking for the OpenAPI Schema for gotham_restful::NoopExtractor. This is probably not what you want.\"\n\t\t);\n\t\tvisitor.visit_unit();\n\t}\n}\n\nimpl StateData for NoopExtractor {}\n\nimpl StaticResponseExtender for NoopExtractor {\n\ttype ResBody = Body;\n\tfn extend(_: &mut State, _: &mut Response) {}\n}\n\n// TODO: Specify default types once https://github.com/rust-lang/rust/issues/29661 lands.\n#[_private_openapi_trait(EndpointWithSchema)]\npub trait Endpoint {\n\t/// The HTTP Verb of this endpoint.\n\tfn http_method() -> Method;\n\t/// The URI that this endpoint listens on in gotham's format.\n\tfn uri() -> Cow<'static, str>;\n\n\t/// The verb used for generating an operation id if [Self::operation_id] returns [None].\n\t/// For example `read`, `read_all`, `create`, `update` etc.\n\t#[openapi_only]\n\tfn operation_verb() -> Option<&'static str>;\n\n\t/// The output type that provides the response.\n\t#[openapi_bound(Output: crate::ResponseSchema)]\n\ttype Output: IntoResponse + Send;\n\n\t/// Returns `true` _iff_ the URI contains placeholders. `false` by default.\n\tfn has_placeholders() -> bool {\n\t\tfalse\n\t}\n\t/// The type that parses the URI placeholders. Use [NoopExtractor] if `has_placeholders()`\n\t/// returns `false`.\n\t#[openapi_bound(Placeholders: OpenapiType)]\n\ttype Placeholders: PathExtractor + Clone + Sync;\n\n\t/// Returns `true` _iff_ the request parameters should be parsed. `false` by default.\n\tfn needs_params() -> bool {\n\t\tfalse\n\t}\n\t/// The type that parses the request parameters. Use [NoopExtractor] if `needs_params()`\n\t/// returns `false`.\n\t#[openapi_bound(Params: OpenapiType)]\n\ttype Params: QueryStringExtractor + Clone + Sync;\n\n\t/// Returns `true` _iff_ the request body should be parsed. `false` by default.\n\tfn needs_body() -> bool {\n\t\tfalse\n\t}\n\t/// The type to parse the body into. Use `()` if `needs_body()` returns `false`.\n\ttype Body: RequestBody + Send;\n\n\t/// Returns `true` if the request wants to know the auth status of the client. `false` by default.\n\tfn wants_auth() -> bool {\n\t\tfalse\n\t}\n\n\t/// Replace the automatically generated operation id with a custom one. Only relevant for the\n\t/// OpenAPI Specification.\n\t#[openapi_only]\n\tfn operation_id() -> Option {\n\t\tNone\n\t}\n\n\t/// Add a description to the openapi specification. Usually taken from the rustdoc comment\n\t/// when using the proc macro.\n\t#[openapi_only]\n\tfn description() -> Option {\n\t\tNone\n\t}\n\n\t/// The handler for this endpoint.\n\tfn handle(\n\t\tstate: &mut State,\n\t\tplaceholders: Self::Placeholders,\n\t\tparams: Self::Params,\n\t\tbody: Option\n\t) -> BoxFuture<'_, Self::Output>;\n}\n\n#[cfg(feature = \"openapi\")]\nimpl Endpoint for E {\n\tfn http_method() -> Method {\n\t\tE::http_method()\n\t}\n\tfn uri() -> Cow<'static, str> {\n\t\tE::uri()\n\t}\n\n\ttype Output = E::Output;\n\n\tfn has_placeholders() -> bool {\n\t\tE::has_placeholders()\n\t}\n\ttype Placeholders = E::Placeholders;\n\n\tfn needs_params() -> bool {\n\t\tE::needs_params()\n\t}\n\ttype Params = E::Params;\n\n\tfn needs_body() -> bool {\n\t\tE::needs_body()\n\t}\n\ttype Body = E::Body;\n\n\tfn wants_auth() -> bool {\n\t\tE::wants_auth()\n\t}\n\n\tfn handle<'a>(\n\t\tstate: &'a mut State,\n\t\tplaceholders: Self::Placeholders,\n\t\tparams: Self::Params,\n\t\tbody: Option\n\t) -> BoxFuture<'a, Self::Output> {\n\t\tE::handle(state, placeholders, params, body)\n\t}\n}\n","traces":[{"line":20,"address":[7202960,7202976],"length":1,"stats":{"Line":10},"fn_name":"deserialize>, gotham::extractor::internal::from_query_string_mapping::{closure_env#0}>, &alloc::vec::Vec, gotham::helpers::http::FormUrlDecoded>>>"},{"line":21,"address":[6821545,6821571],"length":1,"stats":{"Line":10},"fn_name":null},{"line":27,"address":[7630080],"length":1,"stats":{"Line":0},"fn_name":"visit_type"},{"line":28,"address":[7630094,7630204,7630154],"length":1,"stats":{"Line":0},"fn_name":null},{"line":31,"address":[7630193],"length":1,"stats":{"Line":0},"fn_name":null},{"line":39,"address":[7630272,7630282],"length":1,"stats":{"Line":0},"fn_name":"extend"},{"line":60,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":61,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":69,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":70,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":78,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":79,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":85,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":86,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[6619680],"length":1,"stats":{"Line":6},"fn_name":"operation_id"},{"line":93,"address":[6619683],"length":1,"stats":{"Line":6},"fn_name":null},{"line":99,"address":[6619664],"length":1,"stats":{"Line":7},"fn_name":"description"},{"line":100,"address":[6490803,6490819],"length":1,"stats":{"Line":7},"fn_name":null},{"line":114,"address":[6504336,6504368],"length":1,"stats":{"Line":30},"fn_name":"http_method"},{"line":115,"address":[6504376,6504344],"length":1,"stats":{"Line":30},"fn_name":null},{"line":117,"address":[6629792],"length":1,"stats":{"Line":30},"fn_name":"uri"},{"line":118,"address":[6629800],"length":1,"stats":{"Line":30},"fn_name":null},{"line":123,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":129,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[6629744],"length":1,"stats":{"Line":21},"fn_name":"needs_body"},{"line":134,"address":[6629745],"length":1,"stats":{"Line":21},"fn_name":null},{"line":138,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":139,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[6629824],"length":1,"stats":{"Line":21},"fn_name":"handle"},{"line":148,"address":[6629844],"length":1,"stats":{"Line":21},"fn_name":null}],"covered":14,"coverable":32},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","lib.rs"],"content":"#![warn(missing_debug_implementations, rust_2018_idioms, unreachable_pub)]\n#![forbid(unsafe_code)]\n// deny warnings in CI\n#![cfg_attr(gotham_restful_deny_warnings, deny(warnings))]\n// clippy doesn't like our code style\n#![cfg_attr(feature = \"cargo-clippy\", allow(clippy::tabs_in_doc_comments))]\n// intra-doc links only fully work when OpenAPI is enabled\n#![cfg_attr(feature = \"openapi\", deny(rustdoc::broken_intra_doc_links))]\n#![cfg_attr(not(feature = \"openapi\"), allow(rustdoc::broken_intra_doc_links))]\n\n//! This crate is an extension to the popular [gotham web framework][gotham] for Rust. It allows you to\n//! create resources with assigned endpoints that aim to be a more convenient way of creating handlers\n//! for requests.\n//!\n//! # Features\n//!\n//! - Automatically parse **JSON** request and produce response bodies\n//! - Allow using **raw** request and response bodies\n//! - Convenient **macros** to create responses that can be registered with gotham's router\n//! - Auto-Generate an **OpenAPI** specification for your API\n//! - Manage **CORS** headers so you don't have to\n//! - Manage **Authentication** with JWT\n//! - Integrate diesel connection pools for easy **database** integration\n//!\n//! # Safety\n//!\n//! This crate is just as safe as you'd expect from anything written in safe Rust - and\n//! `#![forbid(unsafe_code)]` ensures that no unsafe was used.\n//!\n//! # Endpoints\n//!\n//! There are a set of pre-defined endpoints that should cover the majority of REST APIs. However,\n//! it is also possible to define your own endpoints.\n//!\n//! ## Pre-defined Endpoints\n//!\n//! Assuming you assign `/foobar` to your resource, the following pre-defined endpoints exist:\n//!\n//! | Endpoint Name | Required Arguments | HTTP Verb | HTTP Path |\n//! | ------------- | ------------------ | --------- | -------------- |\n//! | read_all | | GET | /foobar |\n//! | read | id | GET | /foobar/:id |\n//! | search | query | GET | /foobar/search |\n//! | create | body | POST | /foobar |\n//! | update_all | body | PUT | /foobar |\n//! | update | id, body | PUT | /foobar/:id |\n//! | delete_all | | DELETE | /foobar |\n//! | delete | id | DELETE | /foobar/:id |\n//!\n//! Each of those endpoints has a macro that creates the neccessary boilerplate for the Resource. A\n//! simple example looks like this:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # use gotham::router::builder::*;\n//! # use gotham_restful::*;\n//! # use serde::{Deserialize, Serialize};\n//! /// Our RESTful resource.\n//! #[derive(Resource)]\n//! #[resource(read)]\n//! struct FooResource;\n//!\n//! /// The return type of the foo read endpoint.\n//! #[derive(Serialize)]\n//! # #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n//! struct Foo {\n//! \tid: u64\n//! }\n//!\n//! /// The foo read endpoint.\n//! #[read]\n//! fn read(id: u64) -> Success {\n//! \tFoo { id }.into()\n//! }\n//! # fn main() {\n//! # \tgotham::start(\"127.0.0.1:8080\", build_simple_router(|route| {\n//! # \t\troute.resource::(\"foo\");\n//! # \t})).expect(\"Failed to start gotham\");\n//! # }\n//! ```\n//!\n//! ## Custom Endpoints\n//!\n//! Defining custom endpoints is done with the `#[endpoint]` macro. The syntax is similar to that\n//! of the pre-defined endpoints, but you need to give it more context:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # use gotham::{router::build_simple_router, prelude::*};\n//! # use gotham_restful::*;\n//! # use serde::{Deserialize, Serialize};\n//! use gotham_restful::gotham::hyper::Method;\n//!\n//! #[derive(Resource)]\n//! #[resource(custom_endpoint)]\n//! struct CustomResource;\n//!\n//! /// This type is used to parse path parameters.\n//! #[derive(Clone, Deserialize, StateData, StaticResponseExtender)]\n//! # #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n//! struct CustomPath {\n//! \tname: String\n//! }\n//!\n//! #[endpoint(\n//! \turi = \"custom/:name/read\",\n//! \tmethod = \"Method::GET\",\n//! \tparams = false,\n//! \tbody = false\n//! )]\n//! fn custom_endpoint(path: CustomPath) -> Success {\n//! \tpath.name.into()\n//! }\n//! # fn main() {\n//! # \tgotham::start(\"127.0.0.1:8080\", build_simple_router(|route| {\n//! # \t\troute.resource::(\"custom\");\n//! # \t})).expect(\"Failed to start gotham\");\n//! # }\n//! ```\n//!\n//! # Arguments\n//!\n//! Some endpoints require arguments. Those should be\n//! * **id** Should be a deserializable json-primitive like [`i64`] or [`String`].\n//! * **body** Should be any deserializable object, or any type implementing [`RequestBody`].\n//! * **query** Should be any deserializable object whose variables are json-primitives. It will\n//! however not be parsed from json, but from HTTP GET parameters like in `search?id=1`. The\n//! type needs to implement [`QueryStringExtractor`](gotham::extractor::QueryStringExtractor).\n//!\n//! Additionally, all handlers may take a reference to gotham's [`State`]. Please note that for async\n//! handlers, it needs to be a mutable reference until rustc's lifetime checks across await bounds\n//! improve.\n//!\n//! # Uploads and Downloads\n//!\n//! By default, every request body is parsed from json, and every respone is converted to json using\n//! [serde_json]. However, you may also use raw bodies. This is an example where the request body\n//! is simply returned as the response again, no json parsing involved:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # use gotham::{mime::{self, Mime}, router::builder::*};\n//! # use gotham_restful::*;\n//! # use serde::{Deserialize, Serialize};\n//! #[derive(Resource)]\n//! #[resource(create)]\n//! struct ImageResource;\n//!\n//! #[derive(FromBody, RequestBody)]\n//! #[supported_types(mime::IMAGE_GIF, mime::IMAGE_JPEG, mime::IMAGE_PNG)]\n//! struct RawImage {\n//! \tcontent: Vec,\n//! \tcontent_type: Mime\n//! }\n//!\n//! #[create]\n//! fn create(body: RawImage) -> Raw> {\n//! \tRaw::new(body.content, body.content_type)\n//! }\n//! # fn main() {\n//! # \tgotham::start(\"127.0.0.1:8080\", build_simple_router(|route| {\n//! # \t\troute.resource::(\"image\");\n//! # \t})).expect(\"Failed to start gotham\");\n//! # }\n//! ```\n//!\n//! # Custom HTTP Headers\n//!\n//! You can read request headers from the state as you would in any other gotham handler, and specify\n//! custom response headers using [Response::header].\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # use gotham::hyper::header::{ACCEPT, HeaderMap, VARY};\n//! # use gotham::{router::builder::*, state::State};\n//! # use gotham_restful::*;\n//! #[derive(Resource)]\n//! #[resource(read_all)]\n//! struct FooResource;\n//!\n//! #[read_all]\n//! async fn read_all(state: &mut State) -> NoContent {\n//! \tlet headers: &HeaderMap = state.borrow();\n//! \tlet accept = &headers[ACCEPT];\n//! # drop(accept);\n//!\n//! \tlet mut res = NoContent::default();\n//! \tres.header(VARY, \"accept\".parse().unwrap());\n//! \tres\n//! }\n//! # fn main() {\n//! # \tgotham::start(\"127.0.0.1:8080\", build_simple_router(|route| {\n//! # \t\troute.resource::(\"foo\");\n//! # \t})).expect(\"Failed to start gotham\");\n//! # }\n//! ```\n//!\n//! # Features\n//!\n//! To make life easier for common use-cases, this create offers a few features that might be helpful\n//! when you implement your web server. The complete feature list is\n//! - [`auth`](#authentication-feature) Advanced JWT middleware\n//! - [`cors`](#cors-feature) CORS handling for all endpoint handlers\n//! - [`database`](#database-feature) diesel middleware support\n//! - `errorlog` log errors returned from endpoint handlers\n//! - `full` enables all features except `without-openapi`\n//! - [`openapi`](#openapi-feature) router additions to generate an openapi spec\n//! - `without-openapi` (**default**) disables `openapi` support.\n//!\n//! ## Authentication Feature\n//!\n//! In order to enable authentication support, enable the `auth` feature gate. This allows you to\n//! register a middleware that can automatically check for the existence of an JWT authentication\n//! token. Besides being supported by the endpoint macros, it supports to lookup the required JWT secret\n//! with the JWT data, hence you can use several JWT secrets and decide on the fly which secret to use.\n//! None of this is currently supported by gotham's own JWT middleware.\n//!\n//! A simple example that uses only a single secret looks like this:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # #[cfg(feature = \"auth\")]\n//! # mod auth_feature_enabled {\n//! # use gotham::{router::builder::*, pipeline::*, state::State};\n//! # use gotham_restful::*;\n//! # use serde::{Deserialize, Serialize};\n//! #[derive(Resource)]\n//! #[resource(read)]\n//! struct SecretResource;\n//!\n//! #[derive(Serialize)]\n//! # #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n//! struct Secret {\n//! \tid: u64,\n//! \tintended_for: String\n//! }\n//!\n//! #[derive(Deserialize, Clone)]\n//! struct AuthData {\n//! \tsub: String,\n//! \texp: u64\n//! }\n//!\n//! #[read]\n//! fn read(auth: AuthStatus, id: u64) -> AuthSuccess {\n//! \tlet intended_for = auth.ok()?.sub;\n//! \tOk(Secret { id, intended_for })\n//! }\n//!\n//! fn main() {\n//! \tlet auth: AuthMiddleware = AuthMiddleware::new(\n//! \t\tAuthSource::AuthorizationHeader,\n//! \t\tAuthValidation::default(),\n//! \t\tStaticAuthHandler::from_array(b\"zlBsA2QXnkmpe0QTh8uCvtAEa4j33YAc\")\n//! \t);\n//! \tlet (chain, pipelines) = single_pipeline(new_pipeline().add(auth).build());\n//! \tgotham::start(\n//! \t\t\"127.0.0.1:8080\",\n//! \t\tbuild_router(chain, pipelines, |route| {\n//! \t\t\troute.resource::(\"secret\");\n//! \t\t})\n//! \t)\n//! \t.expect(\"Failed to start gotham\");\n//! }\n//! # }\n//! ```\n//!\n//! ## CORS Feature\n//!\n//! The cors feature allows an easy usage of this web server from other origins. By default, only\n//! the `Access-Control-Allow-Methods` header is touched. To change the behaviour, add your desired\n//! configuration as a middleware.\n//!\n//! A simple example that allows authentication from every origin (note that `*` always disallows\n//! authentication), and every content type, looks like this:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # #[cfg(feature = \"cors\")]\n//! # mod cors_feature_enabled {\n//! # use gotham::{hyper::header::*, router::builder::*, pipeline::*, state::State};\n//! # use gotham_restful::{*, cors::*};\n//! # use serde::{Deserialize, Serialize};\n//! #[derive(Resource)]\n//! #[resource(read_all)]\n//! struct FooResource;\n//!\n//! #[read_all]\n//! fn read_all() {\n//! \t// your handler\n//! }\n//!\n//! fn main() {\n//! \tlet cors = CorsConfig {\n//! \t\torigin: Origin::Copy,\n//! \t\theaders: Headers::List(vec![CONTENT_TYPE]),\n//! \t\tmax_age: 0,\n//! \t\tcredentials: true\n//! \t};\n//! \tlet (chain, pipelines) = single_pipeline(new_pipeline().add(cors).build());\n//! \tgotham::start(\n//! \t\t\"127.0.0.1:8080\",\n//! \t\tbuild_router(chain, pipelines, |route| {\n//! \t\t\troute.resource::(\"foo\");\n//! \t\t})\n//! \t)\n//! \t.expect(\"Failed to start gotham\");\n//! }\n//! # }\n//! ```\n//!\n//! The cors feature can also be used for non-resource handlers. Take a look at [`CorsRoute`]\n//! for an example.\n//!\n//! ## Database Feature\n//!\n//! The database feature allows an easy integration of [diesel] into your handler functions. Please\n//! note however that due to the way gotham's diesel middleware implementation, it is not possible\n//! to run async code while holding a database connection. If you need to combine async and database,\n//! you'll need to borrow the connection from the [`State`] yourself and return a boxed future.\n//!\n//! A simple non-async example looks like this:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate diesel;\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # #[cfg(feature = \"database\")]\n//! # mod database_feature_enabled {\n//! # use diesel::{table, PgConnection, QueryResult, RunQueryDsl};\n//! # use gotham::{router::builder::*, pipeline::*, state::State};\n//! # use gotham_middleware_diesel::DieselMiddleware;\n//! # use gotham_restful::*;\n//! # use serde::{Deserialize, Serialize};\n//! # use std::env;\n//! # table! {\n//! # foo (id) {\n//! # id -> Int8,\n//! # value -> Text,\n//! # }\n//! # }\n//! #[derive(Resource)]\n//! #[resource(read_all)]\n//! struct FooResource;\n//!\n//! #[derive(Queryable, Serialize)]\n//! # #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n//! struct Foo {\n//! \tid: i64,\n//! \tvalue: String\n//! }\n//!\n//! #[read_all]\n//! fn read_all(conn: &mut PgConnection) -> QueryResult> {\n//! \tfoo::table.load(conn)\n//! }\n//!\n//! type Repo = gotham_middleware_diesel::Repo;\n//!\n//! fn main() {\n//! \tlet repo = Repo::new(&env::var(\"DATABASE_URL\").unwrap());\n//! \tlet diesel = DieselMiddleware::new(repo);\n//!\n//! \tlet (chain, pipelines) = single_pipeline(new_pipeline().add(diesel).build());\n//! \tgotham::start(\n//! \t\t\"127.0.0.1:8080\",\n//! \t\tbuild_router(chain, pipelines, |route| {\n//! \t\t\troute.resource::(\"foo\");\n//! \t\t})\n//! \t)\n//! \t.expect(\"Failed to start gotham\");\n//! }\n//! # }\n//! ```\n//!\n//! ## OpenAPI Feature\n//!\n//! The OpenAPI feature is probably the most powerful one of this crate. Definitely read this section\n//! carefully both as a binary as well as a library author to avoid unwanted suprises.\n//!\n//! In order to automatically create an openapi specification, gotham-restful needs knowledge over\n//! all routes and the types returned. `serde` does a great job at serialization but doesn't give\n//! enough type information, so all types used in the router need to implement\n//! [`OpenapiType`](openapi_type::OpenapiType). This can be derived for almoust any type and there\n//! should be no need to implement it manually. A simple example looks like this:\n//!\n//! ```rust,no_run\n//! # #[macro_use] extern crate gotham_restful_derive;\n//! # #[cfg(feature = \"openapi\")]\n//! # mod openapi_feature_enabled {\n//! # use gotham::{router::builder::*, state::State};\n//! # use gotham_restful::*;\n//! # use openapi_type::OpenapiType;\n//! # use serde::{Deserialize, Serialize};\n//! #[derive(Resource)]\n//! #[resource(read_all)]\n//! struct FooResource;\n//!\n//! #[derive(OpenapiType, Serialize)]\n//! struct Foo {\n//! \tbar: String\n//! }\n//!\n//! #[read_all]\n//! fn read_all() -> Success {\n//! \tFoo {\n//! \t\tbar: \"Hello World\".to_owned()\n//! \t}\n//! \t.into()\n//! }\n//!\n//! fn main() {\n//! \tgotham::start(\n//! \t\t\"127.0.0.1:8080\",\n//! \t\tbuild_simple_router(|route| {\n//! \t\t\tlet info = OpenapiInfo {\n//! \t\t\t\ttitle: \"My Foo API\".to_owned(),\n//! \t\t\t\tversion: \"0.1.0\".to_owned(),\n//! \t\t\t\turls: vec![\"https://example.org/foo/api/v1\".to_owned()]\n//! \t\t\t};\n//! \t\t\troute.with_openapi(info, |mut route| {\n//! \t\t\t\troute.resource::(\"foo\");\n//! \t\t\t\troute.openapi_spec(\"openapi\");\n//! \t\t\t\troute.openapi_doc(\"/\");\n//! \t\t\t});\n//! \t\t})\n//! \t)\n//! \t.expect(\"Failed to start gotham\");\n//! }\n//! # }\n//! ```\n//!\n//! Above example adds the resource as before, but adds two other endpoints as well: `/openapi` and `/`.\n//! The first one will return the generated openapi specification in JSON format, allowing you to easily\n//! generate clients in different languages without worying to exactly replicate your api in each of those\n//! languages. The second one will return documentation in HTML format, so you can easily view your\n//! api and share it with other people.\n//!\n//! ### Gotchas\n//!\n//! The openapi feature has some gotchas you should be aware of.\n//!\n//! - The name of a struct is used as a \"link\" in the openapi specification. Therefore, if you have two\n//! structs with the same name in your project, the openapi specification will be invalid as only one\n//! of the two will make it into the spec.\n//! - By default, the `without-openapi` feature of this crate is enabled. Disabling it in favour of the\n//! `openapi` feature will add additional type bounds and method requirements to some of the traits and\n//! \ttypes in this crate, for example instead of [`Endpoint`] you now have to implement\n//! \t[`EndpointWithSchema`]. This means that some code might only compile on either feature, but not\n//! on both. If you are writing a library that uses gotham-restful, it is strongly recommended to pass\n//! \tboth features through and conditionally enable the openapi code, like this:\n//!\n//! ```rust\n//! # #[macro_use] extern crate gotham_restful;\n//! # use serde::{Deserialize, Serialize};\n//! #[derive(Deserialize, Serialize)]\n//! #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n//! struct Foo;\n//! ```\n//!\n//! [diesel]: https://diesel.rs/\n//! [`State`]: gotham::state::State\n\n#[cfg(all(feature = \"openapi\", feature = \"without-openapi\"))]\ncompile_error!(\"The 'openapi' and 'without-openapi' features cannot be combined\");\n\n#[cfg(all(not(feature = \"openapi\"), not(feature = \"without-openapi\")))]\ncompile_error!(\"Either the 'openapi' or 'without-openapi' feature needs to be enabled\");\n\n// weird proc macro issue\nextern crate self as gotham_restful;\n\n#[macro_use]\nextern crate gotham_restful_derive;\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate serde;\n\n#[cfg(test)]\n#[macro_use]\nextern crate pretty_assertions;\n\n#[doc(no_inline)]\npub use gotham;\n\npub use gotham_restful_derive::*;\n\n/// Not public API\n#[doc(hidden)]\npub mod private {\n\tpub use crate::routing::PathExtractor as IdPlaceholder;\n\tpub use futures_util::future::{BoxFuture, FutureExt};\n\t#[cfg(feature = \"database\")]\n\tpub use gotham_middleware_diesel::Repo;\n\t#[cfg(feature = \"openapi\")]\n\tpub use openapi_type::{OpenapiSchema, OpenapiType, Visitor};\n\tpub use serde_json;\n\n\t#[cfg(feature = \"openapi\")]\n\tuse gotham::hyper::StatusCode;\n\t#[cfg(feature = \"auth\")]\n\tuse gotham::state::{FromState, State};\n\n\t/// This method is used by the endpoint macro to generate a good error message\n\t/// when the used AuthData type does not implement Clone.\n\t#[cfg(feature = \"auth\")]\n\t#[inline]\n\tpub fn clone_from_state(state: &State) -> T\n\twhere\n\t\tT: FromState + Clone\n\t{\n\t\tT::borrow_from(state).clone()\n\t}\n\n\t/// This trait is used by the endpoint macro to generate a good error message\n\t/// when the schema function has the wrong signature.\n\t#[cfg(feature = \"openapi\")]\n\tpub trait CustomSchema {\n\t\tfn schema(self, code: StatusCode) -> OpenapiSchema;\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\timpl CustomSchema for F\n\twhere\n\t\tF: FnOnce(StatusCode) -> OpenapiSchema\n\t{\n\t\t#[inline]\n\t\tfn schema(self, code: StatusCode) -> OpenapiSchema {\n\t\t\tself(code)\n\t\t}\n\t}\n\n\t/// This trait is used by the endpoint macro to generate a good error message\n\t/// when the status_codes function has the wrong signature.\n\t#[cfg(feature = \"openapi\")]\n\tpub trait CustomStatusCodes {\n\t\tfn status_codes(self) -> Vec;\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\timpl CustomStatusCodes for F\n\twhere\n\t\tF: FnOnce() -> Vec\n\t{\n\t\t#[inline]\n\t\tfn status_codes(self) -> Vec {\n\t\t\tself()\n\t\t}\n\t}\n}\n\n#[cfg(feature = \"auth\")]\nmod auth;\n#[cfg(feature = \"auth\")]\npub use auth::{\n\tAuthHandler, AuthMiddleware, AuthSource, AuthStatus, AuthValidation, StaticAuthHandler\n};\n\n#[cfg(feature = \"cors\")]\npub mod cors;\n#[cfg(feature = \"cors\")]\npub use cors::{handle_cors, CorsConfig, CorsRoute};\n\n#[cfg(feature = \"openapi\")]\nmod openapi;\n#[cfg(feature = \"openapi\")]\npub use openapi::{builder::OpenapiInfo, router::GetOpenapi};\n\nmod endpoint;\n#[cfg(feature = \"openapi\")]\npub use endpoint::EndpointWithSchema;\npub use endpoint::{Endpoint, NoopExtractor};\n\nmod response;\npub use response::{\n\tAuthError, AuthErrorOrOther, AuthResult, AuthSuccess, IntoResponse, IntoResponseError,\n\tNoContent, Raw, Redirect, Response, Success\n};\n#[cfg(feature = \"openapi\")]\npub use response::{IntoResponseWithSchema, ResponseSchema};\n\nmod routing;\npub use routing::{DrawResourceRoutes, DrawResources};\n#[cfg(feature = \"openapi\")]\npub use routing::{DrawResourceRoutesWithSchema, DrawResourcesWithSchema, WithOpenapi};\n\nmod types;\npub use types::{FromBody, RequestBody, ResponseBody};\n\n/// This trait must be implemented for every resource. It allows you to register the different\n/// endpoints that can be handled by this resource to be registered with the underlying router.\n///\n/// It is not recommended to implement this yourself, just use `#[derive(Resource)]`.\n#[_private_openapi_trait(ResourceWithSchema)]\npub trait Resource {\n\t/// Register all methods handled by this resource with the underlying router.\n\t#[openapi_bound(D: crate::DrawResourceRoutesWithSchema)]\n\t#[non_openapi_bound(D: crate::DrawResourceRoutes)]\n\tfn setup(route: D);\n}\n","traces":[{"line":463,"address":[15761518],"length":1,"stats":{"Line":9},"fn_name":null},{"line":464,"address":[16432374],"length":1,"stats":{"Line":9},"fn_name":null},{"line":466,"address":[11252304],"length":1,"stats":{"Line":0},"fn_name":null},{"line":467,"address":[15761680,15761584],"length":1,"stats":{"Line":0},"fn_name":"ok_or"},{"line":489,"address":[7859440],"length":1,"stats":{"Line":0},"fn_name":null},{"line":506,"address":[15810308],"length":1,"stats":{"Line":0},"fn_name":null},{"line":507,"address":[14881463],"length":1,"stats":{"Line":12},"fn_name":null},{"line":508,"address":[11253111],"length":1,"stats":{"Line":12},"fn_name":null},{"line":512,"address":[15762329],"length":1,"stats":{"Line":0},"fn_name":null},{"line":518,"address":[7607826,7607622,7607596,7607606,7607514,7607851],"length":1,"stats":{"Line":6},"fn_name":null},{"line":523,"address":[11253429],"length":1,"stats":{"Line":6},"fn_name":null},{"line":527,"address":[14881604],"length":1,"stats":{"Line":0},"fn_name":null},{"line":528,"address":[7587966],"length":1,"stats":{"Line":7},"fn_name":null},{"line":529,"address":[16435502],"length":1,"stats":{"Line":7},"fn_name":null},{"line":536,"address":[16435640],"length":1,"stats":{"Line":0},"fn_name":null},{"line":541,"address":[15762496],"length":1,"stats":{"Line":0},"fn_name":null},{"line":545,"address":[15810599],"length":1,"stats":{"Line":0},"fn_name":null},{"line":546,"address":[15810643],"length":1,"stats":{"Line":1},"fn_name":null},{"line":547,"address":[15810658],"length":1,"stats":{"Line":1},"fn_name":null},{"line":552,"address":[11253480],"length":1,"stats":{"Line":0},"fn_name":null},{"line":559,"address":[11253518],"length":1,"stats":{"Line":0},"fn_name":null},{"line":564,"address":[10539424,10539392],"length":1,"stats":{"Line":0},"fn_name":"read_i24"},{"line":595,"address":[15762760],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":10,"coverable":23},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","openapi","builder.rs"],"content":"use openapi_type::{\n\tindexmap::IndexMap,\n\topenapiv3::{\n\t\tself, Components, OpenAPI, PathItem, ReferenceOr,\n\t\tReferenceOr::{Item, Reference},\n\t\tSchema, Server\n\t},\n\tOpenapiSchema\n};\nuse parking_lot::RwLock;\nuse std::sync::Arc;\n\n#[derive(Clone, Debug)]\npub struct OpenapiInfo {\n\tpub title: String,\n\tpub version: String,\n\tpub urls: Vec\n}\n\n#[derive(Clone, Debug)]\npub(crate) struct OpenapiBuilder {\n\tpub(crate) openapi: Arc>\n}\n\nimpl OpenapiBuilder {\n\tpub(crate) fn new(info: OpenapiInfo) -> Self {\n\t\tSelf {\n\t\t\topenapi: Arc::new(RwLock::new(OpenAPI {\n\t\t\t\topenapi: \"3.0.2\".to_string(),\n\t\t\t\tinfo: openapiv3::Info {\n\t\t\t\t\ttitle: info.title,\n\t\t\t\t\tversion: info.version,\n\t\t\t\t\t..Default::default()\n\t\t\t\t},\n\t\t\t\tservers: info\n\t\t\t\t\t.urls\n\t\t\t\t\t.into_iter()\n\t\t\t\t\t.map(|url| Server {\n\t\t\t\t\t\turl,\n\t\t\t\t\t\t..Default::default()\n\t\t\t\t\t})\n\t\t\t\t\t.collect(),\n\t\t\t\t..Default::default()\n\t\t\t}))\n\t\t}\n\t}\n\n\t/// Remove path from the OpenAPI spec, or return an empty one if not included. This is handy if you need to\n\t/// modify the path and add it back after the modification\n\tpub(crate) fn remove_path(&mut self, path: &str) -> PathItem {\n\t\tlet mut openapi = self.openapi.write();\n\t\tmatch openapi.paths.paths.swap_remove(path) {\n\t\t\tSome(Item(item)) => item,\n\t\t\t_ => PathItem::default()\n\t\t}\n\t}\n\n\tpub(crate) fn add_path(&mut self, path: Path, item: PathItem) {\n\t\tlet mut openapi = self.openapi.write();\n\t\topenapi.paths.paths.insert(path.to_string(), Item(item));\n\t}\n\n\tfn add_schema_impl(&mut self, name: String, mut schema: OpenapiSchema) {\n\t\tself.add_schema_dependencies(&mut schema.dependencies);\n\n\t\tlet mut openapi = self.openapi.write();\n\t\tmatch &mut openapi.components {\n\t\t\tSome(comp) => {\n\t\t\t\tcomp.schemas.insert(name, Item(schema.schema));\n\t\t\t},\n\t\t\tNone => {\n\t\t\t\tlet mut comp = Components::default();\n\t\t\t\tcomp.schemas.insert(name, Item(schema.schema));\n\t\t\t\topenapi.components = Some(comp);\n\t\t\t}\n\t\t};\n\t}\n\n\tfn add_schema_dependencies(&mut self, dependencies: &mut IndexMap) {\n\t\tlet keys: Vec = dependencies.keys().map(|k| k.to_string()).collect();\n\t\tfor dep in keys {\n\t\t\tlet dep_schema = dependencies.swap_remove(&dep);\n\t\t\tif let Some(dep_schema) = dep_schema {\n\t\t\t\tself.add_schema_impl(dep, dep_schema);\n\t\t\t}\n\t\t}\n\t}\n\n\tpub(crate) fn add_schema(&mut self, mut schema: OpenapiSchema) -> ReferenceOr {\n\t\tmatch schema.schema.schema_data.title.clone() {\n\t\t\tSome(name) => {\n\t\t\t\tlet reference = Reference {\n\t\t\t\t\treference: format!(\"#/components/schemas/{name}\")\n\t\t\t\t};\n\t\t\t\tself.add_schema_impl(name, schema);\n\t\t\t\treference\n\t\t\t},\n\t\t\tNone => {\n\t\t\t\tself.add_schema_dependencies(&mut schema.dependencies);\n\t\t\t\tItem(schema.schema)\n\t\t\t}\n\t\t}\n\t}\n}\n\n#[cfg(test)]\n#[allow(dead_code)]\nmod test {\n\tuse super::*;\n\tuse openapi_type::OpenapiType;\n\n\t#[derive(OpenapiType)]\n\tstruct Message {\n\t\tmsg: String\n\t}\n\n\t#[derive(OpenapiType)]\n\tstruct Messages {\n\t\tmsgs: Vec\n\t}\n\n\tfn info() -> OpenapiInfo {\n\t\tOpenapiInfo {\n\t\t\ttitle: \"TEST CASE\".to_owned(),\n\t\t\tversion: \"1.2.3\".to_owned(),\n\t\t\turls: vec![\n\t\t\t\t\"http://localhost:1234\".to_owned(),\n\t\t\t\t\"https://example.org\".to_owned(),\n\t\t\t]\n\t\t}\n\t}\n\n\tfn openapi(builder: OpenapiBuilder) -> OpenAPI {\n\t\tArc::try_unwrap(builder.openapi).unwrap().into_inner()\n\t}\n\n\t#[test]\n\tfn new_builder() {\n\t\tlet info = info();\n\t\tlet builder = OpenapiBuilder::new(info.clone());\n\t\tlet openapi = openapi(builder);\n\n\t\tassert_eq!(info.title, openapi.info.title);\n\t\tassert_eq!(info.version, openapi.info.version);\n\t\tassert_eq!(info.urls.len(), openapi.servers.len());\n\t}\n\n\t#[test]\n\tfn add_schema() {\n\t\tlet mut builder = OpenapiBuilder::new(info());\n\t\tbuilder.add_schema(>::schema());\n\t\tlet openapi = openapi(builder);\n\n\t\tassert_eq!(\n\t\t\topenapi.components.clone().unwrap_or_default().schemas[\"Message\"],\n\t\t\tReferenceOr::Item(Message::schema().schema)\n\t\t);\n\t\tassert_eq!(\n\t\t\topenapi.components.clone().unwrap_or_default().schemas[\"Messages\"],\n\t\t\tReferenceOr::Item(Messages::schema().schema)\n\t\t);\n\t}\n}\n","traces":[{"line":26,"address":[7831218,7829360,7830060],"length":1,"stats":{"Line":3},"fn_name":null},{"line":28,"address":[7723810,7723277,7722395],"length":1,"stats":{"Line":9},"fn_name":null},{"line":50,"address":[7724926,7724640],"length":1,"stats":{"Line":2},"fn_name":null},{"line":51,"address":[7724763],"length":1,"stats":{"Line":2},"fn_name":null},{"line":52,"address":[7724952,7724870,7724797],"length":1,"stats":{"Line":5},"fn_name":null},{"line":53,"address":[7724987],"length":1,"stats":{"Line":1},"fn_name":null},{"line":54,"address":[7831972,7832073],"length":1,"stats":{"Line":4},"fn_name":null},{"line":58,"address":[6927456,6927394,6926944],"length":1,"stats":{"Line":2},"fn_name":null},{"line":59,"address":[6927059,6927137],"length":1,"stats":{"Line":4},"fn_name":null},{"line":60,"address":[6927232,6927163],"length":1,"stats":{"Line":4},"fn_name":null},{"line":63,"address":[7832336,7833496,7833642],"length":1,"stats":{"Line":2},"fn_name":null},{"line":64,"address":[7832409],"length":1,"stats":{"Line":2},"fn_name":null},{"line":66,"address":[7725528],"length":1,"stats":{"Line":2},"fn_name":null},{"line":67,"address":[7725590,7725663],"length":1,"stats":{"Line":4},"fn_name":null},{"line":68,"address":[7725745],"length":1,"stats":{"Line":2},"fn_name":null},{"line":69,"address":[7725753],"length":1,"stats":{"Line":2},"fn_name":null},{"line":72,"address":[7832706],"length":1,"stats":{"Line":2},"fn_name":null},{"line":73,"address":[7832957,7833162],"length":1,"stats":{"Line":4},"fn_name":null},{"line":74,"address":[7833181,7833474],"length":1,"stats":{"Line":2},"fn_name":null},{"line":79,"address":[7834381,7834338,7833680],"length":1,"stats":{"Line":3},"fn_name":null},{"line":80,"address":[7834400,7834435,7833726],"length":1,"stats":{"Line":7},"fn_name":"{closure#0}"},{"line":81,"address":[7833774,7833987,7834357],"length":1,"stats":{"Line":7},"fn_name":null},{"line":82,"address":[7834027,7834095],"length":1,"stats":{"Line":4},"fn_name":null},{"line":83,"address":[7834103],"length":1,"stats":{"Line":2},"fn_name":null},{"line":84,"address":[7834265,7834182],"length":1,"stats":{"Line":4},"fn_name":null},{"line":89,"address":[7834464,7835139,7835269],"length":1,"stats":{"Line":3},"fn_name":null},{"line":90,"address":[7834507,7834611],"length":1,"stats":{"Line":6},"fn_name":null},{"line":91,"address":[7834664],"length":1,"stats":{"Line":1},"fn_name":null},{"line":93,"address":[7834815],"length":1,"stats":{"Line":1},"fn_name":null},{"line":95,"address":[7834968],"length":1,"stats":{"Line":1},"fn_name":null},{"line":96,"address":[7835114],"length":1,"stats":{"Line":1},"fn_name":null},{"line":99,"address":[7834647],"length":1,"stats":{"Line":3},"fn_name":null},{"line":100,"address":[7835190],"length":1,"stats":{"Line":3},"fn_name":null}],"covered":33,"coverable":33},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","openapi","handler","mod.rs"],"content":"#![cfg_attr(not(feature = \"auth\"), allow(unused_imports))]\nuse super::SECURITY_NAME;\nuse base64::prelude::*;\nuse futures_util::{future, future::FutureExt};\nuse gotham::{\n\tanyhow,\n\thandler::{Handler, HandlerError, HandlerFuture, NewHandler},\n\thelpers::http::response::{create_empty_response, create_response},\n\thyper::{\n\t\theader::{\n\t\t\tHeaderMap, HeaderValue, CACHE_CONTROL, CONTENT_SECURITY_POLICY, ETAG, IF_NONE_MATCH,\n\t\t\tREFERRER_POLICY, X_CONTENT_TYPE_OPTIONS\n\t\t},\n\t\tBody, Response, StatusCode\n\t},\n\tmime::{APPLICATION_JSON, TEXT_HTML_UTF_8, TEXT_PLAIN_UTF_8},\n\tstate::State\n};\nuse gotham_restful_redoc::Redoc;\nuse openapi_type::{\n\tindexmap::IndexMap,\n\topenapiv3::{APIKeyLocation, OpenAPI, ReferenceOr, SecurityScheme}\n};\nuse parking_lot::RwLock;\nuse sha2::{Digest, Sha256};\nuse std::{panic::RefUnwindSafe, pin::Pin, sync::Arc};\n\n#[cfg(feature = \"auth\")]\nfn get_security(state: &State) -> IndexMap> {\n\tuse crate::AuthSource;\n\tuse gotham::state::FromState;\n\n\tlet source = match AuthSource::try_borrow_from(state) {\n\t\tSome(source) => source,\n\t\tNone => return Default::default()\n\t};\n\n\tlet security_scheme = match source {\n\t\tAuthSource::Cookie(name) => SecurityScheme::APIKey {\n\t\t\tlocation: APIKeyLocation::Cookie,\n\t\t\tname: name.to_string(),\n\t\t\tdescription: None\n\t\t},\n\t\tAuthSource::Header(name) => SecurityScheme::APIKey {\n\t\t\tlocation: APIKeyLocation::Header,\n\t\t\tname: name.to_string(),\n\t\t\tdescription: None\n\t\t},\n\t\tAuthSource::AuthorizationHeader => SecurityScheme::HTTP {\n\t\t\tscheme: \"bearer\".to_owned(),\n\t\t\tbearer_format: Some(\"JWT\".to_owned()),\n\t\t\tdescription: None\n\t\t}\n\t};\n\n\tlet mut security_schemes: IndexMap> = Default::default();\n\tsecurity_schemes.insert(SECURITY_NAME.to_owned(), ReferenceOr::Item(security_scheme));\n\n\tsecurity_schemes\n}\n\n#[cfg(not(feature = \"auth\"))]\nfn get_security(_state: &State) -> IndexMap> {\n\tDefault::default()\n}\n\nfn openapi_string(\n\tstate: &State,\n\topenapi: &Arc>\n) -> Result {\n\tlet openapi = openapi.read();\n\n\tlet mut openapi = openapi.clone();\n\tlet security_schemes = get_security(state);\n\tlet mut components = openapi.components.unwrap_or_default();\n\tcomponents.security_schemes = security_schemes;\n\topenapi.components = Some(components);\n\n\tserde_json::to_string(&openapi)\n}\n\nfn create_openapi_response(state: &State, openapi: &Arc>) -> Response {\n\tmatch openapi_string(state, openapi) {\n\t\tOk(body) => {\n\t\t\tlet mut res = create_response(state, StatusCode::OK, APPLICATION_JSON, body);\n\t\t\tlet headers = res.headers_mut();\n\t\t\theaders.insert(X_CONTENT_TYPE_OPTIONS, HeaderValue::from_static(\"nosniff\"));\n\t\t\tres\n\t\t},\n\t\tErr(e) => {\n\t\t\terror!(\"Unable to handle OpenAPI request due to error: {e}\");\n\t\t\tcreate_response(\n\t\t\t\tstate,\n\t\t\t\tStatusCode::INTERNAL_SERVER_ERROR,\n\t\t\t\tTEXT_PLAIN_UTF_8,\n\t\t\t\t\"\"\n\t\t\t)\n\t\t}\n\t}\n}\n\n#[derive(Clone)]\npub(crate) struct OpenapiSpecHandler {\n\topenapi: Arc>\n}\n\n// safety: the handler only ever aquires a read lock, so this usage of\n// RwLock is, in fact, unwind safe\nimpl RefUnwindSafe for OpenapiSpecHandler {}\n\nimpl OpenapiSpecHandler {\n\tpub(crate) fn new(openapi: Arc>) -> Self {\n\t\tSelf { openapi }\n\t}\n}\n\nimpl NewHandler for OpenapiSpecHandler {\n\ttype Instance = Self;\n\n\tfn new_handler(&self) -> anyhow::Result {\n\t\tOk(self.clone())\n\t}\n}\n\nimpl Handler for OpenapiSpecHandler {\n\tfn handle(self, mut state: State) -> Pin> {\n\t\tlet res = create_openapi_response(&mut state, &self.openapi);\n\t\tfuture::ok((state, res)).boxed()\n\t}\n}\n\n#[derive(Clone)]\npub(crate) struct OpenapiDocHandler {\n\topenapi: Arc>\n}\n\n// safety: the handler only ever aquires a read lock, so this usage of\n// RwLock is, in fact, unwind safe\nimpl RefUnwindSafe for OpenapiDocHandler {}\n\nimpl OpenapiDocHandler {\n\tpub(crate) fn new(openapi: Arc>) -> Self {\n\t\tSelf { openapi }\n\t}\n}\n\nimpl NewHandler for OpenapiDocHandler {\n\ttype Instance = Self;\n\n\tfn new_handler(&self) -> anyhow::Result {\n\t\tOk(self.clone())\n\t}\n}\n\nfn redoc_handler(\n\tstate: &State,\n\topenapi: &Arc>\n) -> Result, HandlerError> {\n\tlet spec = openapi_string(state, openapi)?;\n\tlet Redoc { html, script_hash } = gotham_restful_redoc::html(spec);\n\n\tlet mut etag = Sha256::new();\n\tetag.update(&html);\n\tlet etag = format!(\"\\\"{}\\\"\", BASE64_STANDARD.encode(etag.finalize()));\n\n\tif state\n\t\t.borrow::()\n\t\t.get(IF_NONE_MATCH)\n\t\t.map_or(false, |header| header.as_bytes() == etag.as_bytes())\n\t{\n\t\tlet res = create_empty_response(state, StatusCode::NOT_MODIFIED);\n\t\treturn Ok(res);\n\t}\n\n\tlet mut res = create_response(state, StatusCode::OK, TEXT_HTML_UTF_8, html);\n\tlet headers = res.headers_mut();\n\theaders.insert(\n\t\tCACHE_CONTROL,\n\t\tHeaderValue::from_static(\"public,max-age=2592000\")\n\t);\n\theaders.insert(\n\t\tCONTENT_SECURITY_POLICY,\n\t\tformat!(\n\t\t\t\"default-src 'none';base-uri 'none';script-src 'unsafe-inline' https://cdn.jsdelivr.net 'sha256-{script_hash}' 'strict-dynamic';style-src 'unsafe-inline' https://fonts.googleapis.com;font-src https://fonts.gstatic.com;connect-src 'self';img-src blob: data:\",\n\t\t).parse().unwrap()\n\t);\n\theaders.insert(ETAG, etag.parse().unwrap());\n\theaders.insert(REFERRER_POLICY, HeaderValue::from_static(\"no-referrer\"));\n\theaders.insert(X_CONTENT_TYPE_OPTIONS, HeaderValue::from_static(\"nosniff\"));\n\tOk(res)\n}\n\nimpl Handler for OpenapiDocHandler {\n\tfn handle(self, state: State) -> Pin> {\n\t\tmatch redoc_handler(&state, &self.openapi) {\n\t\t\tOk(res) => future::ok((state, res)).boxed(),\n\t\t\tErr(err) => future::err((state, err)).boxed()\n\t\t}\n\t}\n}\n","traces":[{"line":29,"address":[8013168,8013963],"length":1,"stats":{"Line":2},"fn_name":"get_security"},{"line":33,"address":[8013322,8013206],"length":1,"stats":{"Line":2},"fn_name":null},{"line":34,"address":[8013267],"length":1,"stats":{"Line":1},"fn_name":null},{"line":35,"address":[8013256],"length":1,"stats":{"Line":1},"fn_name":null},{"line":38,"address":[8013285],"length":1,"stats":{"Line":1},"fn_name":null},{"line":41,"address":[8013349],"length":1,"stats":{"Line":0},"fn_name":null},{"line":46,"address":[8013516],"length":1,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[8013659],"length":1,"stats":{"Line":1},"fn_name":null},{"line":51,"address":[8013755,8013684],"length":1,"stats":{"Line":2},"fn_name":null},{"line":56,"address":[8013989],"length":1,"stats":{"Line":1},"fn_name":null},{"line":57,"address":[8014114,8014046],"length":1,"stats":{"Line":2},"fn_name":null},{"line":59,"address":[8014256],"length":1,"stats":{"Line":1},"fn_name":null},{"line":67,"address":[8014320,8015231,8015731],"length":1,"stats":{"Line":2},"fn_name":"openapi_string"},{"line":71,"address":[8014386],"length":1,"stats":{"Line":2},"fn_name":null},{"line":73,"address":[8014444,8014516],"length":1,"stats":{"Line":4},"fn_name":null},{"line":74,"address":[8014592,8014544],"length":1,"stats":{"Line":4},"fn_name":null},{"line":75,"address":[8014600,8014708],"length":1,"stats":{"Line":4},"fn_name":null},{"line":76,"address":[8014724],"length":1,"stats":{"Line":2},"fn_name":null},{"line":77,"address":[8014980],"length":1,"stats":{"Line":2},"fn_name":null},{"line":79,"address":[8015107],"length":1,"stats":{"Line":2},"fn_name":null},{"line":82,"address":[8016615,8015760],"length":1,"stats":{"Line":2},"fn_name":"create_openapi_response"},{"line":83,"address":[8015798],"length":1,"stats":{"Line":2},"fn_name":null},{"line":84,"address":[8015851],"length":1,"stats":{"Line":2},"fn_name":null},{"line":85,"address":[8015874],"length":1,"stats":{"Line":2},"fn_name":null},{"line":86,"address":[8016700,8016009],"length":1,"stats":{"Line":4},"fn_name":null},{"line":87,"address":[8016708],"length":1,"stats":{"Line":2},"fn_name":null},{"line":88,"address":[8016912],"length":1,"stats":{"Line":2},"fn_name":null},{"line":90,"address":[8016028],"length":1,"stats":{"Line":0},"fn_name":null},{"line":91,"address":[8016041,8016477,8016131,8016189],"length":1,"stats":{"Line":0},"fn_name":null},{"line":95,"address":[8016265],"length":1,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[8016944],"length":1,"stats":{"Line":2},"fn_name":null},{"line":120,"address":[8016960],"length":1,"stats":{"Line":2},"fn_name":"new_handler"},{"line":121,"address":[8016969],"length":1,"stats":{"Line":2},"fn_name":null},{"line":126,"address":[8017354,8017008],"length":1,"stats":{"Line":2},"fn_name":"handle"},{"line":127,"address":[8017051],"length":1,"stats":{"Line":2},"fn_name":null},{"line":128,"address":[8017025,8017104],"length":1,"stats":{"Line":4},"fn_name":null},{"line":142,"address":[8017392],"length":1,"stats":{"Line":0},"fn_name":null},{"line":150,"address":[8017408],"length":1,"stats":{"Line":0},"fn_name":"new_handler"},{"line":151,"address":[8017417],"length":1,"stats":{"Line":0},"fn_name":null},{"line":155,"address":[8018432,8020780,8017456],"length":1,"stats":{"Line":0},"fn_name":"redoc_handler"},{"line":159,"address":[8017503,8017774],"length":1,"stats":{"Line":0},"fn_name":null},{"line":160,"address":[8017699,8017857],"length":1,"stats":{"Line":0},"fn_name":null},{"line":162,"address":[8017929],"length":1,"stats":{"Line":0},"fn_name":null},{"line":163,"address":[8017992],"length":1,"stats":{"Line":0},"fn_name":null},{"line":164,"address":[8018273,8018019,8018443],"length":1,"stats":{"Line":0},"fn_name":null},{"line":166,"address":[8018527,8018673,8018628],"length":1,"stats":{"Line":0},"fn_name":null},{"line":168,"address":[8018598],"length":1,"stats":{"Line":0},"fn_name":null},{"line":169,"address":[8020841,8018665,8020816],"length":1,"stats":{"Line":0},"fn_name":"{closure#0}"},{"line":171,"address":[8018901],"length":1,"stats":{"Line":0},"fn_name":null},{"line":172,"address":[8018968],"length":1,"stats":{"Line":0},"fn_name":null},{"line":175,"address":[8018720],"length":1,"stats":{"Line":0},"fn_name":null},{"line":176,"address":[8019195,8019122],"length":1,"stats":{"Line":0},"fn_name":null},{"line":177,"address":[8019310],"length":1,"stats":{"Line":0},"fn_name":null},{"line":178,"address":[8019203],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[8019241],"length":1,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[8019815],"length":1,"stats":{"Line":0},"fn_name":null},{"line":182,"address":[8019402],"length":1,"stats":{"Line":0},"fn_name":null},{"line":183,"address":[8019757,8019569],"length":1,"stats":{"Line":0},"fn_name":null},{"line":187,"address":[8019967],"length":1,"stats":{"Line":0},"fn_name":null},{"line":188,"address":[8020231],"length":1,"stats":{"Line":0},"fn_name":null},{"line":189,"address":[8020430],"length":1,"stats":{"Line":0},"fn_name":null},{"line":190,"address":[8020672],"length":1,"stats":{"Line":0},"fn_name":null},{"line":194,"address":[8021519,8020912],"length":1,"stats":{"Line":0},"fn_name":"handle"},{"line":195,"address":[8020929,8021003,8021384],"length":1,"stats":{"Line":0},"fn_name":null},{"line":196,"address":[8021070,8021436],"length":1,"stats":{"Line":0},"fn_name":null},{"line":197,"address":[8021200],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":31,"coverable":66},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","openapi","mod.rs"],"content":"const SECURITY_NAME: &str = \"authToken\";\n\npub(crate) mod builder;\npub(crate) mod handler;\npub(crate) mod operation;\npub(crate) mod router;\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","openapi","operation.rs"],"content":"use super::SECURITY_NAME;\nuse crate::{response::OrAllTypes, EndpointWithSchema, IntoResponse, RequestBody};\nuse gotham::{hyper::StatusCode, mime::Mime};\nuse openapi_type::{\n\tindexmap::IndexMap,\n\topenapiv3::{\n\t\tMediaType, Operation, Parameter, ParameterData, ParameterSchemaOrContent, ReferenceOr,\n\t\tReferenceOr::Item, RequestBody as OARequestBody, Response, Responses, Schema, SchemaKind,\n\t\tStatusCode as OAStatusCode, Type\n\t},\n\tOpenapiSchema\n};\nuse std::collections::HashMap;\n\nfn new_parameter_data(\n\tname: String,\n\trequired: bool,\n\tschema: ReferenceOr>\n) -> ParameterData {\n\tParameterData {\n\t\tname,\n\t\tdescription: None,\n\t\trequired,\n\t\tdeprecated: None,\n\t\tformat: ParameterSchemaOrContent::Schema(schema.unbox()),\n\t\texample: None,\n\t\texamples: Default::default(),\n\t\texplode: None,\n\t\textensions: Default::default()\n\t}\n}\n\n#[derive(Default)]\nstruct OperationParams {\n\tpath_params: Option,\n\tquery_params: Option\n}\n\nimpl OperationParams {\n\t// TODO shouldn't this be a custom openapi_type::Visitor\n\t// rather than this hacky code?\n\tfn add_path_params(\n\t\tpath_params: Option,\n\t\tparams: &mut Vec>\n\t) {\n\t\tlet path_params = match path_params {\n\t\t\tSome(pp) => pp.schema,\n\t\t\tNone => return\n\t\t};\n\t\tlet path_params = match path_params.schema_kind {\n\t\t\tSchemaKind::Type(Type::Object(ty)) => ty,\n\t\t\t_ => panic!(\"Path Parameters needs to be a plain struct\")\n\t\t};\n\t\tfor (name, schema) in path_params.properties {\n\t\t\tlet required = path_params.required.contains(&name);\n\t\t\tparams.push(Item(Parameter::Path {\n\t\t\t\tparameter_data: new_parameter_data(name, required, schema),\n\t\t\t\tstyle: Default::default()\n\t\t\t}))\n\t\t}\n\t}\n\n\t// TODO shouldn't this be a custom openapi_type::Visitor\n\t// rather than this hacky code?\n\tfn add_query_params(\n\t\tquery_params: Option,\n\t\tparams: &mut Vec>\n\t) {\n\t\tlet query_params = match query_params {\n\t\t\tSome(qp) => qp.schema,\n\t\t\tNone => return\n\t\t};\n\t\tlet query_params = match query_params.schema_kind {\n\t\t\tSchemaKind::Type(Type::Object(ty)) => ty,\n\t\t\t_ => panic!(\"Query Parameters needs to be a plain struct\")\n\t\t};\n\t\tfor (name, schema) in query_params.properties {\n\t\t\tlet required = query_params.required.contains(&name);\n\t\t\tparams.push(Item(Parameter::Query {\n\t\t\t\tparameter_data: new_parameter_data(name, required, schema),\n\t\t\t\tallow_reserved: false,\n\t\t\t\tstyle: Default::default(),\n\t\t\t\tallow_empty_value: None\n\t\t\t}))\n\t\t}\n\t}\n\n\tfn into_params(self) -> Vec> {\n\t\tlet mut params: Vec> = Vec::new();\n\t\tSelf::add_path_params(self.path_params, &mut params);\n\t\tSelf::add_query_params(self.query_params, &mut params);\n\t\tparams\n\t}\n}\n\npub(crate) struct OperationDescription {\n\toperation_id: Option,\n\tdescription: Option,\n\n\taccepted_types: Option>,\n\tresponses: HashMap>,\n\tparams: OperationParams,\n\tbody_schema: Option>,\n\tsupported_types: Option>,\n\trequires_auth: bool\n}\n\nimpl OperationDescription {\n\t/// Create a new operation description for the given endpoint type and schema. If the endpoint\n\t/// does not specify an operation id, the path is used to generate one.\n\tpub(crate) fn new(\n\t\tresponses: HashMap>,\n\t\tpath: &str\n\t) -> Self {\n\t\tlet operation_id = E::operation_id().or_else(|| {\n\t\t\tE::operation_verb()\n\t\t\t\t.map(|verb| format!(\"{verb}_{}\", path.replace(\"/\", \"_\").trim_start_matches('_')))\n\t\t});\n\t\tSelf {\n\t\t\toperation_id,\n\t\t\tdescription: E::description(),\n\n\t\t\taccepted_types: E::Output::accepted_types(),\n\t\t\tresponses,\n\t\t\tparams: Default::default(),\n\t\t\tbody_schema: None,\n\t\t\tsupported_types: None,\n\t\t\trequires_auth: E::wants_auth()\n\t\t}\n\t}\n\n\tpub(crate) fn set_path_params(&mut self, params: OpenapiSchema) {\n\t\tself.params.path_params = Some(params);\n\t}\n\n\tpub(crate) fn set_query_params(&mut self, params: OpenapiSchema) {\n\t\tself.params.query_params = Some(params);\n\t}\n\n\tpub(crate) fn set_body(&mut self, schema: ReferenceOr) {\n\t\tself.body_schema = Some(schema);\n\t\tself.supported_types = Body::supported_types();\n\t}\n\n\tfn schema_to_content(\n\t\ttypes: Vec,\n\t\tschema: ReferenceOr\n\t) -> IndexMap {\n\t\tlet mut content: IndexMap = IndexMap::new();\n\t\tfor ty in types {\n\t\t\tcontent.insert(ty.to_string(), MediaType {\n\t\t\t\tschema: Some(schema.clone()),\n\t\t\t\t..Default::default()\n\t\t\t});\n\t\t}\n\t\tcontent\n\t}\n\n\tpub(crate) fn into_operation(self) -> Operation {\n\t\t// this is unfortunately neccessary to prevent rust from complaining about partially moving self\n\t\tlet (\n\t\t\toperation_id,\n\t\t\tdescription,\n\t\t\taccepted_types,\n\t\t\tresponses,\n\t\t\tparams,\n\t\t\tbody_schema,\n\t\t\tsupported_types,\n\t\t\trequires_auth\n\t\t) = (\n\t\t\tself.operation_id,\n\t\t\tself.description,\n\t\t\tself.accepted_types,\n\t\t\tself.responses,\n\t\t\tself.params,\n\t\t\tself.body_schema,\n\t\t\tself.supported_types,\n\t\t\tself.requires_auth\n\t\t);\n\n\t\tlet responses: IndexMap> = responses\n\t\t\t.into_iter()\n\t\t\t.map(|(code, schema)| {\n\t\t\t\tlet content =\n\t\t\t\t\tSelf::schema_to_content(accepted_types.clone().or_all_types(), schema);\n\t\t\t\t(\n\t\t\t\t\tOAStatusCode::Code(code.as_u16()),\n\t\t\t\t\tItem(Response {\n\t\t\t\t\t\tdescription: code\n\t\t\t\t\t\t\t.canonical_reason()\n\t\t\t\t\t\t\t.map(|d| d.to_string())\n\t\t\t\t\t\t\t.unwrap_or_default(),\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\t..Default::default()\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t})\n\t\t\t.collect();\n\n\t\tlet request_body = body_schema.map(|schema| {\n\t\t\tItem(OARequestBody {\n\t\t\t\tcontent: Self::schema_to_content(supported_types.or_all_types(), schema),\n\t\t\t\trequired: true,\n\t\t\t\t..Default::default()\n\t\t\t})\n\t\t});\n\n\t\tlet mut security = None;\n\t\tif requires_auth {\n\t\t\tlet mut sec = IndexMap::new();\n\t\t\tsec.insert(SECURITY_NAME.to_owned(), Vec::new());\n\t\t\tsecurity = Some(vec![sec]);\n\t\t}\n\n\t\tOperation {\n\t\t\ttags: Vec::new(),\n\t\t\toperation_id,\n\t\t\tdescription,\n\t\t\tparameters: params.into_params(),\n\t\t\trequest_body,\n\t\t\tresponses: Responses {\n\t\t\t\tresponses,\n\t\t\t\t..Default::default()\n\t\t\t},\n\t\t\tdeprecated: false,\n\t\t\tsecurity,\n\t\t\t..Default::default()\n\t\t}\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse crate::{NoContent, Raw, ResponseSchema};\n\n\t#[test]\n\tfn no_content_schema_to_content() {\n\t\tlet types = NoContent::accepted_types();\n\t\tlet schema = ::schema(StatusCode::NO_CONTENT);\n\t\tlet content =\n\t\t\tOperationDescription::schema_to_content(types.or_all_types(), Item(schema.schema));\n\t\tassert!(content.is_empty());\n\t}\n\n\t#[test]\n\tfn raw_schema_to_content() {\n\t\tlet types = Raw::<&str>::accepted_types();\n\t\tlet schema = as ResponseSchema>::schema(StatusCode::OK);\n\t\tlet content =\n\t\t\tOperationDescription::schema_to_content(types.or_all_types(), Item(schema.schema));\n\t\tassert_eq!(content.len(), 1);\n\t\tlet json = serde_json::to_string(&content.values().nth(0).unwrap()).unwrap();\n\t\tassert_eq!(json, r#\"{\"schema\":{\"type\":\"string\",\"format\":\"binary\"}}\"#);\n\t}\n}\n","traces":[{"line":15,"address":[7950433,7949872,7950479],"length":1,"stats":{"Line":1},"fn_name":"new_parameter_data"},{"line":25,"address":[7949947,7950032],"length":1,"stats":{"Line":2},"fn_name":null},{"line":27,"address":[7950042],"length":1,"stats":{"Line":1},"fn_name":null},{"line":29,"address":[7950113],"length":1,"stats":{"Line":1},"fn_name":null},{"line":42,"address":[7950496,7951722,7952010],"length":1,"stats":{"Line":2},"fn_name":null},{"line":46,"address":[7950536],"length":1,"stats":{"Line":2},"fn_name":null},{"line":47,"address":[7950599],"length":1,"stats":{"Line":1},"fn_name":null},{"line":50,"address":[7950669],"length":1,"stats":{"Line":1},"fn_name":null},{"line":51,"address":[7950755],"length":1,"stats":{"Line":1},"fn_name":null},{"line":54,"address":[7951749,7950830,7951053],"length":1,"stats":{"Line":3},"fn_name":null},{"line":55,"address":[7951399,7951319],"length":1,"stats":{"Line":2},"fn_name":null},{"line":56,"address":[7951614],"length":1,"stats":{"Line":1},"fn_name":null},{"line":57,"address":[7951438],"length":1,"stats":{"Line":1},"fn_name":null},{"line":58,"address":[7951555],"length":1,"stats":{"Line":1},"fn_name":null},{"line":65,"address":[7953848,7952304,7953560],"length":1,"stats":{"Line":2},"fn_name":null},{"line":69,"address":[7952344],"length":1,"stats":{"Line":2},"fn_name":null},{"line":70,"address":[7952407],"length":1,"stats":{"Line":1},"fn_name":null},{"line":73,"address":[7952477],"length":1,"stats":{"Line":1},"fn_name":null},{"line":74,"address":[7952563],"length":1,"stats":{"Line":1},"fn_name":null},{"line":77,"address":[7952861,7953587,7952638],"length":1,"stats":{"Line":3},"fn_name":null},{"line":78,"address":[7953207,7953127],"length":1,"stats":{"Line":2},"fn_name":null},{"line":79,"address":[7953430],"length":1,"stats":{"Line":1},"fn_name":null},{"line":80,"address":[7953246],"length":1,"stats":{"Line":1},"fn_name":null},{"line":82,"address":[7953363],"length":1,"stats":{"Line":1},"fn_name":null},{"line":83,"address":[7953422],"length":1,"stats":{"Line":1},"fn_name":null},{"line":88,"address":[7954453,7954492,7954144],"length":1,"stats":{"Line":2},"fn_name":null},{"line":89,"address":[7954166],"length":1,"stats":{"Line":2},"fn_name":null},{"line":90,"address":[7954260],"length":1,"stats":{"Line":2},"fn_name":null},{"line":91,"address":[7954352],"length":1,"stats":{"Line":2},"fn_name":null},{"line":92,"address":[7954419],"length":1,"stats":{"Line":2},"fn_name":null},{"line":111,"address":[6408869,6408096,6408943],"length":1,"stats":{"Line":8},"fn_name":null},{"line":115,"address":[6408976,6408144,6408223],"length":1,"stats":{"Line":22},"fn_name":"{closure#0}"},{"line":116,"address":[6409000,6409033],"length":1,"stats":{"Line":12},"fn_name":null},{"line":117,"address":[6409013,6409432,6409525,6409118,6409072],"length":1,"stats":{"Line":22},"fn_name":"{closure#0}"},{"line":121,"address":[6408291],"length":1,"stats":{"Line":8},"fn_name":null},{"line":123,"address":[6408347],"length":1,"stats":{"Line":8},"fn_name":null},{"line":125,"address":[6408451],"length":1,"stats":{"Line":8},"fn_name":null},{"line":128,"address":[6408526],"length":1,"stats":{"Line":8},"fn_name":null},{"line":132,"address":[7954544,7954623],"length":1,"stats":{"Line":1},"fn_name":null},{"line":133,"address":[7954665,7954581],"length":1,"stats":{"Line":2},"fn_name":null},{"line":136,"address":[7954781,7954688],"length":1,"stats":{"Line":1},"fn_name":null},{"line":137,"address":[7954823,7954725],"length":1,"stats":{"Line":2},"fn_name":null},{"line":140,"address":[6409760,6409940],"length":1,"stats":{"Line":2},"fn_name":null},{"line":141,"address":[6409797],"length":1,"stats":{"Line":2},"fn_name":null},{"line":142,"address":[6409912,6409960],"length":1,"stats":{"Line":2},"fn_name":null},{"line":145,"address":[7955914,7954864,7956141],"length":1,"stats":{"Line":3},"fn_name":null},{"line":149,"address":[7954906],"length":1,"stats":{"Line":3},"fn_name":null},{"line":150,"address":[7954984,7955077],"length":1,"stats":{"Line":6},"fn_name":null},{"line":151,"address":[7955350,7955604],"length":1,"stats":{"Line":6},"fn_name":null},{"line":152,"address":[7955514,7955426],"length":1,"stats":{"Line":6},"fn_name":null},{"line":153,"address":[7955524],"length":1,"stats":{"Line":3},"fn_name":null},{"line":156,"address":[7956039],"length":1,"stats":{"Line":3},"fn_name":null},{"line":159,"address":[7956176,7957834,7959174],"length":1,"stats":{"Line":2},"fn_name":null},{"line":161,"address":[7956243,7956597],"length":1,"stats":{"Line":4},"fn_name":null},{"line":162,"address":[7956844],"length":1,"stats":{"Line":2},"fn_name":null},{"line":163,"address":[7956884],"length":1,"stats":{"Line":2},"fn_name":null},{"line":164,"address":[7956924],"length":1,"stats":{"Line":2},"fn_name":null},{"line":165,"address":[7956956],"length":1,"stats":{"Line":2},"fn_name":null},{"line":166,"address":[7957012],"length":1,"stats":{"Line":2},"fn_name":null},{"line":167,"address":[7957045],"length":1,"stats":{"Line":2},"fn_name":null},{"line":168,"address":[7957063],"length":1,"stats":{"Line":2},"fn_name":null},{"line":169,"address":[7957103],"length":1,"stats":{"Line":2},"fn_name":null},{"line":171,"address":[7956323],"length":1,"stats":{"Line":2},"fn_name":null},{"line":172,"address":[7956353],"length":1,"stats":{"Line":2},"fn_name":null},{"line":173,"address":[7956383],"length":1,"stats":{"Line":2},"fn_name":null},{"line":174,"address":[7956413],"length":1,"stats":{"Line":2},"fn_name":null},{"line":175,"address":[7956493],"length":1,"stats":{"Line":2},"fn_name":null},{"line":176,"address":[7956505],"length":1,"stats":{"Line":2},"fn_name":null},{"line":177,"address":[7956557],"length":1,"stats":{"Line":2},"fn_name":null},{"line":178,"address":[7956587],"length":1,"stats":{"Line":2},"fn_name":null},{"line":181,"address":[7957261,7957121],"length":1,"stats":{"Line":4},"fn_name":null},{"line":183,"address":[7960619,7961630,7957253,7960576,7961769],"length":1,"stats":{"Line":6},"fn_name":"{closure#0}"},{"line":185,"address":[7960679,7960742],"length":1,"stats":{"Line":4},"fn_name":null},{"line":187,"address":[7960908,7960850],"length":1,"stats":{"Line":4},"fn_name":null},{"line":188,"address":[7961155],"length":1,"stats":{"Line":2},"fn_name":null},{"line":189,"address":[7960926],"length":1,"stats":{"Line":2},"fn_name":null},{"line":191,"address":[7961808,7961830],"length":1,"stats":{"Line":4},"fn_name":"{closure#0}"},{"line":193,"address":[7961008],"length":1,"stats":{"Line":2},"fn_name":null},{"line":194,"address":[7961104],"length":1,"stats":{"Line":2},"fn_name":null},{"line":200,"address":[7961856,7962356,7962332,7957331],"length":1,"stats":{"Line":3},"fn_name":"{closure#1}"},{"line":201,"address":[7961878,7962115],"length":1,"stats":{"Line":2},"fn_name":null},{"line":202,"address":[7961894,7961990],"length":1,"stats":{"Line":2},"fn_name":null},{"line":204,"address":[7962059],"length":1,"stats":{"Line":1},"fn_name":null},{"line":208,"address":[7957499],"length":1,"stats":{"Line":2},"fn_name":null},{"line":209,"address":[7958223,7957511],"length":1,"stats":{"Line":3},"fn_name":null},{"line":210,"address":[7957537],"length":1,"stats":{"Line":1},"fn_name":null},{"line":211,"address":[7957600,7957673],"length":1,"stats":{"Line":2},"fn_name":null},{"line":212,"address":[7957855],"length":1,"stats":{"Line":1},"fn_name":null},{"line":216,"address":[7957515],"length":1,"stats":{"Line":2},"fn_name":null},{"line":219,"address":[7958330],"length":1,"stats":{"Line":2},"fn_name":null},{"line":221,"address":[7958650],"length":1,"stats":{"Line":2},"fn_name":null}],"covered":91,"coverable":91},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","openapi","router.rs"],"content":"use super::{\n\tbuilder::OpenapiBuilder,\n\thandler::{OpenapiDocHandler, OpenapiSpecHandler},\n\toperation::OperationDescription\n};\nuse crate::{routing::*, EndpointWithSchema, ResourceWithSchema, ResponseSchema};\nuse gotham::{\n\thyper::{Method, StatusCode},\n\tpipeline::PipelineHandleChain,\n\tprelude::*,\n\trouter::builder::{RouterBuilder, ScopeBuilder}\n};\nuse lazy_regex::regex_replace_all;\nuse openapi_type::OpenapiType;\nuse std::{collections::HashMap, panic::RefUnwindSafe};\n\n/// This trait adds the `openapi_spec` and `openapi_doc` method to an OpenAPI-aware router.\npub trait GetOpenapi {\n\t/// Register a GET route to `path` that returns the OpenAPI specification in JSON format.\n\tfn openapi_spec(&mut self, path: &str);\n\n\t/// Register a GET route to `path` that returns the OpenAPI documentation in HTML format.\n\tfn openapi_doc(&mut self, path: &str);\n}\n\n#[derive(Debug)]\npub struct OpenapiRouter<'a, D> {\n\tpub(crate) router: &'a mut D,\n\tpub(crate) scope: Option<&'a str>,\n\tpub(crate) openapi_builder: &'a mut OpenapiBuilder\n}\n\nmacro_rules! implOpenapiRouter {\n\t($implType:ident) => {\n\t\timpl<'a, 'b, C, P> OpenapiRouter<'a, $implType<'b, C, P>>\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tpub fn scope(&mut self, path: &str, callback: F)\n\t\t\twhere\n\t\t\t\tF: FnOnce(&mut OpenapiRouter<'_, ScopeBuilder<'_, C, P>>)\n\t\t\t{\n\t\t\t\tlet mut openapi_builder = self.openapi_builder.clone();\n\t\t\t\tlet new_scope = self\n\t\t\t\t\t.scope\n\t\t\t\t\t.map(|scope| format!(\"{scope}/{path}\").replace(\"//\", \"/\"));\n\t\t\t\tself.router.scope(path, |router| {\n\t\t\t\t\tlet mut router = OpenapiRouter {\n\t\t\t\t\t\trouter,\n\t\t\t\t\t\tscope: Some(new_scope.as_ref().map(String::as_ref).unwrap_or(path)),\n\t\t\t\t\t\topenapi_builder: &mut openapi_builder\n\t\t\t\t\t};\n\t\t\t\t\tcallback(&mut router);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\timpl<'a, 'b, C, P> GetOpenapi for OpenapiRouter<'a, $implType<'b, C, P>>\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn openapi_spec(&mut self, path: &str) {\n\t\t\t\tself.router\n\t\t\t\t\t.get(path)\n\t\t\t\t\t.to_new_handler(OpenapiSpecHandler::new(\n\t\t\t\t\t\tself.openapi_builder.openapi.clone()\n\t\t\t\t\t));\n\t\t\t}\n\n\t\t\tfn openapi_doc(&mut self, path: &str) {\n\t\t\t\tself.router\n\t\t\t\t\t.get(path)\n\t\t\t\t\t.to_new_handler(OpenapiDocHandler::new(self.openapi_builder.openapi.clone()));\n\t\t\t}\n\t\t}\n\n\t\timpl<'a, 'b, C, P> DrawResourcesWithSchema for OpenapiRouter<'a, $implType<'b, C, P>>\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn resource(&mut self, mut path: &str) {\n\t\t\t\tif path.starts_with('/') {\n\t\t\t\t\tpath = &path[1..];\n\t\t\t\t}\n\t\t\t\tR::setup((self, path));\n\t\t\t}\n\t\t}\n\n\t\timpl<'a, 'b, C, P> DrawResourceRoutesWithSchema\n\t\t\tfor (&mut OpenapiRouter<'a, $implType<'b, C, P>>, &str)\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn endpoint(&mut self) {\n\t\t\t\tlet mut responses: HashMap = HashMap::new();\n\t\t\t\tfor code in E::Output::status_codes() {\n\t\t\t\t\tresponses.insert(\n\t\t\t\t\t\tcode,\n\t\t\t\t\t\t(self.0).openapi_builder.add_schema(E::Output::schema(code))\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlet mut path = format!(\"{}/{}\", self.0.scope.unwrap_or_default(), self.1);\n\t\t\t\tlet mut descr = OperationDescription::new::(responses, &path);\n\t\t\t\tif E::has_placeholders() {\n\t\t\t\t\tdescr.set_path_params(E::Placeholders::schema());\n\t\t\t\t}\n\t\t\t\tif E::needs_params() {\n\t\t\t\t\tdescr.set_query_params(E::Params::schema());\n\t\t\t\t}\n\t\t\t\tif E::needs_body() {\n\t\t\t\t\tlet body_schema = (self.0).openapi_builder.add_schema(E::Body::schema());\n\t\t\t\t\tdescr.set_body::(body_schema);\n\t\t\t\t}\n\n\t\t\t\tlet uri: &str = &E::uri();\n\t\t\t\tlet uri =\n\t\t\t\t\tregex_replace_all!(r#\"(^|/):([^/]+)(/|$)\"#, uri, |_, prefix, name, suffix| {\n\t\t\t\t\t\tformat!(\"{prefix}{{{name}}}{suffix}\")\n\t\t\t\t\t});\n\t\t\t\tif !uri.is_empty() {\n\t\t\t\t\tpath = format!(\"{path}/{uri}\");\n\t\t\t\t}\n\n\t\t\t\tlet op = descr.into_operation();\n\t\t\t\tlet mut item = (self.0).openapi_builder.remove_path(&path);\n\t\t\t\tmatch E::http_method() {\n\t\t\t\t\tMethod::GET => item.get = Some(op),\n\t\t\t\t\tMethod::PUT => item.put = Some(op),\n\t\t\t\t\tMethod::POST => item.post = Some(op),\n\t\t\t\t\tMethod::DELETE => item.delete = Some(op),\n\t\t\t\t\tMethod::OPTIONS => item.options = Some(op),\n\t\t\t\t\tMethod::HEAD => item.head = Some(op),\n\t\t\t\t\tMethod::PATCH => item.patch = Some(op),\n\t\t\t\t\tMethod::TRACE => item.trace = Some(op),\n\t\t\t\t\tmethod => {\n\t\t\t\t\t\twarn!(\"Ignoring unsupported method '{method}' in OpenAPI Specification\")\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\t(self.0).openapi_builder.add_path(path, item);\n\n\t\t\t\t(&mut *(self.0).router, self.1).endpoint::()\n\t\t\t}\n\t\t}\n\t};\n}\n\nimplOpenapiRouter!(RouterBuilder);\nimplOpenapiRouter!(ScopeBuilder);\n","traces":[{"line":40,"address":[6855962,6854925,6855997,6854890,6855664,6854592],"length":1,"stats":{"Line":2},"fn_name":null},{"line":44,"address":[6854619,6855691,6855765,6854693],"length":1,"stats":{"Line":4},"fn_name":null},{"line":45,"address":[6854698,6855770,6855788,6854716],"length":1,"stats":{"Line":4},"fn_name":null},{"line":47,"address":[6854944,6855783,6856304,6855089,6856449,6855036,6856396,6854711],"length":1,"stats":{"Line":5},"fn_name":"{closure#0}<(), (), openapi_supports_scope::openapi_supports_scope::{closure#0}::{closure#0}::{closure#0}::{closure_env#0}>"},{"line":48,"address":[6856016,6854776,6855633,6855376,6855848,6856273],"length":1,"stats":{"Line":4},"fn_name":"{closure#1}<(), (), openapi_supports_scope::openapi_supports_scope::{closure#0}::{closure#0}::{closure#0}::{closure_env#0}>"},{"line":49,"address":[6856208,6856041,6855401,6855568],"length":1,"stats":{"Line":4},"fn_name":null},{"line":51,"address":[6855411,6855473,6856113,6856051],"length":1,"stats":{"Line":4},"fn_name":null},{"line":52,"address":[6856204,6855564],"length":1,"stats":{"Line":2},"fn_name":null},{"line":54,"address":[6855598,6856238],"length":1,"stats":{"Line":2},"fn_name":null},{"line":64,"address":[6856948,6856768,6856924],"length":1,"stats":{"Line":2},"fn_name":"openapi_spec<(), ()>"},{"line":65,"address":[6856897,6856798],"length":1,"stats":{"Line":4},"fn_name":null},{"line":67,"address":[6856876],"length":1,"stats":{"Line":2},"fn_name":null},{"line":68,"address":[6856826],"length":1,"stats":{"Line":2},"fn_name":null},{"line":84,"address":[6856960,6857104],"length":1,"stats":{"Line":6},"fn_name":"resource<(), (), openapi_supports_scope::FooResource>"},{"line":85,"address":[6856984,6857101,6857128,6857245],"length":1,"stats":{"Line":7},"fn_name":null},{"line":86,"address":[6857198,6857054],"length":1,"stats":{"Line":1},"fn_name":null},{"line":88,"address":[6857158,6857014],"length":1,"stats":{"Line":6},"fn_name":null},{"line":98,"address":[6864448,6857248,6865072,6870645,6857872,6863445],"length":1,"stats":{"Line":9},"fn_name":"endpoint<(), (), openapi_supports_scope::read_all___gotham_restful_endpoint>"},{"line":99,"address":[6864501,6857301],"length":1,"stats":{"Line":9},"fn_name":null},{"line":100,"address":[6864650,6857514,6857450,6864714],"length":1,"stats":{"Line":18},"fn_name":null},{"line":101,"address":[6857816,6865016],"length":1,"stats":{"Line":9},"fn_name":null},{"line":103,"address":[6864940,6857740],"length":1,"stats":{"Line":9},"fn_name":null},{"line":106,"address":[6857891,6858183,6865276,6858076,6865383,6865091],"length":1,"stats":{"Line":27},"fn_name":null},{"line":107,"address":[6858542,6865742,6858317,6865517],"length":1,"stats":{"Line":9},"fn_name":null},{"line":108,"address":[6865709,6858509,6858606,6865806],"length":1,"stats":{"Line":18},"fn_name":null},{"line":109,"address":[6865826,6858626],"length":1,"stats":{"Line":4},"fn_name":null},{"line":111,"address":[6865812,6858681,6858612,6865881],"length":1,"stats":{"Line":18},"fn_name":null},{"line":112,"address":[6865901,6858701],"length":1,"stats":{"Line":1},"fn_name":null},{"line":114,"address":[6865956,6858687,6858756,6865887],"length":1,"stats":{"Line":18},"fn_name":null},{"line":115,"address":[6865988,6858788],"length":1,"stats":{"Line":2},"fn_name":null},{"line":116,"address":[6858893,6866093],"length":1,"stats":{"Line":2},"fn_name":null},{"line":119,"address":[6858923,6866218,6865970,6859018,6858770,6866123],"length":1,"stats":{"Line":27},"fn_name":null},{"line":121,"address":[6863519,6863968,6864384,6864417,6871200,6864385,6864401,6871169,6871617,6863488,6871632,6870719,6864432,6871168,6864433,6871184,6859056,6866256,6871185,6864400,6870688,6871616,6864416,6871633],"length":1,"stats":{"Line":62},"fn_name":"{closure#0}<(), (), openapi_supports_scope::read_all___gotham_restful_endpoint>"},{"line":122,"address":[6871475,6864243],"length":1,"stats":{"Line":4},"fn_name":null},{"line":124,"address":[6866417,6859826,6866322,6859122,6859217,6867026],"length":1,"stats":{"Line":23},"fn_name":null},{"line":125,"address":[6859542,6866742],"length":1,"stats":{"Line":5},"fn_name":null},{"line":128,"address":[6859244,6867039,6859839,6866444],"length":1,"stats":{"Line":18},"fn_name":null},{"line":129,"address":[6859958,6867158,6867047,6859847],"length":1,"stats":{"Line":18},"fn_name":null},{"line":130,"address":[6860057,6860105,6867305,6867257],"length":1,"stats":{"Line":18},"fn_name":null},{"line":131,"address":[6869475,6862275,6860365,6867565],"length":1,"stats":{"Line":7},"fn_name":null},{"line":132,"address":[6867801,6862047,6869247,6860601],"length":1,"stats":{"Line":1},"fn_name":null},{"line":133,"address":[6867683,6860483,6869361,6862161],"length":1,"stats":{"Line":0},"fn_name":null},{"line":134,"address":[6860719,6861933,6869133,6867919],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[6867447,6869575,6860247,6862375],"length":1,"stats":{"Line":0},"fn_name":null},{"line":136,"address":[6860837,6868037,6861819,6869019],"length":1,"stats":{"Line":0},"fn_name":null},{"line":137,"address":[6861037,6868797,6868237,6861597],"length":1,"stats":{"Line":1},"fn_name":null},{"line":138,"address":[6860937,6861705,6868905,6868137],"length":1,"stats":{"Line":0},"fn_name":null},{"line":139,"address":[6860159,6867359],"length":1,"stats":{"Line":0},"fn_name":null},{"line":140,"address":[6867399,6868388,6868613,6861246,6861413,6868446,6860199,6861188],"length":1,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[6862527,6869727],"length":1,"stats":{"Line":9},"fn_name":null},{"line":145,"address":[6869941,6862741],"length":1,"stats":{"Line":9},"fn_name":null}],"covered":44,"coverable":51},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","auth_result.rs"],"content":"use crate::{IntoResponseError, Response};\nuse gotham::{hyper::StatusCode, mime::TEXT_PLAIN_UTF_8};\nuse gotham_restful_derive::ResourceError;\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiSchema, OpenapiType};\n\n/// This is an error type that always yields a _403 Forbidden_ response. This type\n/// is best used in combination with [`AuthSuccess`] or [`AuthResult`].\n#[derive(Clone, Debug)]\npub struct AuthError(String);\n\nimpl AuthError {\n\tpub fn new>(msg: T) -> Self {\n\t\tSelf(msg.into())\n\t}\n}\n\nimpl IntoResponseError for AuthError {\n\t// TODO why does this need to be serde_json::Error ?!?\n\ttype Err = serde_json::Error;\n\n\tfn into_response_error(self) -> Result {\n\t\tOk(Response::new(\n\t\t\tStatusCode::FORBIDDEN,\n\t\t\tself.0,\n\t\t\tSome(TEXT_PLAIN_UTF_8)\n\t\t))\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\tfn status_codes() -> Vec {\n\t\tvec![StatusCode::FORBIDDEN]\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::FORBIDDEN);\n\t\t as OpenapiType>::schema()\n\t}\n}\n\n/// This return type can be used to wrap any type implementing [IntoResponse](crate::IntoResponse)\n/// that can only be returned if the client is authenticated. Otherwise, an empty _403 Forbidden_\n/// response will be issued.\n///\n/// Use can look something like this (assuming the `auth` feature is enabled):\n///\n/// ```rust\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # #[cfg(feature = \"auth\")]\n/// # mod auth_feature_enabled {\n/// # use gotham::state::State;\n/// # use gotham_restful::*;\n/// # use serde::Deserialize;\n/// #\n/// # #[derive(Resource)]\n/// # #[resource(read_all)]\n/// # struct MyResource;\n/// #\n/// # #[derive(Clone, Deserialize)]\n/// # struct MyAuthData { exp : u64 }\n/// #\n/// #[read_all]\n/// fn read_all(auth: AuthStatus) -> AuthSuccess {\n/// \tlet auth_data = auth.ok()?;\n/// \t// do something\n/// \tOk(NoContent::default())\n/// }\n/// # }\n/// ```\npub type AuthSuccess = Result;\n\n/// This is an error type that either yields a _403 Forbidden_ response if produced\n/// from an authentication error, or delegates to another error type. This type is\n/// best used with [`AuthResult`].\n#[derive(Debug, Clone, ResourceError)]\npub enum AuthErrorOrOther {\n\tForbidden(#[from] AuthError),\n\n\t#[status(INTERNAL_SERVER_ERROR)]\n\t#[display(\"{0}\")]\n\tOther(E)\n}\n\nmod private {\n\tuse gotham::handler::HandlerError;\n\tpub trait Sealed {}\n\timpl> Sealed for E {}\n}\n\nimpl From for AuthErrorOrOther\nwhere\n\t// TODO https://github.com/msrd0/gotham_restful/issues/20\n\tF: private::Sealed + Into\n{\n\tfn from(err: F) -> Self {\n\t\tSelf::Other(err.into())\n\t}\n}\n\n/// This return type can be used to wrap any type implementing [IntoResponse](crate::IntoResponse)\n/// that can only be returned if the client is authenticated. Otherwise, an empty _403 Forbidden_\n/// response will be issued.\n///\n/// Use can look something like this (assuming the `auth` feature is enabled):\n///\n/// ```\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # #[cfg(feature = \"auth\")]\n/// # mod auth_feature_enabled {\n/// # use gotham::state::State;\n/// # use gotham_restful::*;\n/// # use serde::Deserialize;\n/// # use std::io;\n/// #\n/// # #[derive(Resource)]\n/// # #[resource(read_all)]\n/// # struct MyResource;\n/// #\n/// # #[derive(Clone, Deserialize)]\n/// # struct MyAuthData { exp : u64 }\n/// #\n/// #[read_all]\n/// fn read_all(auth: AuthStatus) -> AuthResult {\n/// \tlet auth_data = auth.ok()?;\n/// \t// do something\n/// \tOk(NoContent::default().into())\n/// }\n/// # }\n/// ```\npub type AuthResult = Result>;\n","traces":[{"line":13,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":14,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":22,"address":[7829696],"length":1,"stats":{"Line":0},"fn_name":"into_response_error"},{"line":23,"address":[6333703],"length":1,"stats":{"Line":0},"fn_name":null},{"line":25,"address":[6427841],"length":1,"stats":{"Line":0},"fn_name":null},{"line":26,"address":[8021604],"length":1,"stats":{"Line":0},"fn_name":null},{"line":31,"address":[8021728,8021839],"length":1,"stats":{"Line":1},"fn_name":"status_codes"},{"line":32,"address":[6428088,6428013],"length":1,"stats":{"Line":1},"fn_name":null},{"line":36,"address":[7914864],"length":1,"stats":{"Line":1},"fn_name":"schema"},{"line":38,"address":[8021952],"length":1,"stats":{"Line":1},"fn_name":null},{"line":96,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":4,"coverable":12},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","mod.rs"],"content":"use futures_util::future::{self, BoxFuture, FutureExt};\nuse gotham::{\n\thandler::HandlerError,\n\thyper::{\n\t\theader::{HeaderMap, HeaderName, HeaderValue},\n\t\tBody, StatusCode\n\t},\n\tmime::{Mime, APPLICATION_JSON, STAR_STAR}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiSchema, OpenapiType};\nuse serde::Serialize;\n#[cfg(feature = \"errorlog\")]\nuse std::fmt::Display;\nuse std::{convert::Infallible, fmt::Debug, future::Future, pin::Pin};\n\nmod auth_result;\n#[allow(unreachable_pub)]\npub use auth_result::{AuthError, AuthErrorOrOther, AuthResult, AuthSuccess};\n\nmod no_content;\n#[allow(unreachable_pub)]\npub use no_content::NoContent;\n\nmod raw;\n#[allow(unreachable_pub)]\npub use raw::Raw;\n\nmod redirect;\n#[allow(unreachable_pub)]\npub use redirect::Redirect;\n\nmod result;\n#[allow(unreachable_pub)]\npub use result::IntoResponseError;\n\nmod success;\n#[allow(unreachable_pub)]\npub use success::Success;\n\npub(crate) trait OrAllTypes {\n\tfn or_all_types(self) -> Vec;\n}\n\nimpl OrAllTypes for Option> {\n\tfn or_all_types(self) -> Vec {\n\t\tself.unwrap_or_else(|| vec![STAR_STAR])\n\t}\n}\n\n/// A response, used to create the final gotham response from.\n///\n/// This type is not meant to be used as the return type of endpoint handlers. While it can be\n/// freely used without the `openapi` feature, it is more complicated to use when you enable it,\n/// since this type does not store any schema information. You can attach schema information\n/// like so:\n///\n/// ```rust\n/// # #[cfg(feature = \"openapi\")] mod example {\n/// # use gotham::hyper::StatusCode;\n/// # use gotham_restful::*;\n/// # use openapi_type::*;\n/// fn schema(code: StatusCode) -> OpenapiSchema {\n/// \tassert_eq!(code, StatusCode::ACCEPTED);\n/// \t<()>::schema()\n/// }\n///\n/// fn status_codes() -> Vec {\n/// \tvec![StatusCode::ACCEPTED]\n/// }\n///\n/// #[create(schema = \"schema\", status_codes = \"status_codes\")]\n/// fn create(body: Raw>) {}\n/// # }\n/// ```\n#[derive(Debug)]\npub struct Response {\n\tpub(crate) status: StatusCode,\n\tpub(crate) body: Body,\n\tpub(crate) mime: Option,\n\tpub(crate) headers: HeaderMap\n}\n\nimpl Response {\n\t/// Create a new [Response] from raw data.\n\t#[must_use = \"Creating a response is pointless if you don't use it\"]\n\tpub fn new>(status: StatusCode, body: B, mime: Option) -> Self {\n\t\tSelf {\n\t\t\tstatus,\n\t\t\tbody: body.into(),\n\t\t\tmime,\n\t\t\theaders: Default::default()\n\t\t}\n\t}\n\n\t/// Create a [Response] with mime type json from already serialized data.\n\t#[must_use = \"Creating a response is pointless if you don't use it\"]\n\tpub fn json>(status: StatusCode, body: B) -> Self {\n\t\tSelf {\n\t\t\tstatus,\n\t\t\tbody: body.into(),\n\t\t\tmime: Some(APPLICATION_JSON),\n\t\t\theaders: Default::default()\n\t\t}\n\t}\n\n\t/// Create a _204 No Content_ [Response].\n\t#[must_use = \"Creating a response is pointless if you don't use it\"]\n\tpub fn no_content() -> Self {\n\t\tSelf {\n\t\t\tstatus: StatusCode::NO_CONTENT,\n\t\t\tbody: Body::empty(),\n\t\t\tmime: None,\n\t\t\theaders: Default::default()\n\t\t}\n\t}\n\n\t/// Create an empty _403 Forbidden_ [Response].\n\t#[must_use = \"Creating a response is pointless if you don't use it\"]\n\tpub fn forbidden() -> Self {\n\t\tSelf {\n\t\t\tstatus: StatusCode::FORBIDDEN,\n\t\t\tbody: Body::empty(),\n\t\t\tmime: None,\n\t\t\theaders: Default::default()\n\t\t}\n\t}\n\n\t/// Return the status code of this [Response].\n\tpub fn status(&self) -> StatusCode {\n\t\tself.status\n\t}\n\n\t/// Return the mime type of this [Response].\n\tpub fn mime(&self) -> Option<&Mime> {\n\t\tself.mime.as_ref()\n\t}\n\n\t/// Add an HTTP header to the [Response].\n\tpub fn header(&mut self, name: HeaderName, value: HeaderValue) {\n\t\tself.headers.insert(name, value);\n\t}\n\n\tpub(crate) fn with_headers(mut self, headers: HeaderMap) -> Self {\n\t\tself.headers = headers;\n\t\tself\n\t}\n\n\t#[cfg(test)]\n\tpub(crate) fn full_body(\n\t\tmut self\n\t) -> Result, ::Error> {\n\t\tuse futures_executor::block_on;\n\t\tuse gotham::hyper::body::to_bytes;\n\n\t\tlet bytes: &[u8] = &block_on(to_bytes(&mut self.body))?;\n\t\tOk(bytes.to_vec())\n\t}\n}\n\nimpl IntoResponse for Response {\n\ttype Err = Infallible;\n\n\tfn into_response(self) -> BoxFuture<'static, Result> {\n\t\tfuture::ok(self).boxed()\n\t}\n}\n\n/// This trait needs to be implemented by every type returned from an endpoint to\n/// to provide the response.\npub trait IntoResponse {\n\ttype Err: Into + Send + Sync + 'static;\n\n\t/// Turn this into a response that can be returned to the browser. This api will likely\n\t/// change in the future.\n\tfn into_response(self) -> BoxFuture<'static, Result>;\n\n\t/// Return a list of supported mime types.\n\tfn accepted_types() -> Option> {\n\t\tNone\n\t}\n}\n\n/// Additional details for [IntoResponse] to be used with an OpenAPI-aware router.\n#[cfg(feature = \"openapi\")]\npub trait ResponseSchema {\n\t/// All status codes returned by this response. Returns `[StatusCode::OK]` by default.\n\tfn status_codes() -> Vec {\n\t\tvec![StatusCode::OK]\n\t}\n\n\t/// Return the schema of the response for the given status code. The code may\n\t/// only be one that was previously returned by [Self::status_codes]. The\n\t/// implementation should panic if that is not the case.\n\tfn schema(code: StatusCode) -> OpenapiSchema;\n}\n\n#[cfg(feature = \"openapi\")]\nmod private {\n\tpub trait Sealed {}\n}\n\n/// A trait provided to convert a resource's result to json, and provide an OpenAPI schema to the\n/// router. This trait is implemented for all types that implement [IntoResponse] and\n/// [ResponseSchema].\n#[cfg(feature = \"openapi\")]\npub trait IntoResponseWithSchema: IntoResponse + ResponseSchema + private::Sealed {}\n\n#[cfg(feature = \"openapi\")]\nimpl private::Sealed for R {}\n\n#[cfg(feature = \"openapi\")]\nimpl IntoResponseWithSchema for R {}\n\n/// The default json returned on an 500 Internal Server Error.\n#[derive(Debug, Serialize)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\npub(crate) struct ResourceError {\n\t/// This is always `true` and can be used to detect an error response without looking at the\n\t/// HTTP status code.\n\terror: bool,\n\t/// The error message.\n\tmessage: String\n}\n\nimpl From for ResourceError {\n\tfn from(message: T) -> Self {\n\t\tSelf {\n\t\t\terror: true,\n\t\t\tmessage: message.to_string()\n\t\t}\n\t}\n}\n\n#[cfg(feature = \"errorlog\")]\nfn errorlog(e: E) {\n\terror!(\"The handler encountered an error: {e}\");\n}\n\n#[cfg(not(feature = \"errorlog\"))]\nfn errorlog(_e: E) {}\n\nfn handle_error(e: E) -> Pin> + Send>>\nwhere\n\tE: Debug + IntoResponseError\n{\n\tlet msg = format!(\"{e:?}\");\n\tlet res = e.into_response_error();\n\tmatch &res {\n\t\tOk(res) if res.status.is_server_error() => errorlog(msg),\n\t\tErr(err) => {\n\t\t\terrorlog(msg);\n\t\t\terrorlog(format!(\"{err:?}\"));\n\t\t},\n\t\t_ => {}\n\t};\n\tfuture::ready(res).boxed()\n}\n\nimpl IntoResponse for Pin + Send>>\nwhere\n\tRes: IntoResponse + 'static\n{\n\ttype Err = Res::Err;\n\n\tfn into_response(self) -> Pin> + Send>> {\n\t\tself.then(IntoResponse::into_response).boxed()\n\t}\n\n\tfn accepted_types() -> Option> {\n\t\tRes::accepted_types()\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Pin + Send>>\nwhere\n\tRes: ResponseSchema\n{\n\tfn status_codes() -> Vec {\n\t\tRes::status_codes()\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tRes::schema(code)\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse futures_executor::block_on;\n\tuse thiserror::Error;\n\n\t#[derive(Debug, Default, Deserialize, Serialize)]\n\t#[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n\tstruct Msg {\n\t\tmsg: String\n\t}\n\n\t#[derive(Debug, Default, Error)]\n\t#[error(\"An Error\")]\n\tstruct MsgError;\n\n\t#[test]\n\tfn result_from_future() {\n\t\tlet nc = NoContent::default();\n\t\tlet res = block_on(nc.into_response()).unwrap();\n\n\t\tlet fut_nc = async move { NoContent::default() }.boxed();\n\t\tlet fut_res = block_on(fut_nc.into_response()).unwrap();\n\n\t\tassert_eq!(res.status, fut_res.status);\n\t\tassert_eq!(res.mime, fut_res.mime);\n\t\tassert_eq!(res.full_body().unwrap(), fut_res.full_body().unwrap());\n\t}\n}\n","traces":[{"line":46,"address":[7996336],"length":1,"stats":{"Line":3},"fn_name":"or_all_types"},{"line":47,"address":[7996344],"length":1,"stats":{"Line":9},"fn_name":null},{"line":87,"address":[6493472,6493856,6493886],"length":1,"stats":{"Line":8},"fn_name":null},{"line":90,"address":[6493563],"length":1,"stats":{"Line":9},"fn_name":null},{"line":92,"address":[6493697],"length":1,"stats":{"Line":9},"fn_name":null},{"line":98,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":101,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":102,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":103,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":109,"address":[7783888,7783915,7783696],"length":1,"stats":{"Line":2},"fn_name":null},{"line":112,"address":[7783718],"length":1,"stats":{"Line":2},"fn_name":null},{"line":114,"address":[7783732],"length":1,"stats":{"Line":2},"fn_name":null},{"line":120,"address":[7784155,7784128,7783936],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[7783958],"length":1,"stats":{"Line":0},"fn_name":null},{"line":125,"address":[7783972],"length":1,"stats":{"Line":0},"fn_name":null},{"line":130,"address":[7784176],"length":1,"stats":{"Line":1},"fn_name":null},{"line":131,"address":[7784181],"length":1,"stats":{"Line":1},"fn_name":null},{"line":135,"address":[7784192],"length":1,"stats":{"Line":1},"fn_name":null},{"line":136,"address":[7784197],"length":1,"stats":{"Line":1},"fn_name":null},{"line":140,"address":[7784224],"length":1,"stats":{"Line":2},"fn_name":null},{"line":141,"address":[7784242],"length":1,"stats":{"Line":2},"fn_name":null},{"line":144,"address":[7784288,7784497],"length":1,"stats":{"Line":2},"fn_name":null},{"line":145,"address":[7784320,7784450],"length":1,"stats":{"Line":4},"fn_name":null},{"line":146,"address":[7784477],"length":1,"stats":{"Line":2},"fn_name":null},{"line":150,"address":[6158758,6158732,6158336],"length":1,"stats":{"Line":1},"fn_name":null},{"line":156,"address":[6158366,6158423,6158653],"length":1,"stats":{"Line":3},"fn_name":null},{"line":157,"address":[6158669],"length":1,"stats":{"Line":2},"fn_name":null},{"line":164,"address":[7784528],"length":1,"stats":{"Line":0},"fn_name":"into_response"},{"line":165,"address":[7784538],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[6762000],"length":1,"stats":{"Line":7},"fn_name":"accepted_types>"},{"line":180,"address":[7996371],"length":1,"stats":{"Line":7},"fn_name":null},{"line":188,"address":[6762016,6762127],"length":1,"stats":{"Line":4},"fn_name":"status_codes>"},{"line":189,"address":[6762029,6762104],"length":1,"stats":{"Line":4},"fn_name":null},{"line":227,"address":[6551952,6552076],"length":1,"stats":{"Line":2},"fn_name":"from"},{"line":230,"address":[6551981],"length":1,"stats":{"Line":2},"fn_name":null},{"line":236,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":237,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":243,"address":[6162640,6163596,6160528,6161555,6163667,6161584,6160499,6159472,6160428,6161484,6162540,6162611],"length":1,"stats":{"Line":1},"fn_name":"handle_error"},{"line":247,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":248,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":249,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":250,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":251,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":252,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":253,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":255,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":257,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":266,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":267,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":270,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":271,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":280,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":281,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":284,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":285,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":40,"coverable":55},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","no_content.rs"],"content":"use super::{handle_error, IntoResponse};\n#[cfg(feature = \"openapi\")]\nuse crate::ResponseSchema;\nuse crate::{IntoResponseError, Response};\nuse futures_util::{future, future::FutureExt};\n#[cfg(feature = \"openapi\")]\nuse gotham::hyper::StatusCode;\nuse gotham::{\n\thyper::header::{HeaderMap, HeaderValue, IntoHeaderName},\n\tmime::Mime\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiSchema, OpenapiType};\nuse std::{fmt::Debug, future::Future, pin::Pin};\n\n/// This is the return type of a resource that doesn't actually return something. It will result\n/// in a _204 No Content_ answer by default. You don't need to use this type directly if using\n/// the function attributes:\n///\n/// ```\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # mod doc_tests_are_broken {\n/// # use gotham::state::State;\n/// # use gotham_restful::*;\n/// #\n/// # #[derive(Resource)]\n/// # #[resource(read_all)]\n/// # struct MyResource;\n/// #\n/// #[read_all]\n/// fn read_all() {\n/// \t// do something\n/// }\n/// # }\n/// ```\n#[derive(Clone, Debug, Default)]\npub struct NoContent {\n\theaders: HeaderMap\n}\n\nimpl From<()> for NoContent {\n\tfn from(_: ()) -> Self {\n\t\tSelf::default()\n\t}\n}\n\nimpl NoContent {\n\t/// Set a custom HTTP header. If a header with this name was set before, its value is being updated.\n\tpub fn header(&mut self, name: K, value: HeaderValue) {\n\t\tself.headers.insert(name, value);\n\t}\n\n\t/// Allow manipulating HTTP headers.\n\tpub fn headers_mut(&mut self) -> &mut HeaderMap {\n\t\t&mut self.headers\n\t}\n}\n\nimpl IntoResponse for NoContent {\n\t// TODO this shouldn't be a serde_json::Error\n\ttype Err = serde_json::Error; // just for easier handling of `Result`\n\n\t/// This will always be a _204 No Content_ together with an empty string.\n\tfn into_response(self) -> Pin> + Send>> {\n\t\tfuture::ok(Response::no_content().with_headers(self.headers)).boxed()\n\t}\n\n\tfn accepted_types() -> Option> {\n\t\tSome(Vec::new())\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for NoContent {\n\tfn status_codes() -> Vec {\n\t\tvec![StatusCode::NO_CONTENT]\n\t}\n\n\t/// Returns the schema of the `()` type.\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::NO_CONTENT);\n\t\t<()>::schema()\n\t}\n}\n\nimpl IntoResponse for Result\nwhere\n\tE: Debug + IntoResponseError\n{\n\ttype Err = serde_json::Error;\n\n\tfn into_response(\n\t\tself\n\t) -> Pin> + Send>> {\n\t\tmatch self {\n\t\t\tOk(nc) => nc.into_response(),\n\t\t\tErr(e) => handle_error(e)\n\t\t}\n\t}\n\n\tfn accepted_types() -> Option> {\n\t\tNoContent::accepted_types()\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Result\nwhere\n\tE: Debug + IntoResponseError\n{\n\tfn status_codes() -> Vec {\n\t\tlet mut status_codes = E::status_codes();\n\t\tstatus_codes.push(StatusCode::NO_CONTENT);\n\t\tstatus_codes\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tmatch code {\n\t\t\tStatusCode::NO_CONTENT => ::schema(StatusCode::NO_CONTENT),\n\t\t\tcode => E::schema(code)\n\t\t}\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse futures_executor::block_on;\n\tuse gotham::hyper::{header::ACCESS_CONTROL_ALLOW_ORIGIN, StatusCode};\n\tuse thiserror::Error;\n\n\t#[derive(Debug, Default, Error)]\n\t#[error(\"An Error\")]\n\tstruct MsgError;\n\n\t#[test]\n\tfn no_content_has_empty_response() {\n\t\tlet no_content = NoContent::default();\n\t\tlet res = block_on(no_content.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::NO_CONTENT);\n\t\tassert_eq!(res.mime, None);\n\t\tassert_eq!(res.full_body().unwrap(), &[] as &[u8]);\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(NoContent::status_codes(), vec![StatusCode::NO_CONTENT]);\n\t}\n\n\t#[test]\n\tfn no_content_result() {\n\t\tlet no_content: Result = Ok(NoContent::default());\n\t\tlet res = block_on(no_content.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::NO_CONTENT);\n\t\tassert_eq!(res.mime, None);\n\t\tassert_eq!(res.full_body().unwrap(), &[] as &[u8]);\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(>::status_codes(), vec![\n\t\t\tStatusCode::INTERNAL_SERVER_ERROR,\n\t\t\tStatusCode::NO_CONTENT\n\t\t]);\n\t}\n\n\t#[test]\n\tfn no_content_custom_headers() {\n\t\tlet mut no_content = NoContent::default();\n\t\tno_content.header(ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static(\"*\"));\n\t\tlet res = block_on(no_content.into_response()).expect(\"didn't expect error response\");\n\t\tlet cors = res.headers.get(ACCESS_CONTROL_ALLOW_ORIGIN);\n\t\tassert_eq!(cors.map(|value| value.to_str().unwrap()), Some(\"*\"));\n\t}\n}\n","traces":[{"line":42,"address":[6326480],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":43,"address":[7813228],"length":1,"stats":{"Line":0},"fn_name":null},{"line":49,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":50,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":54,"address":[7813248],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[7920256,7920560,7920529],"length":1,"stats":{"Line":2},"fn_name":"into_response"},{"line":65,"address":[6326621,6326540],"length":1,"stats":{"Line":4},"fn_name":null},{"line":68,"address":[7813584],"length":1,"stats":{"Line":4},"fn_name":"accepted_types"},{"line":69,"address":[6326861],"length":1,"stats":{"Line":4},"fn_name":null},{"line":75,"address":[7920751,7920640],"length":1,"stats":{"Line":2},"fn_name":"status_codes"},{"line":76,"address":[6327000,6326925],"length":1,"stats":{"Line":2},"fn_name":null},{"line":80,"address":[6327040],"length":1,"stats":{"Line":2},"fn_name":"schema"},{"line":82,"address":[6327136],"length":1,"stats":{"Line":2},"fn_name":null},{"line":92,"address":[6153040],"length":1,"stats":{"Line":1},"fn_name":"into_response"},{"line":95,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":96,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":97,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":102,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":111,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":112,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":113,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":114,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":117,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":119,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":120,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":17,"coverable":27},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","raw.rs"],"content":"use super::{handle_error, IntoResponse, IntoResponseError};\nuse crate::{types::ResourceType, FromBody, RequestBody, Response};\n#[cfg(feature = \"openapi\")]\nuse crate::{IntoResponseWithSchema, ResponseSchema};\nuse futures_core::future::Future;\nuse futures_util::{future, future::FutureExt};\nuse gotham::{\n\thyper::{\n\t\tbody::{Body, Bytes},\n\t\tStatusCode\n\t},\n\tmime::Mime\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiSchema, OpenapiType, Visitor};\nuse serde_json::error::Error as SerdeJsonError;\nuse std::{convert::Infallible, fmt::Debug, pin::Pin};\n\n/// This type can be used both as a raw request body, as well as as a raw response. However, all types\n/// of request bodies are accepted by this type. It is therefore recommended to derive your own type\n/// from [RequestBody] and only use this when you need to return a raw response. This is a usage\n/// example that simply returns its body:\n///\n/// ```rust,no_run\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # use gotham::router::builder::*;\n/// # use gotham_restful::*;\n/// #[derive(Resource)]\n/// #[resource(create)]\n/// struct ImageResource;\n///\n/// #[create]\n/// fn create(body: Raw>) -> Raw> {\n/// \tbody\n/// }\n/// # fn main() {\n/// # \tgotham::start(\"127.0.0.1:8080\", build_simple_router(|route| {\n/// # \t\troute.resource::(\"img\");\n/// # \t}));\n/// # }\n/// ```\n#[derive(Debug)]\npub struct Raw {\n\tpub raw: T,\n\tpub mime: Mime\n}\n\nimpl Raw {\n\tpub fn new(raw: T, mime: Mime) -> Self {\n\t\tSelf { raw, mime }\n\t}\n}\n\nimpl AsMut for Raw\nwhere\n\tT: AsMut\n{\n\tfn as_mut(&mut self) -> &mut U {\n\t\tself.raw.as_mut()\n\t}\n}\n\nimpl AsRef for Raw\nwhere\n\tT: AsRef\n{\n\tfn as_ref(&self) -> &U {\n\t\tself.raw.as_ref()\n\t}\n}\n\nimpl Clone for Raw {\n\tfn clone(&self) -> Self {\n\t\tSelf {\n\t\t\traw: self.raw.clone(),\n\t\t\tmime: self.mime.clone()\n\t\t}\n\t}\n}\n\nimpl From<&'a [u8]>> FromBody for Raw {\n\ttype Err = Infallible;\n\n\tfn from_body(body: Bytes, mime: Mime) -> Result {\n\t\tOk(Self::new(body.as_ref().into(), mime))\n\t}\n}\n\nimpl RequestBody for Raw where Raw: FromBody + ResourceType {}\n\n#[cfg(feature = \"openapi\")]\nimpl OpenapiType for Raw {\n\tfn visit_type(visitor: &mut V) {\n\t\tvisitor.visit_binary()\n\t}\n}\n\nimpl> IntoResponse for Raw\nwhere\n\tSelf: Send\n{\n\ttype Err = SerdeJsonError; // just for easier handling of `Result, E>`\n\n\tfn into_response(\n\t\tself\n\t) -> Pin> + Send>> {\n\t\tfuture::ok(Response::new(StatusCode::OK, self.raw, Some(self.mime))).boxed()\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl> ResponseSchema for Raw\nwhere\n\tSelf: Send\n{\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::OK);\n\t\t::schema()\n\t}\n}\n\nimpl IntoResponse for Result, E>\nwhere\n\tRaw: IntoResponse,\n\tE: Debug + IntoResponseError as IntoResponse>::Err>\n{\n\ttype Err = E::Err;\n\n\tfn into_response(self) -> Pin> + Send>> {\n\t\tmatch self {\n\t\t\tOk(raw) => raw.into_response(),\n\t\t\tErr(e) => handle_error(e)\n\t\t}\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Result, E>\nwhere\n\tRaw: IntoResponseWithSchema,\n\tE: Debug + IntoResponseError as IntoResponse>::Err>\n{\n\tfn status_codes() -> Vec {\n\t\tlet mut status_codes = E::status_codes();\n\t\tstatus_codes.push(StatusCode::OK);\n\t\tstatus_codes\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tmatch code {\n\t\t\tStatusCode::OK => as ResponseSchema>::schema(StatusCode::OK),\n\t\t\tcode => E::schema(code)\n\t\t}\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse futures_executor::block_on;\n\tuse gotham::mime::TEXT_PLAIN;\n\tuse thiserror::Error;\n\n\t#[derive(Debug, Default, Error)]\n\t#[error(\"An Error\")]\n\tstruct MsgError;\n\n\t#[test]\n\tfn raw_response() {\n\t\tlet msg = \"Test\";\n\t\tlet raw = Raw::new(msg, TEXT_PLAIN);\n\t\tlet res = block_on(raw.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::OK);\n\t\tassert_eq!(res.mime, Some(TEXT_PLAIN));\n\t\tassert_eq!(res.full_body().unwrap(), msg.as_bytes());\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(>::status_codes(), vec![StatusCode::OK]);\n\t}\n\n\t#[test]\n\tfn raw_result() {\n\t\tlet msg = \"Test\";\n\t\tlet raw: Result, MsgError> = Ok(Raw::new(msg, TEXT_PLAIN));\n\t\tlet res = block_on(raw.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::OK);\n\t\tassert_eq!(res.mime, Some(TEXT_PLAIN));\n\t\tassert_eq!(res.full_body().unwrap(), msg.as_bytes());\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(, MsgError>>::status_codes(), vec![\n\t\t\tStatusCode::INTERNAL_SERVER_ERROR,\n\t\t\tStatusCode::OK\n\t\t]);\n\t}\n}\n","traces":[{"line":49,"address":[6424704],"length":1,"stats":{"Line":7},"fn_name":null},{"line":58,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":59,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":67,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":68,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":75,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":76,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":84,"address":[6486547,6486224],"length":1,"stats":{"Line":1},"fn_name":"from_body>"},{"line":85,"address":[6486342,6486258],"length":1,"stats":{"Line":2},"fn_name":null},{"line":93,"address":[6847248],"length":1,"stats":{"Line":4},"fn_name":"visit_type<&[u8], openapi_type::visitor::openapi::OpenapiVisitor>"},{"line":94,"address":[6847253],"length":1,"stats":{"Line":4},"fn_name":null},{"line":104,"address":[6048400],"length":1,"stats":{"Line":5},"fn_name":"into_response<&str>"},{"line":107,"address":[6048407],"length":1,"stats":{"Line":5},"fn_name":null},{"line":116,"address":[6755296],"length":1,"stats":{"Line":3},"fn_name":"schema<&[u8]>"},{"line":118,"address":[6048755],"length":1,"stats":{"Line":3},"fn_name":null},{"line":129,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":130,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":131,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":132,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":144,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":145,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":146,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":149,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":150,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":151,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":152,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":16,"coverable":28},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","redirect.rs"],"content":"use super::{handle_error, IntoResponse};\nuse crate::{IntoResponseError, Response};\n#[cfg(feature = \"openapi\")]\nuse crate::{NoContent, ResponseSchema};\nuse futures_util::future::{BoxFuture, FutureExt, TryFutureExt};\nuse gotham::hyper::{\n\theader::{InvalidHeaderValue, LOCATION},\n\tBody, StatusCode\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiSchema;\nuse std::{error::Error as StdError, fmt::Debug};\nuse thiserror::Error;\n\n/// This is the return type of a resource that only returns a redirect. It will result\n/// in a _303 See Other_ answer, meaning the redirect will always result in a GET request\n/// on the target.\n///\n/// ```\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # mod doc_tests_are_broken {\n/// # use gotham::state::State;\n/// # use gotham_restful::*;\n/// #\n/// # #[derive(Resource)]\n/// # #[resource(read_all)]\n/// # struct MyResource;\n/// #\n/// #[read_all]\n/// fn read_all() -> Redirect {\n/// \tRedirect {\n/// \t\tto: \"http://localhost:8080/cool/new/location\".to_owned()\n/// \t}\n/// }\n/// # }\n/// ```\n#[derive(Clone, Debug, Default)]\npub struct Redirect {\n\tpub to: String\n}\n\nimpl IntoResponse for Redirect {\n\ttype Err = InvalidHeaderValue;\n\n\tfn into_response(self) -> BoxFuture<'static, Result> {\n\t\tasync move {\n\t\t\tlet mut res = Response::new(StatusCode::SEE_OTHER, Body::empty(), None);\n\t\t\tres.header(LOCATION, self.to.parse()?);\n\t\t\tOk(res)\n\t\t}\n\t\t.boxed()\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Redirect {\n\tfn status_codes() -> Vec {\n\t\tvec![StatusCode::SEE_OTHER]\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::SEE_OTHER);\n\t\t::schema(StatusCode::NO_CONTENT)\n\t}\n}\n\n// private type due to parent mod\n#[derive(Debug, Error)]\npub enum RedirectError {\n\t#[error(\"{0}\")]\n\tInvalidLocation(#[from] InvalidHeaderValue),\n\t#[error(\"{0}\")]\n\tOther(#[source] E)\n}\n\n#[allow(ambiguous_associated_items)] // an enum variant is not a type. never.\nimpl IntoResponse for Result\nwhere\n\tE: Debug + IntoResponseError,\n\t::Err: StdError + Sync\n{\n\ttype Err = RedirectError<::Err>;\n\n\tfn into_response(self) -> BoxFuture<'static, Result> {\n\t\tmatch self {\n\t\t\tOk(nc) => nc.into_response().map_err(Into::into).boxed(),\n\t\t\tErr(e) => handle_error(e).map_err(RedirectError::Other).boxed()\n\t\t}\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Result\nwhere\n\tE: Debug + IntoResponseError,\n\t::Err: StdError + Sync\n{\n\tfn status_codes() -> Vec {\n\t\tlet mut status_codes = E::status_codes();\n\t\tstatus_codes.push(StatusCode::SEE_OTHER);\n\t\tstatus_codes\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tmatch code {\n\t\t\tStatusCode::SEE_OTHER => ::schema(StatusCode::SEE_OTHER),\n\t\t\tcode => E::schema(code)\n\t\t}\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse futures_executor::block_on;\n\tuse gotham::hyper::StatusCode;\n\tuse thiserror::Error;\n\n\t#[derive(Debug, Default, Error)]\n\t#[error(\"An Error\")]\n\tstruct MsgError;\n\n\t#[test]\n\tfn redirect_response() {\n\t\tlet redir = Redirect {\n\t\t\tto: \"http://localhost/foo\".to_owned()\n\t\t};\n\t\tlet res = block_on(redir.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::SEE_OTHER);\n\t\tassert_eq!(res.mime, None);\n\t\tassert_eq!(\n\t\t\tres.headers.get(LOCATION).map(|hdr| hdr.to_str().unwrap()),\n\t\t\tSome(\"http://localhost/foo\")\n\t\t);\n\t\tassert_eq!(res.full_body().unwrap(), &[] as &[u8]);\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(Redirect::status_codes(), vec![StatusCode::SEE_OTHER]);\n\t}\n\n\t#[test]\n\tfn redirect_result() {\n\t\tlet redir: Result = Ok(Redirect {\n\t\t\tto: \"http://localhost/foo\".to_owned()\n\t\t});\n\t\tlet res = block_on(redir.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::SEE_OTHER);\n\t\tassert_eq!(res.mime, None);\n\t\tassert_eq!(\n\t\t\tres.headers.get(LOCATION).map(|hdr| hdr.to_str().unwrap()),\n\t\t\tSome(\"http://localhost/foo\")\n\t\t);\n\t\tassert_eq!(res.full_body().unwrap(), &[] as &[u8]);\n\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(>::status_codes(), vec![\n\t\t\tStatusCode::INTERNAL_SERVER_ERROR,\n\t\t\tStatusCode::SEE_OTHER\n\t\t]);\n\t}\n}\n","traces":[{"line":45,"address":[7888048],"length":1,"stats":{"Line":1},"fn_name":"into_response"},{"line":46,"address":[7888821,7888052,7888126,7888183,7888534,7888096],"length":1,"stats":{"Line":3},"fn_name":"{async_block#0}"},{"line":47,"address":[6153664,6153802],"length":1,"stats":{"Line":4},"fn_name":null},{"line":48,"address":[7888545,7888158,7888347,7888572,7888912,7889004],"length":1,"stats":{"Line":6},"fn_name":null},{"line":49,"address":[6154436],"length":1,"stats":{"Line":2},"fn_name":null},{"line":57,"address":[7889040,7889151],"length":1,"stats":{"Line":1},"fn_name":"status_codes"},{"line":58,"address":[6154599,6154525],"length":1,"stats":{"Line":1},"fn_name":null},{"line":61,"address":[7889168],"length":1,"stats":{"Line":0},"fn_name":"schema"},{"line":63,"address":[6154739],"length":1,"stats":{"Line":0},"fn_name":null},{"line":84,"address":[6154960],"length":1,"stats":{"Line":1},"fn_name":"into_response"},{"line":85,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":86,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":87,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":99,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":100,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":101,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":104,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":14,"coverable":21},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","result.rs"],"content":"use super::{handle_error, IntoResponse, ResourceError};\n#[cfg(feature = \"openapi\")]\nuse crate::ResponseSchema;\nuse crate::{Response, ResponseBody, Success};\nuse futures_core::future::Future;\nuse gotham::{\n\tanyhow::Error,\n\thyper::StatusCode,\n\tmime::{Mime, APPLICATION_JSON}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::{OpenapiSchema, OpenapiType};\nuse std::{fmt::Debug, pin::Pin};\n\npub trait IntoResponseError {\n\ttype Err: Debug + Send + 'static;\n\n\tfn into_response_error(self) -> Result;\n\n\t#[cfg(feature = \"openapi\")]\n\tfn status_codes() -> Vec;\n\n\t#[cfg(feature = \"openapi\")]\n\tfn schema(code: StatusCode) -> OpenapiSchema;\n}\n\nimpl IntoResponseError for E\nwhere\n\tE: Into\n{\n\ttype Err = serde_json::Error;\n\n\tfn into_response_error(self) -> Result {\n\t\tlet err: Error = self.into();\n\t\tlet err: ResourceError = err.into();\n\t\tOk(Response::json(\n\t\t\tStatusCode::INTERNAL_SERVER_ERROR,\n\t\t\tserde_json::to_string(&err)?\n\t\t))\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\tfn status_codes() -> Vec {\n\t\tvec![StatusCode::INTERNAL_SERVER_ERROR]\n\t}\n\n\t#[cfg(feature = \"openapi\")]\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::INTERNAL_SERVER_ERROR);\n\t\tResourceError::schema()\n\t}\n}\n\nimpl IntoResponse for Result\nwhere\n\tR: ResponseBody,\n\tE: Debug + IntoResponseError\n{\n\ttype Err = E::Err;\n\n\tfn into_response(self) -> Pin> + Send>> {\n\t\tmatch self {\n\t\t\tOk(r) => Success::from(r).into_response(),\n\t\t\tErr(e) => handle_error(e)\n\t\t}\n\t}\n\n\tfn accepted_types() -> Option> {\n\t\tSome(vec![APPLICATION_JSON])\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Result\nwhere\n\tR: ResponseBody,\n\tE: Debug + IntoResponseError\n{\n\tfn status_codes() -> Vec {\n\t\tlet mut status_codes = E::status_codes();\n\t\tstatus_codes.push(StatusCode::OK);\n\t\tstatus_codes\n\t}\n\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tmatch code {\n\t\t\tStatusCode::OK => R::schema(),\n\t\t\tcode => E::schema(code)\n\t\t}\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse crate::response::OrAllTypes;\n\tuse futures_executor::block_on;\n\tuse thiserror::Error;\n\n\t#[derive(Debug, Default, Deserialize, Serialize)]\n\t#[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n\tstruct Msg {\n\t\tmsg: String\n\t}\n\n\t#[derive(Debug, Default, Error)]\n\t#[error(\"An Error\")]\n\tstruct MsgError;\n\n\t#[test]\n\tfn result_ok() {\n\t\tlet ok: Result = Ok(Msg::default());\n\t\tlet res = block_on(ok.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::OK);\n\t\tassert_eq!(res.mime, Some(APPLICATION_JSON));\n\t\tassert_eq!(res.full_body().unwrap(), br#\"{\"msg\":\"\"}\"#);\n\t}\n\n\t#[test]\n\tfn result_err() {\n\t\tlet err: Result = Err(MsgError::default());\n\t\tlet res = block_on(err.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::INTERNAL_SERVER_ERROR);\n\t\tassert_eq!(res.mime, Some(APPLICATION_JSON));\n\t\tassert_eq!(\n\t\t\tres.full_body().unwrap(),\n\t\t\tformat!(r#\"{{\"error\":true,\"message\":\"{}\"}}\"#, MsgError::default()).as_bytes()\n\t\t);\n\t}\n\n\t#[test]\n\tfn success_accepts_json() {\n\t\tassert!(\n\t\t\t>::accepted_types()\n\t\t\t\t.or_all_types()\n\t\t\t\t.contains(&APPLICATION_JSON)\n\t\t)\n\t}\n}\n","traces":[{"line":33,"address":[6048976,6049135],"length":1,"stats":{"Line":2},"fn_name":"into_response_error"},{"line":34,"address":[6079680],"length":1,"stats":{"Line":2},"fn_name":null},{"line":35,"address":[5721060],"length":1,"stats":{"Line":2},"fn_name":null},{"line":36,"address":[5721257,5721366],"length":1,"stats":{"Line":4},"fn_name":null},{"line":37,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":38,"address":[6079849,6079724,6079765,6079910],"length":1,"stats":{"Line":6},"fn_name":null},{"line":43,"address":[5981838,5981728,5981710,5981600],"length":1,"stats":{"Line":3},"fn_name":"status_codes"},{"line":44,"address":[5720952,5720877],"length":1,"stats":{"Line":3},"fn_name":null},{"line":48,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":61,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":62,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":63,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":64,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":68,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":69,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":79,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":80,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":81,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":82,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":85,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":86,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":87,"address":[],"length":0,"stats":{"Line":2},"fn_name":null},{"line":88,"address":[],"length":0,"stats":{"Line":2},"fn_name":null}],"covered":21,"coverable":24},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","response","success.rs"],"content":"use super::IntoResponse;\n#[cfg(feature = \"openapi\")]\nuse crate::ResponseSchema;\nuse crate::{Response, ResponseBody};\nuse futures_util::future::{self, FutureExt};\nuse gotham::{\n\thyper::{\n\t\theader::{HeaderMap, HeaderValue, IntoHeaderName},\n\t\tStatusCode\n\t},\n\tmime::{Mime, APPLICATION_JSON}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiSchema;\nuse std::{fmt::Debug, future::Future, pin::Pin};\n\n/// This can be returned from a resource when there is no cause of an error.\n///\n/// Usage example:\n///\n/// ```\n/// # #[macro_use] extern crate gotham_restful_derive;\n/// # mod doc_tests_are_broken {\n/// # use gotham::state::State;\n/// # use gotham_restful::*;\n/// # use serde::{Deserialize, Serialize};\n/// #\n/// # #[derive(Resource)]\n/// # #[resource(read_all)]\n/// # struct MyResource;\n/// #\n/// #[derive(Deserialize, Serialize)]\n/// # #[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n/// struct MyResponse {\n/// \tmessage: &'static str\n/// }\n///\n/// #[read_all]\n/// fn read_all() -> Success {\n/// \tlet res = MyResponse {\n/// \t\tmessage: \"I'm always happy\"\n/// \t};\n/// \tres.into()\n/// }\n/// # }\n/// ```\n#[derive(Clone, Debug, Default)]\npub struct Success {\n\tvalue: T,\n\theaders: HeaderMap\n}\n\nimpl From for Success {\n\tfn from(t: T) -> Self {\n\t\tSelf {\n\t\t\tvalue: t,\n\t\t\theaders: HeaderMap::new()\n\t\t}\n\t}\n}\n\nimpl Success {\n\t/// Set a custom HTTP header. If a header with this name was set before, its value is being updated.\n\tpub fn header(&mut self, name: K, value: HeaderValue) {\n\t\tself.headers.insert(name, value);\n\t}\n\n\t/// Allow manipulating HTTP headers.\n\tpub fn headers_mut(&mut self) -> &mut HeaderMap {\n\t\t&mut self.headers\n\t}\n}\n\nimpl IntoResponse for Success {\n\ttype Err = serde_json::Error;\n\n\tfn into_response(self) -> Pin> + Send>> {\n\t\tlet res = serde_json::to_string(&self.value)\n\t\t\t.map(|body| Response::json(StatusCode::OK, body).with_headers(self.headers));\n\t\tfuture::ready(res).boxed()\n\t}\n\n\tfn accepted_types() -> Option> {\n\t\tSome(vec![APPLICATION_JSON])\n\t}\n}\n\n#[cfg(feature = \"openapi\")]\nimpl ResponseSchema for Success {\n\tfn schema(code: StatusCode) -> OpenapiSchema {\n\t\tassert_eq!(code, StatusCode::OK);\n\t\tT::schema()\n\t}\n}\n\n#[cfg(test)]\nmod test {\n\tuse super::*;\n\tuse crate::response::OrAllTypes;\n\tuse futures_executor::block_on;\n\tuse gotham::hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN;\n\n\t#[derive(Debug, Default, Serialize)]\n\t#[cfg_attr(feature = \"openapi\", derive(openapi_type::OpenapiType))]\n\tstruct Msg {\n\t\tmsg: String\n\t}\n\n\t#[test]\n\tfn success_always_successfull() {\n\t\tlet success: Success = Msg::default().into();\n\t\tlet res = block_on(success.into_response()).expect(\"didn't expect error response\");\n\t\tassert_eq!(res.status, StatusCode::OK);\n\t\tassert_eq!(res.mime, Some(APPLICATION_JSON));\n\t\tassert_eq!(res.full_body().unwrap(), br#\"{\"msg\":\"\"}\"#);\n\t\t#[cfg(feature = \"openapi\")]\n\t\tassert_eq!(>::status_codes(), vec![StatusCode::OK]);\n\t}\n\n\t#[test]\n\tfn success_custom_headers() {\n\t\tlet mut success: Success = Msg::default().into();\n\t\tsuccess.header(ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static(\"*\"));\n\t\tlet res = block_on(success.into_response()).expect(\"didn't expect error response\");\n\t\tlet cors = res.headers.get(ACCESS_CONTROL_ALLOW_ORIGIN);\n\t\tassert_eq!(cors.map(|value| value.to_str().unwrap()), Some(\"*\"));\n\t}\n\n\t#[test]\n\tfn success_accepts_json() {\n\t\tassert!(\n\t\t\t>::accepted_types()\n\t\t\t\t.or_all_types()\n\t\t\t\t.contains(&APPLICATION_JSON)\n\t\t)\n\t}\n}\n","traces":[{"line":54,"address":[7581008,7580912,7580864,7581150],"length":1,"stats":{"Line":3},"fn_name":"from"},{"line":57,"address":[],"length":0,"stats":{"Line":3},"fn_name":null},{"line":64,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":65,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":69,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":70,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":77,"address":[7595244,7595275,7594944,7594576,7594911,7594890],"length":1,"stats":{"Line":2},"fn_name":"into_response"},{"line":78,"address":[],"length":0,"stats":{"Line":6},"fn_name":null},{"line":79,"address":[],"length":0,"stats":{"Line":9},"fn_name":null},{"line":80,"address":[],"length":0,"stats":{"Line":5},"fn_name":null},{"line":83,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":84,"address":[],"length":0,"stats":{"Line":1},"fn_name":null},{"line":90,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":10,"coverable":14},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","routing.rs"],"content":"#[cfg(feature = \"openapi\")]\nuse crate::openapi::{\n\tbuilder::{OpenapiBuilder, OpenapiInfo},\n\trouter::OpenapiRouter\n};\nuse crate::{response::ResourceError, Endpoint, FromBody, IntoResponse, Resource, Response};\n#[cfg(feature = \"cors\")]\nuse gotham::router::route::matcher::AccessControlRequestMethodMatcher;\nuse gotham::{\n\thandler::HandlerError,\n\thelpers::http::response::{create_empty_response, create_response},\n\thyper::{body::to_bytes, header::CONTENT_TYPE, Body, HeaderMap, Method, StatusCode},\n\tmime::{Mime, APPLICATION_JSON},\n\tpipeline::PipelineHandleChain,\n\tprelude::*,\n\trouter::{\n\t\tbuilder::{RouterBuilder, ScopeBuilder},\n\t\troute::matcher::{AcceptHeaderRouteMatcher, ContentTypeHeaderRouteMatcher, RouteMatcher},\n\t\tRouteNonMatch\n\t},\n\tstate::{FromState, State}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiType;\nuse std::{any::TypeId, panic::RefUnwindSafe};\n\n/// Allow us to extract an id from a path.\n#[derive(Clone, Copy, Debug, Deserialize, StateData, StaticResponseExtender)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\npub struct PathExtractor {\n\tpub id: ID\n}\n\n/// This trait adds the `with_openapi` method to gotham's routing. It turns the default\n/// router into one that will only allow RESTful resources, but record them and generate\n/// an OpenAPI specification on request.\n#[cfg(feature = \"openapi\")]\npub trait WithOpenapi {\n\tfn with_openapi(&mut self, info: OpenapiInfo, block: F)\n\twhere\n\t\tF: FnOnce(OpenapiRouter<'_, D>);\n}\n\n/// This trait adds the `resource` method to gotham's routing. It allows you to register\n/// any RESTful [Resource] with a path.\n#[_private_openapi_trait(DrawResourcesWithSchema)]\npub trait DrawResources {\n\t#[openapi_bound(R: crate::ResourceWithSchema)]\n\t#[non_openapi_bound(R: crate::Resource)]\n\tfn resource(&mut self, path: &str);\n}\n\n/// This trait allows to draw routes within an resource. Use this only inside the\n/// [Resource::setup] method.\n#[_private_openapi_trait(DrawResourceRoutesWithSchema)]\npub trait DrawResourceRoutes {\n\t#[openapi_bound(E: crate::EndpointWithSchema)]\n\t#[non_openapi_bound(E: crate::Endpoint)]\n\tfn endpoint(&mut self);\n}\n\nfn response_from(res: Response, state: &State) -> gotham::hyper::Response {\n\tlet mut r = create_empty_response(state, res.status);\n\tlet headers = r.headers_mut();\n\tif let Some(mime) = res.mime {\n\t\theaders.insert(CONTENT_TYPE, mime.as_ref().parse().unwrap());\n\t}\n\tlet mut last_name = None;\n\tfor (name, value) in res.headers {\n\t\tif name.is_some() {\n\t\t\tlast_name = name;\n\t\t}\n\t\t// this unwrap is safe: the first item will always be Some\n\t\tlet name = last_name.clone().unwrap();\n\t\theaders.insert(name, value);\n\t}\n\n\tlet method = Method::borrow_from(state);\n\tif method != Method::HEAD {\n\t\t*r.body_mut() = res.body;\n\t}\n\n\t#[cfg(feature = \"cors\")]\n\tcrate::cors::handle_cors(state, &mut r);\n\n\tr\n}\n\nasync fn endpoint_handler(\n\tstate: &mut State\n) -> Result, HandlerError>\nwhere\n\tE: Endpoint,\n\t::Err: Into\n{\n\ttrace!(\"entering endpoint_handler\");\n\tlet placeholders = E::Placeholders::take_from(state);\n\t// workaround for E::Placeholders and E::Param being the same type\n\t// when fixed remove `Clone` requirement on endpoint\n\tif TypeId::of::() == TypeId::of::() {\n\t\tstate.put(placeholders.clone());\n\t}\n\tlet params = E::Params::take_from(state);\n\n\tlet body = match E::needs_body() {\n\t\ttrue => {\n\t\t\tlet body = to_bytes(Body::take_from(state)).await?;\n\n\t\t\tlet content_type: Mime = match HeaderMap::borrow_from(state).get(CONTENT_TYPE) {\n\t\t\t\tSome(content_type) => content_type.to_str().unwrap().parse().unwrap(),\n\t\t\t\tNone => {\n\t\t\t\t\tdebug!(\"Missing Content-Type: Returning 415 Response\");\n\t\t\t\t\tlet res = create_empty_response(state, StatusCode::UNSUPPORTED_MEDIA_TYPE);\n\t\t\t\t\treturn Ok(res);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tmatch E::Body::from_body(body, content_type) {\n\t\t\t\tOk(body) => Some(body),\n\t\t\t\tErr(e) => {\n\t\t\t\t\tdebug!(\"Invalid Body: Returning 400 Response\");\n\t\t\t\t\tlet error: ResourceError = e.into();\n\t\t\t\t\tlet json = serde_json::to_string(&error)?;\n\t\t\t\t\tlet res =\n\t\t\t\t\t\tcreate_response(state, StatusCode::BAD_REQUEST, APPLICATION_JSON, json);\n\t\t\t\t\treturn Ok(res);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tfalse => None\n\t};\n\n\tlet out = E::handle(state, placeholders, params, body).await;\n\tlet res = out.into_response().await.map_err(Into::into)?;\n\tdebug!(\"Returning response {res:?}\");\n\tOk(response_from(res, state))\n}\n\n#[derive(Clone)]\nstruct MaybeMatchAcceptHeader {\n\tmatcher: Option\n}\n\nimpl RouteMatcher for MaybeMatchAcceptHeader {\n\tfn is_match(&self, state: &State) -> Result<(), RouteNonMatch> {\n\t\tmatch &self.matcher {\n\t\t\tSome(matcher) => matcher.is_match(state),\n\t\t\tNone => Ok(())\n\t\t}\n\t}\n}\n\nimpl MaybeMatchAcceptHeader {\n\tfn new(types: Option>) -> Self {\n\t\tlet types = match types {\n\t\t\tSome(types) if types.is_empty() => None,\n\t\t\ttypes => types\n\t\t};\n\t\tSelf {\n\t\t\tmatcher: types.map(AcceptHeaderRouteMatcher::new)\n\t\t}\n\t}\n}\n\nimpl From>> for MaybeMatchAcceptHeader {\n\tfn from(types: Option>) -> Self {\n\t\tSelf::new(types)\n\t}\n}\n\n#[derive(Clone)]\nstruct MaybeMatchContentTypeHeader {\n\tmatcher: Option\n}\n\nimpl RouteMatcher for MaybeMatchContentTypeHeader {\n\tfn is_match(&self, state: &State) -> Result<(), RouteNonMatch> {\n\t\tmatch &self.matcher {\n\t\t\tSome(matcher) => matcher.is_match(state),\n\t\t\tNone => Ok(())\n\t\t}\n\t}\n}\n\nimpl MaybeMatchContentTypeHeader {\n\tfn new(types: Option>) -> Self {\n\t\tSelf {\n\t\t\tmatcher: types.map(|types| ContentTypeHeaderRouteMatcher::new(types).allow_no_type())\n\t\t}\n\t}\n}\n\nimpl From>> for MaybeMatchContentTypeHeader {\n\tfn from(types: Option>) -> Self {\n\t\tSelf::new(types)\n\t}\n}\n\nmacro_rules! implDrawResourceRoutes {\n\t($implType:ident) => {\n\t\t#[cfg(feature = \"openapi\")]\n\t\timpl<'a, C, P> WithOpenapi for $implType<'a, C, P>\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn with_openapi(&mut self, info: OpenapiInfo, block: F)\n\t\t\twhere\n\t\t\t\tF: FnOnce(OpenapiRouter<'_, $implType<'a, C, P>>)\n\t\t\t{\n\t\t\t\tlet router = OpenapiRouter {\n\t\t\t\t\trouter: self,\n\t\t\t\t\tscope: None,\n\t\t\t\t\topenapi_builder: &mut OpenapiBuilder::new(info)\n\t\t\t\t};\n\t\t\t\tblock(router);\n\t\t\t}\n\t\t}\n\n\t\timpl<'a, C, P> DrawResources for $implType<'a, C, P>\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn resource(&mut self, mut path: &str) {\n\t\t\t\tif path.starts_with('/') {\n\t\t\t\t\tpath = &path[1..];\n\t\t\t\t}\n\t\t\t\tR::setup((self, path));\n\t\t\t}\n\t\t}\n\n\t\timpl<'a, C, P> DrawResourceRoutes for (&mut $implType<'a, C, P>, &str)\n\t\twhere\n\t\t\tC: PipelineHandleChain

+ Copy + Send + Sync + 'static,\n\t\t\tP: RefUnwindSafe + Send + Sync + 'static\n\t\t{\n\t\t\tfn endpoint(&mut self) {\n\t\t\t\tlet uri = format!(\"{}/{}\", self.1, E::uri());\n\t\t\t\tdebug!(\"Registering endpoint for {uri}\");\n\t\t\t\tself.0.associate(&uri, |assoc| {\n\t\t\t\t\tassoc\n\t\t\t\t\t\t.request(vec![E::http_method()])\n\t\t\t\t\t\t.add_route_matcher(MaybeMatchAcceptHeader::new(E::Output::accepted_types()))\n\t\t\t\t\t\t.with_path_extractor::()\n\t\t\t\t\t\t.with_query_string_extractor::()\n\t\t\t\t\t\t.to_async_borrowing(endpoint_handler::);\n\n\t\t\t\t\t#[cfg(feature = \"cors\")]\n\t\t\t\t\tif E::http_method() != Method::GET {\n\t\t\t\t\t\tassoc\n\t\t\t\t\t\t\t.options()\n\t\t\t\t\t\t\t.add_route_matcher(AccessControlRequestMethodMatcher::new(\n\t\t\t\t\t\t\t\tE::http_method()\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t.to(crate::cors::cors_preflight_handler);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t};\n}\n\nimplDrawResourceRoutes!(RouterBuilder);\nimplDrawResourceRoutes!(ScopeBuilder);\n","traces":[{"line":62,"address":[8024298,8022856,8022032],"length":1,"stats":{"Line":5},"fn_name":"response_from"},{"line":63,"address":[8022075,8022226],"length":1,"stats":{"Line":10},"fn_name":null},{"line":64,"address":[8022303,8022234],"length":1,"stats":{"Line":10},"fn_name":null},{"line":65,"address":[8022311],"length":1,"stats":{"Line":5},"fn_name":null},{"line":66,"address":[8022837,8022425,8022636],"length":1,"stats":{"Line":4},"fn_name":null},{"line":68,"address":[8022496],"length":1,"stats":{"Line":5},"fn_name":null},{"line":69,"address":[8022508,8022955,8023737],"length":1,"stats":{"Line":10},"fn_name":null},{"line":70,"address":[8023467,8023209,8023277],"length":1,"stats":{"Line":0},"fn_name":null},{"line":71,"address":[8023313],"length":1,"stats":{"Line":0},"fn_name":null},{"line":74,"address":[8023283,8023472],"length":1,"stats":{"Line":0},"fn_name":null},{"line":75,"address":[8023511],"length":1,"stats":{"Line":0},"fn_name":null},{"line":78,"address":[8023766],"length":1,"stats":{"Line":5},"fn_name":null},{"line":79,"address":[8023795,8024087],"length":1,"stats":{"Line":10},"fn_name":null},{"line":80,"address":[8023864,8024092],"length":1,"stats":{"Line":5},"fn_name":null},{"line":84,"address":[8023840],"length":1,"stats":{"Line":5},"fn_name":null},{"line":86,"address":[8024119],"length":1,"stats":{"Line":5},"fn_name":null},{"line":89,"address":[6927472],"length":1,"stats":{"Line":21},"fn_name":"endpoint_handler"},{"line":96,"address":[6928232,6927691,6928143,6928079],"length":1,"stats":{"Line":63},"fn_name":null},{"line":97,"address":[6928362,6928211],"length":1,"stats":{"Line":42},"fn_name":null},{"line":100,"address":[6928374,6928439],"length":1,"stats":{"Line":42},"fn_name":null},{"line":101,"address":[6928539],"length":1,"stats":{"Line":13},"fn_name":null},{"line":103,"address":[6928621,6928521],"length":1,"stats":{"Line":42},"fn_name":null},{"line":105,"address":[6928696,6928633],"length":1,"stats":{"Line":42},"fn_name":null},{"line":106,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[6927610,6928921,6929161,6927769,6927914,6928715,6954362],"length":1,"stats":{"Line":24},"fn_name":null},{"line":109,"address":[6929131,6929276],"length":1,"stats":{"Line":16},"fn_name":null},{"line":110,"address":[6929426],"length":1,"stats":{"Line":8},"fn_name":null},{"line":111,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[6929378,6931028,6930861,6930925],"length":1,"stats":{"Line":0},"fn_name":null},{"line":113,"address":[6930993],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[6931171],"length":1,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[6929588],"length":1,"stats":{"Line":8},"fn_name":null},{"line":119,"address":[6929792],"length":1,"stats":{"Line":8},"fn_name":null},{"line":120,"address":[6929805],"length":1,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[6929829,6930085,6929986,6929922],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[6930054],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[6930251,6930564,6930192],"length":1,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[6930383],"length":1,"stats":{"Line":0},"fn_name":null},{"line":125,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":126,"address":[6930672],"length":1,"stats":{"Line":0},"fn_name":null},{"line":130,"address":[6928702],"length":1,"stats":{"Line":13},"fn_name":null},{"line":133,"address":[6927865,6930793,6927790,6954380,6931245],"length":1,"stats":{"Line":64},"fn_name":null},{"line":134,"address":[6927808,6931642,6931580,6954395,6932075],"length":1,"stats":{"Line":42},"fn_name":null},{"line":135,"address":[6932573,6932289,6932030,6932347],"length":1,"stats":{"Line":63},"fn_name":null},{"line":136,"address":[6932409,6932695],"length":1,"stats":{"Line":42},"fn_name":null},{"line":145,"address":[8024512],"length":1,"stats":{"Line":5},"fn_name":"is_match"},{"line":146,"address":[8024545],"length":1,"stats":{"Line":5},"fn_name":null},{"line":147,"address":[8024594],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[8024574],"length":1,"stats":{"Line":5},"fn_name":null},{"line":154,"address":[8024624,8025079,8025009],"length":1,"stats":{"Line":7},"fn_name":null},{"line":155,"address":[8024646],"length":1,"stats":{"Line":7},"fn_name":null},{"line":156,"address":[8024854,8024707],"length":1,"stats":{"Line":6},"fn_name":null},{"line":157,"address":[8024735],"length":1,"stats":{"Line":5},"fn_name":null},{"line":160,"address":[8024918],"length":1,"stats":{"Line":7},"fn_name":null},{"line":166,"address":[8025104],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":167,"address":[8025112],"length":1,"stats":{"Line":0},"fn_name":null},{"line":177,"address":[8025136],"length":1,"stats":{"Line":0},"fn_name":"is_match"},{"line":178,"address":[8025169],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[8025215],"length":1,"stats":{"Line":0},"fn_name":null},{"line":180,"address":[8025195],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[8025248],"length":1,"stats":{"Line":0},"fn_name":null},{"line":188,"address":[8025312,8025262,8025325],"length":1,"stats":{"Line":0},"fn_name":"{closure#0}"},{"line":194,"address":[8025376],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":195,"address":[8025384],"length":1,"stats":{"Line":0},"fn_name":null},{"line":207,"address":[6933493,6933248,6933512],"length":1,"stats":{"Line":2},"fn_name":"with_openapi<(), (), openapi_supports_scope::openapi_supports_scope::{closure#0}::{closure_env#0}>"},{"line":211,"address":[6933276,6933364],"length":1,"stats":{"Line":4},"fn_name":null},{"line":213,"address":[6933286],"length":1,"stats":{"Line":2},"fn_name":null},{"line":214,"address":[6933359,6933295],"length":1,"stats":{"Line":4},"fn_name":null},{"line":216,"address":[6933399],"length":1,"stats":{"Line":2},"fn_name":null},{"line":225,"address":[7158432],"length":1,"stats":{"Line":5},"fn_name":"resource<(borrow_bag::handle::Handle, borrow_bag::handle::Take>, ()), (gotham::pipeline::Pipeline<(gotham_restful::cors::CorsConfig, ())>, ()), cors_handling::FooResource>"},{"line":226,"address":[7158573,7158456],"length":1,"stats":{"Line":7},"fn_name":null},{"line":227,"address":[7158526],"length":1,"stats":{"Line":2},"fn_name":null},{"line":229,"address":[7158486],"length":1,"stats":{"Line":5},"fn_name":null},{"line":238,"address":[6925216,6923844,6925572,6923488],"length":1,"stats":{"Line":31},"fn_name":"endpoint<(), (), openapi_supports_scope::read_all___gotham_restful_endpoint>"},{"line":239,"address":[6925236,6923590,6923870,6925318,6925598,6923508,6925395,6923667],"length":1,"stats":{"Line":62},"fn_name":null},{"line":240,"address":[6925813,6925991,6924027,6924085,6925668,6925755,6924263,6923940],"length":1,"stats":{"Line":93},"fn_name":null},{"line":241,"address":[6924152,6926128,6924572,6925880,6924432,6924400,6926300,6924559,6926160,6926287],"length":1,"stats":{"Line":93},"fn_name":"{closure#0}<(), (), openapi_supports_scope::read_all___gotham_restful_endpoint>"},{"line":242,"address":[6926577,6924452,6924849,6926510,6926384,6924656,6926180,6924782],"length":1,"stats":{"Line":124},"fn_name":null},{"line":243,"address":[6926298,6924661,6924689,6926417,6924570,6924592,6926206,6926320,6926389,6924478],"length":1,"stats":{"Line":93},"fn_name":null},{"line":244,"address":[6924714,6926442,6924821,6926405,6924677,6926549,6926664,6924936],"length":1,"stats":{"Line":62},"fn_name":null},{"line":250,"address":[6926674,6924896,6924946,6926624],"length":1,"stats":{"Line":31},"fn_name":null},{"line":251,"address":[6925172,6925025,6925125,6926853,6926753,6926900],"length":1,"stats":{"Line":45},"fn_name":null},{"line":253,"address":[6926826,6925098],"length":1,"stats":{"Line":15},"fn_name":null},{"line":254,"address":[6926782,6925054],"length":1,"stats":{"Line":15},"fn_name":null}],"covered":57,"coverable":84},{"path":["/","home","runner","work","gotham_restful","gotham_restful","src","types.rs"],"content":"use gotham::{\n\thyper::body::Bytes,\n\tmime::{Mime, APPLICATION_JSON}\n};\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiType;\nuse serde::{de::DeserializeOwned, Serialize};\nuse std::error::Error;\n\n#[cfg(not(feature = \"openapi\"))]\npub trait ResourceType {}\n\n#[cfg(not(feature = \"openapi\"))]\nimpl ResourceType for T {}\n\n#[cfg(feature = \"openapi\")]\npub trait ResourceType: OpenapiType {}\n\n#[cfg(feature = \"openapi\")]\nimpl ResourceType for T {}\n\n/// A type that can be used inside a response body. Implemented for every type that is\n/// serializable with serde. If the `openapi` feature is used, it must also be of type\n/// [OpenapiType].\npub trait ResponseBody: ResourceType + Serialize {}\n\nimpl ResponseBody for T {}\n\n/// This trait should be implemented for every type that can be built from an HTTP request body\n/// plus its media type.\n///\n/// For most use cases it is sufficient to derive this trait, you usually don't need to manually\n/// implement this. Therefore, make sure that the first variable of your struct can be built from\n/// [Bytes], and the second one can be build from [Mime]. If you have any additional variables, they\n/// need to be [Default]. This is an example of such a struct:\n///\n/// ```rust\n/// # use gotham::mime::{self, Mime};\n/// # use gotham_restful::{FromBody, RequestBody};\n/// #[derive(FromBody, RequestBody)]\n/// #[supported_types(mime::IMAGE_GIF, mime::IMAGE_JPEG, mime::IMAGE_PNG)]\n/// struct RawImage {\n/// \tcontent: Vec,\n/// \tcontent_type: Mime\n/// }\n/// ```\npub trait FromBody: Sized {\n\t/// The error type returned by the conversion if it was unsuccessfull. When using the derive\n\t/// macro, there is no way to trigger an error, so [std::convert::Infallible] is used here.\n\t/// However, this might change in the future.\n\ttype Err: Error;\n\n\t/// Perform the conversion.\n\tfn from_body(body: Bytes, content_type: Mime) -> Result;\n}\n\nimpl FromBody for T {\n\ttype Err = serde_json::Error;\n\n\tfn from_body(body: Bytes, _content_type: Mime) -> Result {\n\t\tserde_json::from_slice(&body)\n\t}\n}\n\n/// A type that can be used inside a request body. Implemented for every type that is deserializable\n/// with serde. If the `openapi` feature is used, it must also be of type [OpenapiType].\n///\n/// If you want a non-deserializable type to be used as a request body, e.g. because you'd like to\n/// get the raw data, you can derive it for your own type. All you need is to have a type implementing\n/// [FromBody] and optionally a list of supported media types:\n///\n/// ```rust\n/// # use gotham::mime::{self, Mime};\n/// # use gotham_restful::{FromBody, RequestBody};\n/// #[derive(FromBody, RequestBody)]\n/// #[supported_types(mime::IMAGE_GIF, mime::IMAGE_JPEG, mime::IMAGE_PNG)]\n/// struct RawImage {\n/// \tcontent: Vec,\n/// \tcontent_type: Mime\n/// }\n/// ```\npub trait RequestBody: ResourceType + FromBody {\n\t/// Return all types that are supported as content types. Use `None` if all types are supported.\n\tfn supported_types() -> Option> {\n\t\tNone\n\t}\n}\n\nimpl RequestBody for T {\n\tfn supported_types() -> Option> {\n\t\tSome(vec![APPLICATION_JSON])\n\t}\n}\n","traces":[{"line":60,"address":[6984272,6984372,6984435],"length":1,"stats":{"Line":2},"fn_name":"from_body<()>"},{"line":61,"address":[6903470,6903529],"length":1,"stats":{"Line":4},"fn_name":null},{"line":84,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":85,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":90,"address":[6984674,6984480],"length":1,"stats":{"Line":1},"fn_name":"supported_types<()>"},{"line":91,"address":[6984497,6984651],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":4,"coverable":6},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","async_methods.rs"],"content":"use gotham::{\n\thyper::{HeaderMap, Method},\n\tmime::{APPLICATION_JSON, TEXT_PLAIN},\n\tprelude::*,\n\trouter::build_simple_router,\n\tstate::State,\n\ttest::TestServer\n};\nuse gotham_restful::*;\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiType;\nuse serde::Deserialize;\nuse tokio::time::{sleep, Duration};\n\nmod util {\n\tinclude!(\"util/mod.rs\");\n}\nuse util::{test_delete_response, test_get_response, test_post_response, test_put_response};\n\n#[derive(Resource)]\n#[resource(\n\tread_all, read, search, create, update_all, update, delete_all, delete, state_test\n)]\nstruct FooResource;\n\n#[derive(Deserialize)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\n#[allow(dead_code)]\nstruct FooBody {\n\tdata: String\n}\n\n#[derive(Clone, Deserialize, StateData, StaticResponseExtender)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\n#[allow(dead_code)]\nstruct FooSearch {\n\tquery: String\n}\n\nconst READ_ALL_RESPONSE: &[u8] = b\"1ARwwSPVyOKpJKrYwqGgECPVWDl1BqajAAj7g7WJ3e\";\n#[read_all]\nasync fn read_all() -> Raw<&'static [u8]> {\n\tRaw::new(READ_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst READ_RESPONSE: &[u8] = b\"FEReHoeBKU17X2bBpVAd1iUvktFL43CDu0cFYHdaP9\";\n#[read]\nasync fn read(_id: u64) -> Raw<&'static [u8]> {\n\tRaw::new(READ_RESPONSE, TEXT_PLAIN)\n}\n\nconst SEARCH_RESPONSE: &[u8] = b\"AWqcQUdBRHXKh3at4u79mdupOAfEbnTcx71ogCVF0E\";\n#[search]\nasync fn search(_body: FooSearch) -> Raw<&'static [u8]> {\n\tRaw::new(SEARCH_RESPONSE, TEXT_PLAIN)\n}\n\nconst CREATE_RESPONSE: &[u8] = b\"y6POY7wOMAB0jBRBw0FJT7DOpUNbhmT8KdpQPLkI83\";\n#[create]\nasync fn create(_body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(CREATE_RESPONSE, TEXT_PLAIN)\n}\n\nconst UPDATE_ALL_RESPONSE: &[u8] = b\"QlbYg8gHE9OQvvk3yKjXJLTSXlIrg9mcqhfMXJmQkv\";\n#[update_all]\nasync fn update_all(_body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(UPDATE_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst UPDATE_RESPONSE: &[u8] = b\"qGod55RUXkT1lgPO8h0uVM6l368O2S0GrwENZFFuRu\";\n#[update]\nasync fn update(_id: u64, _body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(UPDATE_RESPONSE, TEXT_PLAIN)\n}\n\nconst DELETE_ALL_RESPONSE: &[u8] = b\"Y36kZ749MRk2Nem4BedJABOZiZWPLOtiwLfJlGTwm5\";\n#[delete_all]\nasync fn delete_all() -> Raw<&'static [u8]> {\n\tRaw::new(DELETE_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst DELETE_RESPONSE: &[u8] = b\"CwRzBrKErsVZ1N7yeNfjZuUn1MacvgBqk4uPOFfDDq\";\n#[delete]\nasync fn delete(_id: u64) -> Raw<&'static [u8]> {\n\tRaw::new(DELETE_RESPONSE, TEXT_PLAIN)\n}\n\nconst STATE_TEST_RESPONSE: &[u8] = b\"xxJbxOuwioqR5DfzPuVqvaqRSfpdNQGluIvHU4n1LM\";\n#[endpoint(method = \"Method::GET\", uri = \"state_test\")]\nasync fn state_test(state: &mut State) -> Raw<&'static [u8]> {\n\tsleep(Duration::from_nanos(1)).await;\n\tstate.borrow::();\n\tsleep(Duration::from_nanos(1)).await;\n\tRaw::new(STATE_TEST_RESPONSE, TEXT_PLAIN)\n}\n\n#[test]\nfn async_methods() {\n\tlet _ = pretty_env_logger::try_init_timed();\n\n\tlet server = TestServer::new(build_simple_router(|router| {\n\t\trouter.resource::(\"foo\");\n\t}))\n\t.unwrap();\n\n\ttest_get_response(&server, \"http://localhost/foo\", READ_ALL_RESPONSE);\n\ttest_get_response(&server, \"http://localhost/foo/1\", READ_RESPONSE);\n\ttest_get_response(\n\t\t&server,\n\t\t\"http://localhost/foo/search?query=hello+world\",\n\t\tSEARCH_RESPONSE\n\t);\n\ttest_post_response(\n\t\t&server,\n\t\t\"http://localhost/foo\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tCREATE_RESPONSE\n\t);\n\ttest_put_response(\n\t\t&server,\n\t\t\"http://localhost/foo\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tUPDATE_ALL_RESPONSE\n\t);\n\ttest_put_response(\n\t\t&server,\n\t\t\"http://localhost/foo/1\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tUPDATE_RESPONSE\n\t);\n\ttest_delete_response(&server, \"http://localhost/foo\", DELETE_ALL_RESPONSE);\n\ttest_delete_response(&server, \"http://localhost/foo/1\", DELETE_RESPONSE);\n\ttest_get_response(\n\t\t&server,\n\t\t\"http://localhost/foo/state_test\",\n\t\tSTATE_TEST_RESPONSE\n\t);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","cors_handling.rs"],"content":"#![cfg(feature = \"cors\")]\nuse gotham::{\n\thyper::{body::Body, client::connect::Connect, header::*, StatusCode},\n\tmime::TEXT_PLAIN,\n\tpipeline::{new_pipeline, single_pipeline},\n\trouter::build_router,\n\ttest::{Server, TestRequest, TestServer}\n};\nuse gotham_restful::{\n\tcors::{Headers, Origin},\n\tread_all, update_all, CorsConfig, DrawResources, Raw, Resource\n};\n\n#[derive(Resource)]\n#[resource(read_all, update_all)]\nstruct FooResource;\n\n#[read_all]\nfn read_all() {}\n\n#[update_all]\nfn update_all(_body: Raw>) {}\n\nfn test_server(cfg: CorsConfig) -> TestServer {\n\tlet (chain, pipeline) = single_pipeline(new_pipeline().add(cfg).build());\n\tTestServer::new(build_router(chain, pipeline, |router| {\n\t\trouter.resource::(\"/foo\")\n\t}))\n\t.unwrap()\n}\n\nfn test_response(\n\treq: TestRequest,\n\torigin: Option<&str>,\n\tvary: Option<&str>,\n\tcredentials: bool\n) where\n\tTS: Server + 'static,\n\tC: Connect + Clone + Send + Sync + 'static\n{\n\tlet res = req\n\t\t.with_header(ORIGIN, \"http://example.org\".parse().unwrap())\n\t\t.perform()\n\t\t.unwrap();\n\tassert_eq!(res.status(), StatusCode::NO_CONTENT);\n\tlet headers = res.headers();\n\tprintln!(\n\t\t\"{}\",\n\t\theaders\n\t\t\t.keys()\n\t\t\t.map(|name| name.as_str())\n\t\t\t.collect::>()\n\t\t\t.join(\",\")\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_ALLOW_ORIGIN)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\torigin\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(VARY)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\tvary\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_ALLOW_CREDENTIALS)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.map(|value| value == \"true\")\n\t\t\t.unwrap_or(false),\n\t\tcredentials\n\t);\n\tassert!(headers.get(ACCESS_CONTROL_MAX_AGE).is_none());\n}\n\nfn test_preflight(\n\tserver: &TestServer,\n\tmethod: &str,\n\torigin: Option<&str>,\n\tvary: &str,\n\tcredentials: bool,\n\tmax_age: u64\n) {\n\tlet res = server\n\t\t.client()\n\t\t.options(\"http://example.org/foo\")\n\t\t.with_header(ACCESS_CONTROL_REQUEST_METHOD, method.parse().unwrap())\n\t\t.with_header(ORIGIN, \"http://example.org\".parse().unwrap())\n\t\t.perform()\n\t\t.unwrap();\n\tassert_eq!(res.status(), StatusCode::NO_CONTENT);\n\tlet headers = res.headers();\n\tprintln!(\n\t\t\"{}\",\n\t\theaders\n\t\t\t.keys()\n\t\t\t.map(|name| name.as_str())\n\t\t\t.collect::>()\n\t\t\t.join(\",\")\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_ALLOW_METHODS)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\tSome(method)\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_ALLOW_ORIGIN)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\torigin\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(VARY)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\tSome(vary)\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_ALLOW_CREDENTIALS)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.map(|value| value == \"true\")\n\t\t\t.unwrap_or(false),\n\t\tcredentials\n\t);\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(ACCESS_CONTROL_MAX_AGE)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.and_then(|value| value.parse().ok()),\n\t\tSome(max_age)\n\t);\n}\n\nfn test_preflight_headers(\n\tserver: &TestServer,\n\tmethod: &str,\n\trequest_headers: Option<&str>,\n\tallowed_headers: Option<&str>,\n\tvary: &str\n) {\n\tlet client = server.client();\n\tlet mut res = client\n\t\t.options(\"http://example.org/foo\")\n\t\t.with_header(ACCESS_CONTROL_REQUEST_METHOD, method.parse().unwrap())\n\t\t.with_header(ORIGIN, \"http://example.org\".parse().unwrap());\n\tif let Some(hdr) = request_headers {\n\t\tres = res.with_header(ACCESS_CONTROL_REQUEST_HEADERS, hdr.parse().unwrap());\n\t}\n\tlet res = res.perform().unwrap();\n\tassert_eq!(res.status(), StatusCode::NO_CONTENT);\n\tlet headers = res.headers();\n\tprintln!(\n\t\t\"{}\",\n\t\theaders\n\t\t\t.keys()\n\t\t\t.map(|name| name.as_str())\n\t\t\t.collect::>()\n\t\t\t.join(\",\")\n\t);\n\tif let Some(hdr) = allowed_headers {\n\t\tassert_eq!(\n\t\t\theaders\n\t\t\t\t.get(ACCESS_CONTROL_ALLOW_HEADERS)\n\t\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t\t.as_deref(),\n\t\t\tSome(hdr)\n\t\t)\n\t} else {\n\t\tassert!(!headers.contains_key(ACCESS_CONTROL_ALLOW_HEADERS));\n\t}\n\tassert_eq!(\n\t\theaders\n\t\t\t.get(VARY)\n\t\t\t.and_then(|value| value.to_str().ok())\n\t\t\t.as_deref(),\n\t\tSome(vary)\n\t);\n}\n\n#[test]\nfn cors_origin_none() {\n\tlet cfg = Default::default();\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tNone,\n\t\t\"access-control-request-method\",\n\t\tfalse,\n\t\t0\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tNone,\n\t\tNone,\n\t\tfalse\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tNone,\n\t\tNone,\n\t\tfalse\n\t);\n}\n\n#[test]\nfn cors_origin_star() {\n\tlet cfg = CorsConfig {\n\t\torigin: Origin::Star,\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"*\"),\n\t\t\"access-control-request-method\",\n\t\tfalse,\n\t\t0\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tSome(\"*\"),\n\t\tNone,\n\t\tfalse\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tSome(\"*\"),\n\t\tNone,\n\t\tfalse\n\t);\n}\n\n#[test]\nfn cors_origin_single() {\n\tlet cfg = CorsConfig {\n\t\torigin: Origin::Single(\"https://foo.com\".to_owned()),\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"https://foo.com\"),\n\t\t\"access-control-request-method\",\n\t\tfalse,\n\t\t0\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tSome(\"https://foo.com\"),\n\t\tNone,\n\t\tfalse\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tSome(\"https://foo.com\"),\n\t\tNone,\n\t\tfalse\n\t);\n}\n\n#[test]\nfn cors_origin_copy() {\n\tlet cfg = CorsConfig {\n\t\torigin: Origin::Copy,\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"http://example.org\"),\n\t\t\"access-control-request-method,origin\",\n\t\tfalse,\n\t\t0\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tSome(\"http://example.org\"),\n\t\tSome(\"origin\"),\n\t\tfalse\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tSome(\"http://example.org\"),\n\t\tSome(\"origin\"),\n\t\tfalse\n\t);\n}\n\n#[test]\nfn cors_headers_none() {\n\tlet cfg = Default::default();\n\tlet server = test_server(cfg);\n\n\ttest_preflight_headers(&server, \"PUT\", None, None, \"access-control-request-method\");\n\ttest_preflight_headers(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"Content-Type\"),\n\t\tNone,\n\t\t\"access-control-request-method\"\n\t);\n}\n\n#[test]\nfn cors_headers_list() {\n\tlet cfg = CorsConfig {\n\t\theaders: Headers::List(vec![CONTENT_TYPE]),\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight_headers(\n\t\t&server,\n\t\t\"PUT\",\n\t\tNone,\n\t\tSome(\"content-type\"),\n\t\t\"access-control-request-method\"\n\t);\n\ttest_preflight_headers(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"content-type\"),\n\t\tSome(\"content-type\"),\n\t\t\"access-control-request-method\"\n\t);\n}\n\n#[test]\nfn cors_headers_copy() {\n\tlet cfg = CorsConfig {\n\t\theaders: Headers::Copy,\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight_headers(\n\t\t&server,\n\t\t\"PUT\",\n\t\tNone,\n\t\tNone,\n\t\t\"access-control-request-method,access-control-request-headers\"\n\t);\n\ttest_preflight_headers(\n\t\t&server,\n\t\t\"PUT\",\n\t\tSome(\"content-type\"),\n\t\tSome(\"content-type\"),\n\t\t\"access-control-request-method,access-control-request-headers\"\n\t);\n}\n\n#[test]\nfn cors_credentials() {\n\tlet cfg = CorsConfig {\n\t\torigin: Origin::None,\n\t\tcredentials: true,\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tNone,\n\t\t\"access-control-request-method\",\n\t\ttrue,\n\t\t0\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tNone,\n\t\tNone,\n\t\ttrue\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tNone,\n\t\tNone,\n\t\ttrue\n\t);\n}\n\n#[test]\nfn cors_max_age() {\n\tlet cfg = CorsConfig {\n\t\torigin: Origin::None,\n\t\tmax_age: 31536000,\n\t\t..Default::default()\n\t};\n\tlet server = test_server(cfg);\n\n\ttest_preflight(\n\t\t&server,\n\t\t\"PUT\",\n\t\tNone,\n\t\t\"access-control-request-method\",\n\t\tfalse,\n\t\t31536000\n\t);\n\n\ttest_response(\n\t\tserver.client().get(\"http://example.org/foo\"),\n\t\tNone,\n\t\tNone,\n\t\tfalse\n\t);\n\ttest_response(\n\t\tserver\n\t\t\t.client()\n\t\t\t.put(\"http://example.org/foo\", Body::empty(), TEXT_PLAIN),\n\t\tNone,\n\t\tNone,\n\t\tfalse\n\t);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","custom_request_body.rs"],"content":"use gotham::{\n\thyper::header::CONTENT_TYPE,\n\tmime::{Mime, TEXT_PLAIN},\n\trouter::builder::*,\n\ttest::TestServer\n};\nuse gotham_restful::{create, DrawResources, FromBody, Raw, RequestBody, Resource};\n\nconst RESPONSE: &[u8] = b\"This is the only valid response.\";\n\n#[derive(Resource)]\n#[resource(create)]\nstruct FooResource;\n\n#[derive(FromBody, RequestBody)]\n#[supported_types(TEXT_PLAIN)]\nstruct Foo {\n\tcontent: Vec,\n\tcontent_type: Mime\n}\n\n#[create]\nfn create(body: Foo) -> Raw> {\n\tRaw::new(body.content, body.content_type)\n}\n\n#[test]\nfn custom_request_body() {\n\tlet server = TestServer::new(build_simple_router(|router| {\n\t\trouter.resource::(\"foo\");\n\t}))\n\t.unwrap();\n\n\tlet res = server\n\t\t.client()\n\t\t.post(\"http://localhost/foo\", RESPONSE, TEXT_PLAIN)\n\t\t.perform()\n\t\t.unwrap();\n\tassert_eq!(\n\t\tres.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap(),\n\t\t\"text/plain\"\n\t);\n\tlet res = res.read_body().unwrap();\n\tlet body: &[u8] = res.as_ref();\n\tassert_eq!(body, RESPONSE);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","openapi_specification.rs"],"content":"#![cfg(all(feature = \"auth\", feature = \"openapi\"))]\n#![allow(clippy::approx_constant)]\n\n#[macro_use]\nextern crate pretty_assertions;\n\nuse gotham::{\n\thyper::{Method, StatusCode},\n\tmime::{IMAGE_PNG, TEXT_PLAIN_UTF_8},\n\tpipeline::{new_pipeline, single_pipeline},\n\tprelude::*,\n\trouter::build_router,\n\ttest::TestServer\n};\nuse gotham_restful::*;\nuse openapi_type::{OpenapiSchema, OpenapiType, Visitor};\nuse serde::{Deserialize, Serialize};\n\n#[allow(dead_code)]\nmod util {\n\tinclude!(\"util/mod.rs\");\n}\nuse util::test_openapi_response;\n\nconst IMAGE_RESPONSE : &[u8] = b\"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUA/wA0XsCoAAAAAXRSTlN/gFy0ywAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=\";\n\n#[derive(Resource)]\n#[resource(get_image, set_image)]\nstruct ImageResource;\n\n#[derive(FromBody, RequestBody)]\n#[supported_types(IMAGE_PNG)]\nstruct Image(Vec);\n\n#[read(operation_id = \"getImage\")]\nfn get_image(_id: u64) -> Raw<&'static [u8]> {\n\tRaw::new(IMAGE_RESPONSE, \"image/png;base64\".parse().unwrap())\n}\n\n#[update(operation_id = \"setImage\")]\nfn set_image(_id: u64, _image: Image) {}\n\n#[derive(Resource)]\n#[resource(read_secret, search_secret)]\nstruct SecretResource;\n\n#[derive(Deserialize, Clone)]\nstruct AuthData {\n\tsub: String,\n\tiat: u64,\n\texp: u64\n}\n\ntype AuthStatus = gotham_restful::AuthStatus;\n\n#[derive(OpenapiType, Serialize)]\nstruct Secret {\n\tcode: f32\n}\n\n#[derive(OpenapiType, Serialize)]\nstruct Secrets {\n\tsecrets: Vec\n}\n\n#[derive(Clone, Deserialize, OpenapiType, StateData, StaticResponseExtender)]\nstruct SecretQuery {\n\tdate: String,\n\thour: Option,\n\tminute: Option\n}\n\n/// This endpoint gives access to the secret.\n///\n/// You need to be authenticated to call this endpoint.\n#[read]\nfn read_secret(auth: AuthStatus, _id: String) -> AuthSuccess {\n\tauth.ok()?;\n\tOk(Secret { code: 4.2 })\n}\n\n#[search]\nfn search_secret(auth: AuthStatus, _query: SecretQuery) -> AuthSuccess {\n\tauth.ok()?;\n\tOk(Secrets {\n\t\tsecrets: vec![Secret { code: 4.2 }, Secret { code: 3.14 }]\n\t})\n}\n\n#[derive(Resource)]\n#[resource(coffee_read_all)]\nstruct CoffeeResource;\n\nfn teapot_status_codes() -> Vec {\n\tvec![StatusCode::IM_A_TEAPOT]\n}\n\nfn teapot_schema(code: StatusCode) -> OpenapiSchema {\n\tassert_eq!(code, StatusCode::IM_A_TEAPOT);\n\n\tstruct Binary;\n\n\timpl OpenapiType for Binary {\n\t\tfn visit_type(visitor: &mut V) {\n\t\t\tvisitor.visit_binary();\n\t\t}\n\t}\n\n\tBinary::schema()\n}\n\n#[read_all(status_codes = \"teapot_status_codes\", schema = \"teapot_schema\")]\nfn coffee_read_all() -> Response {\n\tResponse::new(\n\t\tStatusCode::IM_A_TEAPOT,\n\t\t\"Sorry, this is just your fancy grandma's teapot. Can't make coffee.\",\n\t\tSome(TEXT_PLAIN_UTF_8)\n\t)\n}\n\n#[derive(Resource)]\n#[resource(custom_read_with, custom_patch)]\nstruct CustomResource;\n\n#[derive(Clone, Deserialize, OpenapiType, StateData, StaticResponseExtender)]\nstruct ReadWithPath {\n\tfrom: String,\n\tid: u64\n}\n\n#[endpoint(method = \"Method::GET\", uri = \"read/:from/with/:id\")]\nfn custom_read_with(_path: ReadWithPath) {}\n\n#[endpoint(method = \"Method::PATCH\", uri = \"\", body = true)]\nfn custom_patch(_body: String) {}\n\n#[test]\nfn openapi_specification() {\n\tlet info = OpenapiInfo {\n\t\ttitle: \"This is just a test\".to_owned(),\n\t\tversion: \"1.2.3\".to_owned(),\n\t\turls: vec![\"http://localhost:12345/api/v1\".to_owned()]\n\t};\n\tlet auth: AuthMiddleware = AuthMiddleware::new(\n\t\tAuthSource::AuthorizationHeader,\n\t\tAuthValidation::default(),\n\t\tStaticAuthHandler::from_array(b\"zlBsA2QXnkmpe0QTh8uCvtAEa4j33YAc\")\n\t);\n\tlet (chain, pipelines) = single_pipeline(new_pipeline().add(auth).build());\n\tlet server = TestServer::new(build_router(chain, pipelines, |router| {\n\t\trouter.with_openapi(info, |mut router| {\n\t\t\t// the leading slash tests that the spec doesn't contain '//img' nonsense\n\t\t\trouter.resource::(\"/img\");\n\t\t\trouter.resource::(\"secret\");\n\t\t\trouter.resource::(\"coffee\");\n\t\t\trouter.resource::(\"custom\");\n\t\t\trouter.openapi_spec(\"openapi\");\n\t\t});\n\t}))\n\t.unwrap();\n\n\ttest_openapi_response(\n\t\t&server,\n\t\t\"http://localhost/openapi\",\n\t\t\"tests/openapi_specification.json\"\n\t);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","openapi_supports_scope.rs"],"content":"#![cfg(feature = \"openapi\")]\n\n#[macro_use]\nextern crate pretty_assertions;\n\nuse gotham::{mime::TEXT_PLAIN, router::builder::*, test::TestServer};\nuse gotham_restful::*;\n\n#[allow(dead_code)]\nmod util {\n\tinclude!(\"util/mod.rs\");\n}\nuse util::{test_get_response, test_openapi_response};\n\nconst RESPONSE: &[u8] = b\"This is the only valid response.\";\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all]\nfn read_all() -> Raw<&'static [u8]> {\n\tRaw::new(RESPONSE, TEXT_PLAIN)\n}\n\n#[test]\nfn openapi_supports_scope() {\n\tlet info = OpenapiInfo {\n\t\ttitle: \"Test\".to_owned(),\n\t\tversion: \"1.2.3\".to_owned(),\n\t\turls: Vec::new()\n\t};\n\tlet server = TestServer::new(build_simple_router(|router| {\n\t\trouter.with_openapi(info, |mut router| {\n\t\t\trouter.openapi_spec(\"openapi\");\n\t\t\trouter.resource::(\"foo1\");\n\t\t\trouter.scope(\"/bar\", |router| {\n\t\t\t\trouter.resource::(\"foo2\");\n\t\t\t\trouter.scope(\"/baz\", |router| {\n\t\t\t\t\trouter.resource::(\"foo3\");\n\t\t\t\t})\n\t\t\t});\n\t\t\trouter.resource::(\"foo4\");\n\t\t});\n\t}))\n\t.unwrap();\n\n\ttest_get_response(&server, \"http://localhost/foo1\", RESPONSE);\n\ttest_get_response(&server, \"http://localhost/bar/foo2\", RESPONSE);\n\ttest_get_response(&server, \"http://localhost/bar/baz/foo3\", RESPONSE);\n\ttest_get_response(&server, \"http://localhost/foo4\", RESPONSE);\n\ttest_openapi_response(\n\t\t&server,\n\t\t\"http://localhost/openapi\",\n\t\t\"tests/openapi_supports_scope.json\"\n\t);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","resource_error.rs"],"content":"use gotham_restful::ResourceError;\n\n#[derive(ResourceError)]\nenum Error {\n\t#[display(\"I/O Error: {0}\")]\n\tIoError(#[from] std::io::Error),\n\n\t#[status(INTERNAL_SERVER_ERROR)]\n\t#[display(\"Internal Server Error: {0}\")]\n\tInternalServerError(String)\n}\n\n#[allow(deprecated)]\nmod resource_error {\n\tuse super::Error;\n\tuse gotham::{hyper::StatusCode, mime::APPLICATION_JSON};\n\tuse gotham_restful::IntoResponseError;\n\n\t#[test]\n\tfn io_error() {\n\t\tlet err = Error::IoError(std::io::Error::last_os_error());\n\t\tlet res = err.into_response_error().unwrap();\n\t\tassert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);\n\t\tassert_eq!(res.mime(), Some(&APPLICATION_JSON));\n\t}\n\n\t#[test]\n\tfn internal_server_error() {\n\t\tlet err = Error::InternalServerError(\"Brocken\".to_owned());\n\t\tassert_eq!(&format!(\"{err}\"), \"Internal Server Error: Brocken\");\n\n\t\tlet res = err.into_response_error().unwrap();\n\t\tassert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);\n\t\tassert_eq!(res.mime(), None); // TODO shouldn't this be a json error message?\n\t}\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","sync_methods.rs"],"content":"use gotham::{\n\tmime::{APPLICATION_JSON, TEXT_PLAIN},\n\tprelude::*,\n\trouter::build_simple_router,\n\ttest::TestServer\n};\nuse gotham_restful::*;\n#[cfg(feature = \"openapi\")]\nuse openapi_type::OpenapiType;\nuse serde::Deserialize;\n\nmod util {\n\tinclude!(\"util/mod.rs\");\n}\nuse util::{test_delete_response, test_get_response, test_post_response, test_put_response};\n\n#[derive(Resource)]\n#[resource(read_all, read, search, create, update_all, update, delete_all, delete)]\nstruct FooResource;\n\n#[derive(Deserialize)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\n#[allow(dead_code)]\nstruct FooBody {\n\tdata: String\n}\n\n#[derive(Clone, Deserialize, StateData, StaticResponseExtender)]\n#[cfg_attr(feature = \"openapi\", derive(OpenapiType))]\n#[allow(dead_code)]\nstruct FooSearch {\n\tquery: String\n}\n\nconst READ_ALL_RESPONSE: &[u8] = b\"1ARwwSPVyOKpJKrYwqGgECPVWDl1BqajAAj7g7WJ3e\";\n#[read_all]\nfn read_all() -> Raw<&'static [u8]> {\n\tRaw::new(READ_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst READ_RESPONSE: &[u8] = b\"FEReHoeBKU17X2bBpVAd1iUvktFL43CDu0cFYHdaP9\";\n#[read]\nfn read(_id: u64) -> Raw<&'static [u8]> {\n\tRaw::new(READ_RESPONSE, TEXT_PLAIN)\n}\n\nconst SEARCH_RESPONSE: &[u8] = b\"AWqcQUdBRHXKh3at4u79mdupOAfEbnTcx71ogCVF0E\";\n#[search]\nfn search(_body: FooSearch) -> Raw<&'static [u8]> {\n\tRaw::new(SEARCH_RESPONSE, TEXT_PLAIN)\n}\n\nconst CREATE_RESPONSE: &[u8] = b\"y6POY7wOMAB0jBRBw0FJT7DOpUNbhmT8KdpQPLkI83\";\n#[create]\nfn create(_body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(CREATE_RESPONSE, TEXT_PLAIN)\n}\n\nconst UPDATE_ALL_RESPONSE: &[u8] = b\"QlbYg8gHE9OQvvk3yKjXJLTSXlIrg9mcqhfMXJmQkv\";\n#[update_all]\nfn update_all(_body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(UPDATE_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst UPDATE_RESPONSE: &[u8] = b\"qGod55RUXkT1lgPO8h0uVM6l368O2S0GrwENZFFuRu\";\n#[update]\nfn update(_id: u64, _body: FooBody) -> Raw<&'static [u8]> {\n\tRaw::new(UPDATE_RESPONSE, TEXT_PLAIN)\n}\n\nconst DELETE_ALL_RESPONSE: &[u8] = b\"Y36kZ749MRk2Nem4BedJABOZiZWPLOtiwLfJlGTwm5\";\n#[delete_all]\nfn delete_all() -> Raw<&'static [u8]> {\n\tRaw::new(DELETE_ALL_RESPONSE, TEXT_PLAIN)\n}\n\nconst DELETE_RESPONSE: &[u8] = b\"CwRzBrKErsVZ1N7yeNfjZuUn1MacvgBqk4uPOFfDDq\";\n#[delete]\nfn delete(_id: u64) -> Raw<&'static [u8]> {\n\tRaw::new(DELETE_RESPONSE, TEXT_PLAIN)\n}\n\n#[test]\nfn sync_methods() {\n\tlet _ = pretty_env_logger::try_init_timed();\n\n\tlet server = TestServer::new(build_simple_router(|router| {\n\t\trouter.resource::(\"foo\");\n\t}))\n\t.unwrap();\n\n\ttest_get_response(&server, \"http://localhost/foo\", READ_ALL_RESPONSE);\n\ttest_get_response(&server, \"http://localhost/foo/1\", READ_RESPONSE);\n\ttest_get_response(\n\t\t&server,\n\t\t\"http://localhost/foo/search?query=hello+world\",\n\t\tSEARCH_RESPONSE\n\t);\n\ttest_post_response(\n\t\t&server,\n\t\t\"http://localhost/foo\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tCREATE_RESPONSE\n\t);\n\ttest_put_response(\n\t\t&server,\n\t\t\"http://localhost/foo\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tUPDATE_ALL_RESPONSE\n\t);\n\ttest_put_response(\n\t\t&server,\n\t\t\"http://localhost/foo/1\",\n\t\tr#\"{\"data\":\"hello world\"}\"#,\n\t\tAPPLICATION_JSON,\n\t\tUPDATE_RESPONSE\n\t);\n\ttest_delete_response(&server, \"http://localhost/foo\", DELETE_ALL_RESPONSE);\n\ttest_delete_response(&server, \"http://localhost/foo/1\", DELETE_RESPONSE);\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","trybuild_ui.rs"],"content":"use trybuild::TestCases;\n\n#[test]\n#[ignore]\nfn trybuild_ui() {\n\tlet t = TestCases::new();\n\tt.compile_fail(\"tests/ui/endpoint/*.rs\");\n\tt.compile_fail(\"tests/ui/from_body/*.rs\");\n\tt.compile_fail(\"tests/ui/resource/*.rs\");\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","async_state.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\nuse gotham::state::State;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all]\nasync fn read_all(state: &State) {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","auth_data_non_clone.rs"],"content":"use gotham_restful::*;\nuse serde::Deserialize;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[derive(Deserialize)]\nstruct AuthData {\n\tiat: u64,\n\texp: u64\n}\n\n#[read_all]\nasync fn read_all(auth: AuthStatus) -> Result {\n\tauth.ok()?;\n\tOk(NoContent::default())\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","custom_method_invalid_expr.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[endpoint(method = \"I like pizza\", uri = \"custom_read\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","custom_method_invalid_type.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[endpoint(method = \"String::new()\", uri = \"custom_read\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","custom_method_missing.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[endpoint(uri = \"custom_read\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","custom_uri_missing.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[endpoint(method = \"gotham_restful::gotham::hyper::Method::GET\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","dynamic_schema_missing_schema.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\nuse gotham::hyper::StatusCode;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\nfn status_codes() -> Vec {\n\tunimplemented!()\n}\n\n#[read_all(status_codes = \"status_codes\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","dynamic_schema_missing_status_codes.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\nuse gotham::hyper::StatusCode;\nuse gotham_restful::private::OpenapiSchema;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\nfn schema(_: StatusCode) -> OpenapiSchema {\n\tunimplemented!()\n}\n\n#[read_all(schema = \"schema\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","dynamic_schema_wrong_type.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\nfn schema(_: u16) -> String {\n\tunimplemented!()\n}\n\nfn status_codes() -> Vec {\n\tunimplemented!()\n}\n\n#[read_all(schema = \"schema\", status_codes = \"status_codes\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","invalid_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(FooResource)]\nfn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","invalid_body_ty.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\nuse gotham_restful::gotham::hyper::Method;\n\n#[derive(Resource)]\n#[resource(endpoint)]\nstruct FooResource;\n\n#[derive(Debug)]\nstruct FooBody {\n\tfoo: String\n}\n\n#[endpoint(method = \"Method::GET\", uri = \"\", body = true)]\nfn endpoint(_: FooBody) {\n\tunimplemented!()\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","invalid_params_ty.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\nuse gotham_restful::gotham::hyper::Method;\n\n#[derive(Resource)]\n#[resource(endpoint)]\nstruct FooResource;\n\n#[derive(Debug)]\nstruct FooParams {\n\tfoo: String\n}\n\n#[endpoint(method = \"Method::GET\", uri = \"\", params = true)]\nfn endpoint(_: FooParams) {\n\tunimplemented!()\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","invalid_placeholders_ty.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\nuse gotham_restful::gotham::hyper::Method;\n\n#[derive(Resource)]\n#[resource(endpoint)]\nstruct FooResource;\n\n#[derive(Debug)]\nstruct FooPlaceholders {\n\tfoo: String\n}\n\n#[endpoint(method = \"Method::GET\", uri = \":foo\")]\nfn endpoint(_: FooPlaceholders) {\n\tunimplemented!()\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","invalid_return_type.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\nuse gotham_restful::gotham::hyper::Method;\n\n#[derive(Resource)]\n#[resource(endpoint)]\nstruct FooResource;\n\nstruct FooResponse;\n\n#[endpoint(method = \"Method::GET\", uri = \"\")]\nfn endpoint() -> FooResponse {\n\tunimplemented!()\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","non_custom_body_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(body = false)]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","non_custom_method_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(method = \"gotham_restful::gotham::hyper::Method::GET\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","non_custom_params_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(params = true)]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","non_custom_uri_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(uri = \"custom_read\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","self.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all]\nfn read_all(self) {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","too_few_args.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read)]\nstruct FooResource;\n\n#[read]\nfn read() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","too_many_args.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all]\nfn read_all(_id: u64) {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","unknown_attribute.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(pineapple = \"on pizza\")]\nfn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","unsafe.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all]\nunsafe fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","endpoint","wants_auth_non_bool.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_all)]\nstruct FooResource;\n\n#[read_all(wants_auth = \"yes, please\")]\nasync fn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","from_body","enum.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(FromBody)]\nenum FromBodyEnum {\n\tSomeVariant(Vec),\n\tOtherVariant(String)\n}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","ui","resource","unknown_method.rs"],"content":"#[macro_use]\nextern crate gotham_restful;\n\n#[derive(Resource)]\n#[resource(read_any)]\nstruct FooResource;\n\n#[read_all]\nfn read_all() {}\n\nfn main() {}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","runner","work","gotham_restful","gotham_restful","tests","util","mod.rs"],"content":"use gotham::{\n\thyper::Body,\n\ttest::TestServer\n};\nuse log::info;\nuse gotham::mime::Mime;\n#[allow(unused_imports)]\nuse std::{fs::File, io::{Read, Write}, str};\n\npub fn test_get_response(server : &TestServer, path : &str, expected : &[u8])\n{\n\tinfo!(\"GET {path}\");\n\tlet res = server.client().get(path).perform().unwrap().read_body().unwrap();\n\tlet body : &[u8] = res.as_ref();\n\tassert_eq!(body, expected);\n}\n\npub fn test_post_response(server : &TestServer, path : &str, body : B, mime : Mime, expected : &[u8])\nwhere\n\tB : Into\n{\n\tinfo!(\"POST {path}\");\n\tlet res = server.client().post(path, body, mime).perform().unwrap().read_body().unwrap();\n\tlet body : &[u8] = res.as_ref();\n\tassert_eq!(body, expected);\n}\n\npub fn test_put_response(server : &TestServer, path : &str, body : B, mime : Mime, expected : &[u8])\nwhere\n\tB : Into\n{\n\tinfo!(\"PUT {path}\");\n\tlet res = server.client().put(path, body, mime).perform().unwrap().read_body().unwrap();\n\tlet body : &[u8] = res.as_ref();\n\tassert_eq!(body, expected);\n}\n\npub fn test_delete_response(server : &TestServer, path : &str, expected : &[u8])\n{\n\tinfo!(\"DELETE {path}\");\n\tlet res = server.client().delete(path).perform().unwrap().read_body().unwrap();\n\tlet body : &[u8] = res.as_ref();\n\tassert_eq!(body, expected);\n}\n\n#[cfg(feature = \"openapi\")]\npub fn test_openapi_response(server : &TestServer, path : &str, output_file : &str)\n{\n\tinfo!(\"GET {path}\");\n\tlet res = server.client().get(path).perform().unwrap().read_body().unwrap();\n\tlet body: serde_json::Value = serde_json::from_slice(&res).unwrap();\n\n\tlet mut file = File::open(output_file).unwrap();\n\tlet expected: serde_json::Value = serde_json::from_reader(&mut file).unwrap();\n\n\t//eprintln!(\"{body}\");\n\tassert_eq!(body, expected);\n}\n","traces":[],"covered":0,"coverable":0}]} \ No newline at end of file