mirror of
https://gitlab.com/msrd0/gotham-restful.git
synced 2025-02-22 20:52:27 +00:00
split the ResourceResult trait
This commit is contained in:
parent
c640efcb88
commit
7de11cdae1
12 changed files with 102 additions and 29 deletions
|
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- All fields of `Response` are now private
|
- All fields of `Response` are now private
|
||||||
- If not enabling the `openapi` feature, `without-openapi` has to be enabled
|
- If not enabling the `openapi` feature, `without-openapi` has to be enabled
|
||||||
- The endpoint macro attributes (`read`, `create`, ...) no longer take the resource ident and reject all unknown attributes ([!18])
|
- The endpoint macro attributes (`read`, `create`, ...) no longer take the resource ident and reject all unknown attributes ([!18])
|
||||||
|
- The `ResourceResult` trait has been split into `ResourceResult` and `ResourceResultSchema`
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- All pre-defined methods (`read`, `create`, ...) from our router extensions ([!18])
|
- All pre-defined methods (`read`, `create`, ...) from our router extensions ([!18])
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub trait Endpoint {
|
||||||
fn uri() -> Cow<'static, str>;
|
fn uri() -> Cow<'static, str>;
|
||||||
|
|
||||||
/// The output type that provides the response.
|
/// The output type that provides the response.
|
||||||
|
#[openapi_bound("Output: crate::ResourceResultSchema")]
|
||||||
type Output: ResourceResult + Send;
|
type Output: ResourceResult + Send;
|
||||||
|
|
||||||
/// Returns `true` _iff_ the URI contains placeholders. `false` by default.
|
/// Returns `true` _iff_ the URI contains placeholders. `false` by default.
|
||||||
|
|
|
@ -482,6 +482,8 @@ pub use result::{
|
||||||
AuthError, AuthError::Forbidden, AuthErrorOrOther, AuthResult, AuthSuccess, IntoResponseError, NoContent, Raw, Redirect,
|
AuthError, AuthError::Forbidden, AuthErrorOrOther, AuthResult, AuthSuccess, IntoResponseError, NoContent, Raw, Redirect,
|
||||||
ResourceResult, Success
|
ResourceResult, Success
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
pub use result::{ResourceResultSchema, ResourceResultWithSchema};
|
||||||
|
|
||||||
mod routing;
|
mod routing;
|
||||||
pub use routing::{DrawResourceRoutes, DrawResources};
|
pub use routing::{DrawResourceRoutes, DrawResources};
|
||||||
|
|
|
@ -188,7 +188,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn no_content_schema_to_content() {
|
fn no_content_schema_to_content() {
|
||||||
let types = NoContent::accepted_types();
|
let types = NoContent::accepted_types();
|
||||||
let schema = <NoContent as ResourceResult>::schema();
|
let schema = <NoContent as ResourceResultSchema>::schema();
|
||||||
let content = OperationDescription::schema_to_content(types.or_all_types(), Item(schema.into_schema()));
|
let content = OperationDescription::schema_to_content(types.or_all_types(), Item(schema.into_schema()));
|
||||||
assert!(content.is_empty());
|
assert!(content.is_empty());
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn raw_schema_to_content() {
|
fn raw_schema_to_content() {
|
||||||
let types = Raw::<&str>::accepted_types();
|
let types = Raw::<&str>::accepted_types();
|
||||||
let schema = <Raw<&str> as ResourceResult>::schema();
|
let schema = <Raw<&str> as ResourceResultSchema>::schema();
|
||||||
let content = OperationDescription::schema_to_content(types.or_all_types(), Item(schema.into_schema()));
|
let content = OperationDescription::schema_to_content(types.or_all_types(), Item(schema.into_schema()));
|
||||||
assert_eq!(content.len(), 1);
|
assert_eq!(content.len(), 1);
|
||||||
let json = serde_json::to_string(&content.values().nth(0).unwrap()).unwrap();
|
let json = serde_json::to_string(&content.values().nth(0).unwrap()).unwrap();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{builder::OpenapiBuilder, handler::OpenapiHandler, operation::OperationDescription};
|
use super::{builder::OpenapiBuilder, handler::OpenapiHandler, operation::OperationDescription};
|
||||||
use crate::{routing::*, EndpointWithSchema, OpenapiType, ResourceResult, ResourceWithSchema};
|
use crate::{routing::*, EndpointWithSchema, OpenapiType, ResourceResultSchema, ResourceWithSchema};
|
||||||
use gotham::{hyper::Method, pipeline::chain::PipelineHandleChain, router::builder::*};
|
use gotham::{hyper::Method, pipeline::chain::PipelineHandleChain, router::builder::*};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::{Captures, Regex};
|
use regex::{Captures, Regex};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
use crate::OpenapiSchema;
|
use crate::OpenapiSchema;
|
||||||
use crate::Response;
|
use crate::Response;
|
||||||
|
|
||||||
use futures_util::future::FutureExt;
|
use futures_util::future::{BoxFuture, FutureExt};
|
||||||
use gotham::handler::HandlerError;
|
use gotham::handler::HandlerError;
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
use gotham::hyper::StatusCode;
|
use gotham::hyper::StatusCode;
|
||||||
|
@ -49,22 +49,41 @@ pub trait ResourceResult {
|
||||||
|
|
||||||
/// Turn this into a response that can be returned to the browser. This api will likely
|
/// Turn this into a response that can be returned to the browser. This api will likely
|
||||||
/// change in the future.
|
/// change in the future.
|
||||||
fn into_response(self) -> Pin<Box<dyn Future<Output = Result<Response, Self::Err>> + Send>>;
|
fn into_response(self) -> BoxFuture<'static, Result<Response, Self::Err>>;
|
||||||
|
|
||||||
/// Return a list of supported mime types.
|
/// Return a list of supported mime types.
|
||||||
fn accepted_types() -> Option<Vec<Mime>> {
|
fn accepted_types() -> Option<Vec<Mime>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
/// Additional details for [ResourceResult] to be used with an OpenAPI-aware router.
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
pub trait ResourceResultSchema {
|
||||||
fn schema() -> OpenapiSchema;
|
fn schema() -> OpenapiSchema;
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
|
||||||
fn default_status() -> StatusCode {
|
fn default_status() -> StatusCode {
|
||||||
StatusCode::OK
|
StatusCode::OK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
mod private {
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait provided to convert a resource's result to json, and provide an OpenAPI schema to the
|
||||||
|
/// router. This trait is implemented for all types that implement [ResourceResult] and
|
||||||
|
/// [ResourceResultSchema].
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
pub trait ResourceResultWithSchema: ResourceResult + ResourceResultSchema + private::Sealed {}
|
||||||
|
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
impl<R: ResourceResult + ResourceResultSchema> private::Sealed for R {}
|
||||||
|
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
impl<R: ResourceResult + ResourceResultSchema> ResourceResultWithSchema for R {}
|
||||||
|
|
||||||
/// The default json returned on an 500 Internal Server Error.
|
/// The default json returned on an 500 Internal Server Error.
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct ResourceError {
|
pub(crate) struct ResourceError {
|
||||||
|
@ -130,8 +149,13 @@ where
|
||||||
fn accepted_types() -> Option<Vec<Mime>> {
|
fn accepted_types() -> Option<Vec<Mime>> {
|
||||||
Res::accepted_types()
|
Res::accepted_types()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
impl<Res> ResourceResultSchema for Pin<Box<dyn Future<Output = Res> + Send>>
|
||||||
|
where
|
||||||
|
Res: ResourceResultSchema
|
||||||
|
{
|
||||||
fn schema() -> OpenapiSchema {
|
fn schema() -> OpenapiSchema {
|
||||||
Res::schema()
|
Res::schema()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{handle_error, ResourceResult};
|
use super::{handle_error, ResourceResult};
|
||||||
use crate::{IntoResponseError, Response};
|
use crate::{IntoResponseError, Response};
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
use crate::{OpenapiSchema, OpenapiType};
|
use crate::{OpenapiSchema, OpenapiType, ResourceResultSchema};
|
||||||
|
|
||||||
use futures_util::{future, future::FutureExt};
|
use futures_util::{future, future::FutureExt};
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
@ -52,15 +52,16 @@ impl ResourceResult for NoContent {
|
||||||
fn accepted_types() -> Option<Vec<Mime>> {
|
fn accepted_types() -> Option<Vec<Mime>> {
|
||||||
Some(Vec::new())
|
Some(Vec::new())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
impl ResourceResultSchema for NoContent {
|
||||||
/// Returns the schema of the `()` type.
|
/// Returns the schema of the `()` type.
|
||||||
#[cfg(feature = "openapi")]
|
|
||||||
fn schema() -> OpenapiSchema {
|
fn schema() -> OpenapiSchema {
|
||||||
<()>::schema()
|
<()>::schema()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This will always be a _204 No Content_
|
/// This will always be a _204 No Content_
|
||||||
#[cfg(feature = "openapi")]
|
|
||||||
fn default_status() -> StatusCode {
|
fn default_status() -> StatusCode {
|
||||||
StatusCode::NO_CONTENT
|
StatusCode::NO_CONTENT
|
||||||
}
|
}
|
||||||
|
@ -82,10 +83,15 @@ where
|
||||||
fn accepted_types() -> Option<Vec<Mime>> {
|
fn accepted_types() -> Option<Vec<Mime>> {
|
||||||
NoContent::accepted_types()
|
NoContent::accepted_types()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
impl<E> ResourceResultSchema for Result<NoContent, E>
|
||||||
|
where
|
||||||
|
E: Display + IntoResponseError<Err = serde_json::Error>
|
||||||
|
{
|
||||||
fn schema() -> OpenapiSchema {
|
fn schema() -> OpenapiSchema {
|
||||||
<NoContent as ResourceResult>::schema()
|
<NoContent as ResourceResultSchema>::schema()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{handle_error, IntoResponseError, ResourceResult};
|
use super::{handle_error, IntoResponseError, ResourceResult};
|
||||||
use crate::{FromBody, RequestBody, ResourceType, Response};
|
use crate::{FromBody, RequestBody, ResourceType, Response};
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
use crate::{OpenapiSchema, OpenapiType};
|
use crate::{OpenapiSchema, OpenapiType, ResourceResultSchema};
|
||||||
|
|
||||||
use futures_core::future::Future;
|
use futures_core::future::Future;
|
||||||
use futures_util::{future, future::FutureExt};
|
use futures_util::{future, future::FutureExt};
|
||||||
|
@ -108,8 +108,13 @@ where
|
||||||
fn into_response(self) -> Pin<Box<dyn Future<Output = Result<Response, SerdeJsonError>> + Send>> {
|
fn into_response(self) -> Pin<Box<dyn Future<Output = Result<Response, SerdeJsonError>> + Send>> {
|
||||||
future::ok(Response::new(StatusCode::OK, self.raw, Some(self.mime))).boxed()
|
future::ok(Response::new(StatusCode::OK, self.raw, Some(self.mime))).boxed()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
impl<T: Into<Body>> ResourceResultSchema for Raw<T>
|
||||||
|
where
|
||||||
|
Self: Send
|
||||||
|
{
|
||||||
fn schema() -> OpenapiSchema {
|
fn schema() -> OpenapiSchema {
|
||||||
<Self as OpenapiType>::schema()
|
<Self as OpenapiType>::schema()
|
||||||
}
|
}
|
||||||
|
@ -128,10 +133,16 @@ where
|
||||||
Err(e) => handle_error(e)
|
Err(e) => handle_error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
impl<T, E> ResourceResultSchema for Result<Raw<T>, E>
|
||||||
|
where
|
||||||
|
Raw<T>: ResourceResult + ResourceResultSchema,
|
||||||
|
E: Display + IntoResponseError<Err = <Raw<T> as ResourceResult>::Err>
|
||||||
|
{
|
||||||
fn schema() -> OpenapiSchema {
|
fn schema() -> OpenapiSchema {
|
||||||
<Raw<T> as ResourceResult>::schema()
|
<Raw<T> as ResourceResultSchema>::schema()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{handle_error, ResourceResult};
|
use super::{handle_error, ResourceResult};
|
||||||
use crate::{IntoResponseError, Response};
|
use crate::{IntoResponseError, Response};
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
use crate::{NoContent, OpenapiSchema};
|
use crate::{NoContent, OpenapiSchema, ResourceResultSchema};
|
||||||
use futures_util::future::{BoxFuture, FutureExt, TryFutureExt};
|
use futures_util::future::{BoxFuture, FutureExt, TryFutureExt};
|
||||||
use gotham::hyper::{
|
use gotham::hyper::{
|
||||||
header::{InvalidHeaderValue, LOCATION},
|
header::{InvalidHeaderValue, LOCATION},
|
||||||
|
@ -53,15 +53,16 @@ impl ResourceResult for Redirect {
|
||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
impl ResourceResultSchema for Redirect {
|
||||||
fn default_status() -> StatusCode {
|
fn default_status() -> StatusCode {
|
||||||
StatusCode::SEE_OTHER
|
StatusCode::SEE_OTHER
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
|
||||||
fn schema() -> OpenapiSchema {
|
fn schema() -> OpenapiSchema {
|
||||||
<NoContent as ResourceResult>::schema()
|
<NoContent as ResourceResultSchema>::schema()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,15 +89,20 @@ where
|
||||||
Err(e) => handle_error(e).map_err(|e| RedirectError::Other(e)).boxed()
|
Err(e) => handle_error(e).map_err(|e| RedirectError::Other(e)).boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
impl<E> ResourceResultSchema for Result<Redirect, E>
|
||||||
|
where
|
||||||
|
E: Display + IntoResponseError,
|
||||||
|
<E as IntoResponseError>::Err: StdError + Sync
|
||||||
|
{
|
||||||
fn default_status() -> StatusCode {
|
fn default_status() -> StatusCode {
|
||||||
Redirect::default_status()
|
Redirect::default_status()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
|
||||||
fn schema() -> OpenapiSchema {
|
fn schema() -> OpenapiSchema {
|
||||||
<Redirect as ResourceResult>::schema()
|
<Redirect as ResourceResultSchema>::schema()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{handle_error, into_response_helper, ResourceResult};
|
use super::{handle_error, into_response_helper, ResourceResult};
|
||||||
#[cfg(feature = "openapi")]
|
|
||||||
use crate::OpenapiSchema;
|
|
||||||
use crate::{result::ResourceError, Response, ResponseBody};
|
use crate::{result::ResourceError, Response, ResponseBody};
|
||||||
|
#[cfg(feature = "openapi")]
|
||||||
|
use crate::{OpenapiSchema, ResourceResultSchema};
|
||||||
|
|
||||||
use futures_core::future::Future;
|
use futures_core::future::Future;
|
||||||
use gotham::hyper::StatusCode;
|
use gotham::hyper::StatusCode;
|
||||||
|
@ -43,8 +43,14 @@ where
|
||||||
fn accepted_types() -> Option<Vec<Mime>> {
|
fn accepted_types() -> Option<Vec<Mime>> {
|
||||||
Some(vec![APPLICATION_JSON])
|
Some(vec![APPLICATION_JSON])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
impl<R, E> ResourceResultSchema for Result<R, E>
|
||||||
|
where
|
||||||
|
R: ResponseBody,
|
||||||
|
E: Display + IntoResponseError<Err = serde_json::Error>
|
||||||
|
{
|
||||||
fn schema() -> OpenapiSchema {
|
fn schema() -> OpenapiSchema {
|
||||||
R::schema()
|
R::schema()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{into_response_helper, ResourceResult};
|
use super::{into_response_helper, ResourceResult};
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
use crate::OpenapiSchema;
|
use crate::{OpenapiSchema, ResourceResultSchema};
|
||||||
use crate::{Response, ResponseBody};
|
use crate::{Response, ResponseBody};
|
||||||
use gotham::hyper::StatusCode;
|
use gotham::hyper::StatusCode;
|
||||||
use mime::{Mime, APPLICATION_JSON};
|
use mime::{Mime, APPLICATION_JSON};
|
||||||
|
@ -104,8 +104,13 @@ where
|
||||||
fn accepted_types() -> Option<Vec<Mime>> {
|
fn accepted_types() -> Option<Vec<Mime>> {
|
||||||
Some(vec![APPLICATION_JSON])
|
Some(vec![APPLICATION_JSON])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
|
impl<T: ResponseBody> ResourceResultSchema for Success<T>
|
||||||
|
where
|
||||||
|
Self: Send
|
||||||
|
{
|
||||||
fn schema() -> OpenapiSchema {
|
fn schema() -> OpenapiSchema {
|
||||||
T::schema()
|
T::schema()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
error[E0277]: the trait bound `FooResponse: ResourceResultSchema` is not satisfied
|
||||||
|
--> $DIR/invalid_return_type.rs:12:18
|
||||||
|
|
|
||||||
|
12 | fn endpoint() -> FooResponse {
|
||||||
|
| ^^^^^^^^^^^ the trait `ResourceResultSchema` is not implemented for `FooResponse`
|
||||||
|
|
|
||||||
|
::: $WORKSPACE/src/endpoint.rs
|
||||||
|
|
|
||||||
|
| #[openapi_bound("Output: crate::ResourceResultSchema")]
|
||||||
|
| ------------------------------------- required by this bound in `gotham_restful::EndpointWithSchema::Output`
|
||||||
|
|
||||||
error[E0277]: the trait bound `FooResponse: ResourceResult` is not satisfied
|
error[E0277]: the trait bound `FooResponse: ResourceResult` is not satisfied
|
||||||
--> $DIR/invalid_return_type.rs:12:18
|
--> $DIR/invalid_return_type.rs:12:18
|
||||||
|
|
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue