Skip to content

Commit

Permalink
Chore refactor OpenApi derive macro (#1011)
Browse files Browse the repository at this point in the history
Remove need for `#[openapi()]` attribute for `OpenApi` derive macro.
Previously it was mandatory to always provide.

From now onwards this will work as well.
```rust
 #[derive(OpenApi)]
 struct Api;
```
  • Loading branch information
juhaku authored Aug 30, 2024
1 parent 509e8e7 commit 04c490d
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 52 deletions.
12 changes: 2 additions & 10 deletions utoipa-gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1718,16 +1718,8 @@ pub fn openapi(input: TokenStream) -> TokenStream {
let DeriveInput { attrs, ident, .. } = syn::parse_macro_input!(input);

parse_openapi_attrs(&attrs)
.and_then(|openapi_attr| {
openapi_attr.ok_or(
syn::Error::new(
ident.span(),
"expected #[openapi(...)] attribute to be present when used with OpenApi derive trait")
)
})
.map_or_else(syn::Error::into_compile_error, |attrs| {
OpenApi(attrs, ident).into_token_stream()
})
.map(|openapi_attr| OpenApi(openapi_attr, ident).to_token_stream())
.map_or_else(syn::Error::into_compile_error, ToTokens::into_token_stream)
.into()
}

Expand Down
106 changes: 64 additions & 42 deletions utoipa-gen/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,17 +390,12 @@ impl Parse for ServerVariable {
}
}

pub(crate) struct OpenApi<'o>(pub OpenApiAttr<'o>, pub Ident);
pub(crate) struct OpenApi<'o>(pub Option<OpenApiAttr<'o>>, pub Ident);

impl OpenApi<'_> {
fn nested_tokens(&self) -> Option<TokenStream> {
if self.0.nested.is_empty() {
None
} else {
let nest_tokens = self
.0
.nested
.iter()
let nested = self.0.as_ref().map(|openapi| &openapi.nested)?;
let nest_tokens = nested.iter()
.map(|item| {
let path = &item.path;
let nest_api = &item
Expand Down Expand Up @@ -443,6 +438,9 @@ impl OpenApi<'_> {
})
.collect::<TokenStream>();

if nest_tokens.is_empty() {
None
} else {
Some(nest_tokens)
}
}
Expand All @@ -452,43 +450,67 @@ impl ToTokens for OpenApi<'_> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let OpenApi(attributes, ident) = self;

let info = info::impl_info(attributes.info.clone());

let components_builder_stream = attributes.components.to_token_stream();
let info = info::impl_info(
attributes
.as_ref()
.and_then(|attributes| attributes.info.clone()),
);

let components = if !components_builder_stream.is_empty() {
Some(quote! { .components(Some(#components_builder_stream)) })
} else {
None
let components = match attributes
.as_ref()
.map(|attributes| attributes.components.to_token_stream())
{
Some(tokens) if !tokens.is_empty() => Some(quote! { .components(Some(#tokens)) }),
_ => None,
};

let modifiers = &attributes.modifiers;
let modifiers_len = modifiers.len();
let path_items = impl_paths(attributes.as_ref().map(|attributes| &attributes.paths));

let path_items = impl_paths(&attributes.paths);
let securities = attributes
.as_ref()
.and_then(|openapi_attributes| openapi_attributes.security.as_ref())
.map(|securities| {
quote! {
.security(Some(#securities))
}
});
let tags = attributes
.as_ref()
.and_then(|attributes| attributes.tags.as_ref())
.map(|tags| {
quote! {
.tags(Some(#tags))
}
});
let external_docs = attributes
.as_ref()
.and_then(|attributes| attributes.external_docs.as_ref())
.map(|external_docs| {
quote! {
.external_docs(Some(#external_docs))
}
});

let securities = attributes.security.as_ref().map(|securities| {
quote! {
.security(Some(#securities))
}
});
let tags = attributes.tags.as_ref().map(|tags| {
quote! {
.tags(Some(#tags))
let servers = match attributes.as_ref().map(|attributes| &attributes.servers) {
Some(servers) if !servers.is_empty() => {
let servers = servers.iter().collect::<Array<&Server>>();
Some(quote! { .servers(Some(#servers)) })
}
});
let external_docs = attributes.external_docs.as_ref().map(|external_docs| {
quote! {
.external_docs(Some(#external_docs))
}
});
let servers = if !attributes.servers.is_empty() {
let servers = attributes.servers.iter().collect::<Array<&Server>>();
Some(quote! { .servers(Some(#servers)) })
} else {
None
_ => None,
};

let modifiers_tokens = attributes
.as_ref()
.map(|attributes| &attributes.modifiers)
.map(|modifiers| {
let modifiers_len = modifiers.len();

quote! {
let _mods: [&dyn utoipa::Modify; #modifiers_len] = [#modifiers];
_mods.iter().for_each(|modifier| modifier.modify(&mut openapi));
}
});

let nested_tokens = self
.nested_tokens()
.map(|tokens| quote! {openapi = openapi #tokens;});
Expand All @@ -509,8 +531,7 @@ impl ToTokens for OpenApi<'_> {
.build();
#nested_tokens

let _mods: [&dyn utoipa::Modify; #modifiers_len] = [#modifiers];
_mods.iter().for_each(|modifier| modifier.modify(&mut openapi));
#modifiers_tokens

openapi
}
Expand Down Expand Up @@ -600,9 +621,10 @@ impl ToTokens for Components {
}
}

fn impl_paths(handler_paths: &Punctuated<ExprPath, Comma>) -> TokenStream {
fn impl_paths(handler_paths: Option<&Punctuated<ExprPath, Comma>>) -> TokenStream {
let handlers = handler_paths
.iter()
.into_iter()
.flatten()
.map(|handler| {
let segments = handler.path.segments.iter().collect::<Vec<_>>();
let handler_fn = &segments.last().unwrap().ident;
Expand Down Expand Up @@ -653,7 +675,7 @@ fn impl_paths(handler_paths: &Punctuated<ExprPath, Comma>) -> TokenStream {
})
.collect::<TokenStream>();

handler_paths.iter().fold(
handler_paths.into_iter().flatten().fold(
quote! { #handlers utoipa::openapi::path::PathsBuilder::new() },
|mut paths, handler| {
let segments = handler.path.segments.iter().collect::<Vec<_>>();
Expand Down

0 comments on commit 04c490d

Please sign in to comment.