Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add derive IntoResponses support #433

Merged
merged 2 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 223 additions & 24 deletions utoipa-gen/src/lib.rs

Large diffs are not rendered by default.

30 changes: 17 additions & 13 deletions utoipa-gen/src/path.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::ops::Deref;
use std::{io::Error, str::FromStr};

Expand Down Expand Up @@ -73,8 +74,8 @@ pub(crate) const PATH_STRUCT_PREFIX: &str = "__path_";
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct PathAttr<'p> {
path_operation: Option<PathOperation>,
request_body: Option<RequestBodyAttr>,
responses: Vec<Response>,
request_body: Option<RequestBodyAttr<'p>>,
responses: Vec<Response<'p>>,
pub(super) path: Option<String>,
operation_id: Option<String>,
tag: Option<String>,
Expand Down Expand Up @@ -473,8 +474,8 @@ struct Operation<'a> {
description: Option<&'a Vec<String>>,
deprecated: &'a Option<bool>,
parameters: &'a Vec<Parameter<'a>>,
request_body: Option<&'a RequestBodyAttr>,
responses: &'a Vec<Response>,
request_body: Option<&'a RequestBodyAttr<'a>>,
responses: &'a Vec<Response<'a>>,
security: Option<&'a Array<'a, SecurityRequirementAttr>>,
}

Expand Down Expand Up @@ -534,13 +535,13 @@ impl ToTokens for Operation<'_> {

/// Represents either `ref("...")` or `Type` that can be optionally inlined with `inline(Type)`.
#[cfg_attr(feature = "debug", derive(Debug))]
enum PathType {
enum PathType<'p> {
Ref(String),
Type(InlineType),
MediaType(InlineType<'p>),
InlineSchema(TokenStream2, Type),
}

impl Parse for PathType {
impl Parse for PathType<'_> {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let fork = input.fork();
let is_ref = if (fork.parse::<Option<Token![ref]>>()?).is_some() {
Expand All @@ -555,26 +556,26 @@ impl Parse for PathType {
parenthesized!(ref_stream in input);
Ok(Self::Ref(ref_stream.parse::<LitStr>()?.value()))
} else {
Ok(Self::Type(input.parse()?))
Ok(Self::MediaType(input.parse()?))
}
}
}

// inline(syn::Type) | syn::Type
#[cfg_attr(feature = "debug", derive(Debug))]
struct InlineType {
ty: Type,
struct InlineType<'i> {
ty: Cow<'i, Type>,
is_inline: bool,
}

impl InlineType {
impl InlineType<'_> {
/// Get's the underlying [`syn::Type`] as [`TypeTree`].
fn as_type_tree(&self) -> TypeTree {
TypeTree::from_type(&self.ty)
}
}

impl Parse for InlineType {
impl Parse for InlineType<'_> {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let fork = input.fork();
let is_inline = if let Some(ident) = fork.parse::<Option<Ident>>()? {
Expand All @@ -593,7 +594,10 @@ impl Parse for InlineType {
input.parse::<Type>()?
};

Ok(InlineType { ty, is_inline })
Ok(InlineType {
ty: Cow::Owned(ty),
is_inline,
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion utoipa-gen/src/path/parameter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub struct ValueParameter<'a> {
parameter_ext: Option<ParameterExt>,

/// Type only when value parameter is parsed
parsed_type: Option<InlineType>,
parsed_type: Option<InlineType<'a>>,
}

impl<'p> ValueParameter<'p> {
Expand Down
12 changes: 6 additions & 6 deletions utoipa-gen/src/path/request_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ use super::{PathType, PathTypeTree};
/// ```
#[derive(Default)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct RequestBodyAttr {
content: Option<PathType>,
pub struct RequestBodyAttr<'r> {
content: Option<PathType<'r>>,
content_type: Option<String>,
description: Option<String>,
example: Option<AnyValue>,
examples: Option<Punctuated<Example, Comma>>,
}

impl Parse for RequestBodyAttr {
impl Parse for RequestBodyAttr<'_> {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
const EXPECTED_ATTRIBUTE_MESSAGE: &str =
"unexpected attribute, expected any of: content, content_type, description, examples";
Expand Down Expand Up @@ -133,14 +133,14 @@ impl Parse for RequestBodyAttr {
}
}

impl ToTokens for RequestBodyAttr {
impl ToTokens for RequestBodyAttr<'_> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
if let Some(body_type) = &self.content {
let media_type_schema = match body_type {
PathType::Ref(ref_type) => quote! {
utoipa::openapi::schema::Ref::new(#ref_type)
},
PathType::Type(body_type) => {
PathType::MediaType(body_type) => {
let type_tree = body_type.as_type_tree();
MediaTypeSchema {
type_tree: &type_tree,
Expand Down Expand Up @@ -180,7 +180,7 @@ impl ToTokens for RequestBodyAttr {
.content("application/json", #content.build())
});
}
PathType::Type(body_type) => {
PathType::MediaType(body_type) => {
let type_tree = body_type.as_type_tree();
let content_type = self
.content_type
Expand Down
Loading