apistos_gen/lib.rs
1//! A set of macro utilities to generate [OpenAPI v3.0.3](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md) documentation from Rust models.
2//!
3//! ⚠️ This crate is not indented to be used by itself. Please use [**apistos**](https://crates.io/crates/apistos) instead.
4
5use crate::internal::schemas::Schemas;
6use crate::internal::utils::extract_deprecated_from_attr;
7use crate::internal::{gen_item_ast, gen_open_api_impl};
8use crate::openapi_cookie_attr::parse_openapi_cookie_attrs;
9use crate::openapi_error_attr::parse_openapi_error_attrs;
10use crate::openapi_header_attr::parse_openapi_header_attrs;
11use crate::openapi_security_attr::parse_openapi_security_attrs;
12use crate::operation_attr::parse_openapi_operation_attrs;
13use convert_case::{Case, Casing};
14use darling::ast::NestedMeta;
15use darling::Error;
16use proc_macro::TokenStream;
17use proc_macro2::Span;
18use proc_macro_error::{abort, proc_macro_error, OptionExt};
19use quote::{format_ident, quote};
20use syn::{DeriveInput, GenericParam, Ident, ItemFn};
21
22mod internal;
23mod openapi_cookie_attr;
24mod openapi_error_attr;
25mod openapi_header_attr;
26mod openapi_security_attr;
27mod operation_attr;
28
29const OPENAPI_STRUCT_PREFIX: &str = "__openapi_";
30
31/// Generates a custom OpenAPI type.
32///
33/// This `#[derive]` macro should be used in combination with [TypedSchema](trait.TypedSchema.html).
34///
35/// When deriving [ApiType], [ApiComponent] and [JsonSchema](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html) are automatically implemented and thus
36/// should not be derived.
37///
38/// ```rust
39/// use apistos::{ApiType, InstanceType, TypedSchema};
40///
41/// #[derive(Debug, Clone, ApiType)]
42/// pub struct Name(String);
43///
44/// impl TypedSchema for Name {
45/// fn schema_type() -> InstanceType {
46/// InstanceType::String
47/// }
48///
49/// fn format() -> Option<String> {
50/// None
51/// }
52/// }
53/// ```
54#[proc_macro_error]
55#[proc_macro_derive(ApiType)]
56pub fn derive_api_type(input: TokenStream) -> TokenStream {
57 let input = syn::parse_macro_input!(input as DeriveInput);
58 let DeriveInput {
59 attrs: _attrs,
60 ident,
61 data: _data,
62 generics,
63 vis: _vis,
64 } = input;
65
66 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
67 let component_name = quote!(#ident).to_string();
68 quote!(
69 #[automatically_derived]
70 impl #impl_generics schemars::JsonSchema for #ident #ty_generics #where_clause {
71 fn is_referenceable() -> bool {
72 false
73 }
74
75 fn schema_name() -> String {
76 #component_name.to_string()
77 }
78
79 fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> apistos::Schema {
80 let instance_type = <Self as TypedSchema>::schema_type();
81 apistos::Schema::Object(apistos::SchemaObject {
82 instance_type: Some(apistos::SingleOrVec::Single(Box::new(instance_type))),
83 format: <Self as TypedSchema>::format(),
84 ..Default::default()
85 })
86 }
87 }
88
89 #[automatically_derived]
90 impl #impl_generics apistos::ApiComponent for #ident #ty_generics #where_clause {
91 fn child_schemas() -> Vec<(String, apistos::reference_or::ReferenceOr<apistos::Schema>)> {
92 vec![]
93 }
94
95 fn schema() -> Option<(String, apistos::reference_or::ReferenceOr<apistos::Schema>)> {
96 Some((
97 #component_name.to_string(),
98 apistos::reference_or::ReferenceOr::Object(apistos::Schema::Object(apistos::SchemaObject {
99 instance_type: Some(apistos::SingleOrVec::Single(Box::new(<#ident #ty_generics>::schema_type()))),
100 format: <#ident #ty_generics>::format(),
101 ..Default::default()
102 }))
103 ))
104 }
105 }
106 )
107 .into()
108}
109
110/// Generates a reusable OpenAPI schema.
111///
112/// This `#[derive]` macro should be used in combination with [api_operation](attr.api_operation.html).
113///
114/// This macro requires your type to derive [JsonSchema](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html).
115///
116/// ```rust
117/// use apistos::ApiComponent;
118/// use schemars::JsonSchema;
119/// use garde::Validate;
120///
121/// #[derive(Debug, Clone, JsonSchema, ApiComponent, Validate)]
122/// pub(crate) struct QueryTag {
123/// #[garde(length(min = 2))]
124/// #[schemars(length(min = 2))]
125/// pub(crate) tags: Vec<String>,
126/// }
127/// ```
128///
129/// Because this macro requires [JsonSchema](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html), all attributes supported by [JsonSchema](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html) are forwarded to
130/// this implementation.
131#[proc_macro_error]
132#[proc_macro_derive(ApiComponent)]
133pub fn derive_api_component(input: TokenStream) -> TokenStream {
134 let input = syn::parse_macro_input!(input as DeriveInput);
135 let DeriveInput {
136 attrs: _attrs,
137 ident,
138 data: _data,
139 generics,
140 vis: _vis,
141 } = input;
142
143 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
144 let schema_impl = Schemas { deprecated: false };
145 quote!(
146 #[automatically_derived]
147 impl #impl_generics apistos::ApiComponent for #ident #ty_generics #where_clause {
148 #schema_impl
149 }
150 )
151 .into()
152}
153
154/// Generates a reusable OpenAPI security scheme.
155///
156/// This `#[derive]` macro should be used in combination with [api_operation](attr.api_operation.html).
157/// The macro requires one and only one `openapi_security`.
158///
159/// ```rust
160/// use apistos::ApiSecurity;
161///
162/// #[derive(ApiSecurity)]
163/// #[openapi_security(scheme(security_type(api_key(name = "api_key", api_key_in = "header"))))]
164/// pub struct ApiKey;
165/// ```
166///
167/// # `#[openapi_security(...)]` options:
168/// - `name = "..."` an optional name for your security definition. If not provided, the struct ident will be used.
169/// - `scheme(...)` a **required** parameter with:
170/// - `description = "..."` an optional description
171/// - `security_type(...)` a **required** parameter with one of
172/// - `oauth2(flows(...))` with
173/// - `implicit(...)` with `authorization_url = "..."` a **required** parameter, `refresh_url = "..."` an optional parameter and `scopes(scope = "...", description = "...")` a list of scopes
174/// - `password(...)` with `token_url = "..."` a **required** parameter, `refresh_url = "..."` an optional parameter and `scopes(scope = "...", description = "...")` a list of scopes
175/// - `client_credentials(...)` with `token_url = "..."` a **required** parameter, `refresh_url = "..."` an optional parameter and `scopes(scope = "...", description = "...")` a list of scopes
176/// - `authorization_code(...)` with `token_url = "..."` a **required** parameter, `refresh_url = "..."` an optional parameter and `scopes(scope = "...", description = "...")` a list of scopes
177/// - `api_key(...)` with
178/// - `name = "..."` a **required** parameter
179/// - `api_key_in = "..."` a **required** parameter being one of `query`, `header` or `cookie`
180/// - `http(...)` with
181/// - `scheme = "..."` a **required** parameter
182/// - `bearer_format = "..."` a **required** parameter
183/// - `open_id_connect(open_id_connect_url = "...")`
184///
185/// # Examples:
186///
187/// ## **oauth2**
188/// ```rust
189/// use apistos::ApiSecurity;
190///
191/// #[derive(ApiSecurity)]
192/// #[openapi_security(scheme(security_type(oauth2(flows(implicit(
193/// authorization_url = "https://authorize.com",
194/// refresh_url = "https://refresh.com",
195/// scopes(scope = "all:read", description = "Read all the things"),
196/// scopes(scope = "all:write", description = "Write all the things")
197/// ))))))]
198/// pub struct ApiKey;
199/// ```
200///
201/// ## **api_key**
202/// ```rust
203/// use apistos::ApiSecurity;
204///
205/// #[derive(ApiSecurity)]
206/// #[openapi_security(scheme(security_type(api_key(name = "api_key", api_key_in = "header"))))]
207/// pub struct ApiKey;
208/// ```
209///
210/// ## **http**
211/// ```rust
212/// use apistos::ApiSecurity;
213///
214/// #[derive(ApiSecurity)]
215/// #[openapi_security(scheme(security_type(http(scheme = "bearer", bearer_format = "JWT"))))]
216/// pub struct ApiKey;
217/// ```
218///
219/// ## **open_id_connect**
220/// ```rust
221/// use apistos::ApiSecurity;
222///
223/// #[derive(ApiSecurity)]
224/// #[openapi_security(scheme(security_type(open_id_connect(open_id_connect_url = "https://connect.com"))))]
225/// pub struct ApiKey;
226/// ```
227#[proc_macro_error]
228#[proc_macro_derive(ApiSecurity, attributes(openapi_security))]
229pub fn derive_api_security(input: TokenStream) -> TokenStream {
230 let input = syn::parse_macro_input!(input as DeriveInput);
231 let DeriveInput {
232 attrs,
233 ident,
234 data: _data,
235 generics,
236 vis: _vis,
237 } = input;
238
239 let security_name: String = ident.to_string().to_case(Case::Snake);
240 let openapi_security_attributes = parse_openapi_security_attrs(&attrs, security_name).expect_or_abort(
241 "expected #[openapi_security(...)] attribute to be present when used with ApiSecurity derive trait",
242 );
243 let security_name = &openapi_security_attributes.name;
244
245 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
246 quote!(
247 #[automatically_derived]
248 impl #impl_generics apistos::ApiComponent for #ident #ty_generics #where_clause {
249 fn child_schemas() -> Vec<(String, apistos::reference_or::ReferenceOr<apistos::Schema>)> {
250 vec![]
251 }
252
253 fn schema() -> Option<(String, apistos::reference_or::ReferenceOr<apistos::Schema>)> {
254 None
255 }
256
257 fn securities() -> std::collections::BTreeMap<String, apistos::security::SecurityScheme> {
258 #openapi_security_attributes
259 }
260
261 fn security_requirement_name() -> Option<String> {
262 Some(#security_name.to_string())
263 }
264 }
265 )
266 .into()
267}
268
269/// Generates a reusable OpenAPI header schema.
270///
271/// This `#[derive]` macro should be used in combination with [api_operation](attr.api_operation.html).
272/// The macro requires one and only one `openapi_header`.
273///
274/// This macro requires your type to derive [JsonSchema](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html).
275///
276/// ```rust
277/// use apistos::ApiHeader;
278/// use schemars::JsonSchema;
279///
280/// #[derive(Debug, Clone, JsonSchema, ApiHeader)]
281/// #[openapi_header(
282/// name = "X-Organization-Slug",
283/// description = "Organization of the current caller",
284/// required = true
285/// )]
286/// pub struct OrganizationSlug(String);
287/// ```
288///
289/// # `#[openapi_header(...)]` options:
290/// - `name = "..."` a **required** parameter with the header name
291/// - `description = "..."` an optional description for the header
292/// - `required = false` an optional parameter, default value is false
293/// - `deprecated = false` an optional parameter, default value is false
294///
295/// Because this macro requires [JsonSchema](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html), all attributes supported by [JsonSchema](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html) are forwarded to
296/// this implementation.
297#[proc_macro_error]
298#[proc_macro_derive(ApiHeader, attributes(openapi_header))]
299pub fn derive_api_header(input: TokenStream) -> TokenStream {
300 let input = syn::parse_macro_input!(input as DeriveInput);
301 let DeriveInput {
302 attrs,
303 ident,
304 data: _data,
305 generics,
306 vis: _vis,
307 } = input;
308
309 let deprecated = extract_deprecated_from_attr(&attrs);
310
311 let openapi_header_attributes = parse_openapi_header_attrs(&attrs, deprecated)
312 .expect_or_abort("expected #[openapi_header(...)] attribute to be present when used with ApiHeader derive trait");
313
314 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
315 let schema_impl = Schemas {
316 deprecated: openapi_header_attributes.deprecated.unwrap_or_default(),
317 };
318 quote!(
319 #[automatically_derived]
320 impl #impl_generics apistos::ApiComponent for #ident #ty_generics #where_clause {
321 #schema_impl
322 }
323
324 #[automatically_derived]
325 impl #impl_generics apistos::ApiHeader for #ident #ty_generics #where_clause {
326 #openapi_header_attributes
327 }
328 )
329 .into()
330}
331
332/// Generates a reusable OpenAPI parameter schema in cookie.
333///
334/// This `#[derive]` macro should be used in combination with [api_operation](attr.api_operation.html).
335/// The macro requires one and only one `openapi_cookie`.
336///
337/// This macro requires your type to derive [JsonSchema](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html).
338///
339/// ```rust
340/// use apistos::ApiCookie;
341/// use schemars::JsonSchema;
342///
343/// #[derive(Debug, Clone, JsonSchema, ApiCookie)]
344/// #[openapi_cookie(
345/// name = "X-Organization-Slug",
346/// description = "Organization of the current caller",
347/// required = true
348/// )]
349/// pub struct OrganizationSlugCookie(String);
350/// ```
351///
352/// # `#[openapi_cookie(...)]` options:
353/// - `name = "..."` a **required** parameter with the header name
354/// - `description = "..."` an optional description for the header
355/// - `required = false` an optional parameter, default value is false
356/// - `deprecated = false` an optional parameter, default value is false
357///
358/// Because this macro requires [JsonSchema](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html), all attributes supported by [JsonSchema](https://docs.rs/schemars/latest/schemars/trait.JsonSchema.html) are forwarded to
359/// this implementation.
360#[proc_macro_error]
361#[proc_macro_derive(ApiCookie, attributes(openapi_cookie))]
362pub fn derive_api_cookie(input: TokenStream) -> TokenStream {
363 let input = syn::parse_macro_input!(input as DeriveInput);
364 let DeriveInput {
365 attrs,
366 ident,
367 data: _data,
368 generics,
369 vis: _vis,
370 } = input;
371
372 let deprecated = extract_deprecated_from_attr(&attrs);
373
374 let openapi_cookie_attributes = parse_openapi_cookie_attrs(&attrs, deprecated)
375 .expect_or_abort("expected #[openapi_cookie(...)] attribute to be present when used with ApiCookie derive trait");
376
377 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
378 quote!(
379 #[automatically_derived]
380 impl #impl_generics apistos::ApiComponent for #ident #ty_generics #where_clause {
381 #openapi_cookie_attributes
382 }
383 )
384 .into()
385}
386
387/// Generates a reusable OpenAPI error schema.
388///
389/// This `#[derive]` macro should be used in combination with [api_operation](attr.api_operation.html).
390/// The macro only supports one `openapi_error`.
391///
392/// ```rust
393/// use apistos::ApiErrorComponent;
394///
395/// #[derive(Clone, ApiErrorComponent)]
396/// #[openapi_error(
397/// status(code = 403),
398/// status(code = 404),
399/// status(code = 405, description = "Invalid input"),
400/// status(code = 409)
401/// )]
402/// pub enum ErrorResponse {
403/// MethodNotAllowed(String),
404/// NotFound(String),
405/// Conflict(String),
406/// Unauthorized(String),
407/// }
408/// ```
409///
410/// # `#[openapi_error(...)]` options:
411/// - `status(...)` a list of possible error status with
412/// - `code = 000` a **required** http status code
413/// - `description = "..."` an optional description, default is the canonical reason of the given status code
414#[proc_macro_error]
415#[proc_macro_derive(ApiErrorComponent, attributes(openapi_error))]
416pub fn derive_api_error(input: TokenStream) -> TokenStream {
417 let input = syn::parse_macro_input!(input as DeriveInput);
418 let DeriveInput {
419 attrs,
420 ident,
421 data: _data,
422 generics,
423 vis: _vis,
424 } = input;
425
426 let openapi_error_attributes = parse_openapi_error_attrs(&attrs).expect_or_abort(
427 "expected #[openapi_error(...)] attribute to be present when used with ApiErrorComponent derive trait",
428 );
429
430 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
431 quote!(
432 #[automatically_derived]
433 impl #impl_generics apistos::ApiErrorComponent for #ident #ty_generics #where_clause {
434 #openapi_error_attributes
435 }
436 )
437 .into()
438}
439
440/// Operation attribute macro implementing [PathItemDefinition](path_item_definition/trait.PathItemDefinition.html) for the decorated handler function.
441///
442/// ```rust
443/// use std::fmt::Display;
444/// use actix_web::web::Json;
445/// use actix_web::http::StatusCode;
446/// use actix_web::ResponseError;
447/// use core::fmt::Formatter;
448/// use apistos::actix::CreatedJson;
449/// use apistos::{api_operation, ApiComponent, ApiErrorComponent};
450/// use schemars::JsonSchema;
451/// use serde::{Serialize, Deserialize};
452///
453/// #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, ApiComponent)]
454/// pub struct Test {
455/// pub test: String
456/// }
457///
458/// #[derive(Serialize, Deserialize, Debug, Clone, ApiErrorComponent)]
459/// #[openapi_error(
460/// status(code = 405, description = "Invalid input"),
461/// )]
462/// pub enum ErrorResponse {
463/// MethodNotAllowed(String),
464/// }
465///
466/// impl Display for ErrorResponse {
467/// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
468/// todo!()
469/// }
470/// }
471///
472/// impl ResponseError for ErrorResponse {
473/// fn status_code(&self) -> StatusCode {
474/// todo!()
475/// }
476/// }
477///
478/// #[api_operation(
479/// tag = "pet",
480/// summary = "Add a new pet to the store",
481/// description = r###"Add a new pet to the store
482/// Plop"###,
483/// error_code = 405
484/// )]
485/// pub(crate) async fn test(
486/// body: Json<Test>,
487/// ) -> Result<CreatedJson<Test>, ErrorResponse> {
488/// Ok(CreatedJson(body.0))
489/// }
490/// ```
491///
492/// # `#[api_operation(...)]` options:
493/// - `skip` a bool allowing to skip documentation for the decorated handler. No component
494/// strictly associated to this operation will be document in the resulting openapi definition.
495/// - `skip_args = "..."` an optional list of arguments to skip. `Apistos` will not try to generate the
496/// documentation for those args which prevent errors linked to missing `ApiComponent` implementation.
497/// - `deprecated` a bool indicating the operation is deprecated. Deprecation can also be declared
498/// with rust `#[deprecated]` decorator.
499/// - `operation_id = "..."` an optional operation id for this operation. Default is the handler's fn name.
500/// - `summary = "..."` an optional summary
501/// - `description = "..."` an optional description
502/// - `tag = "..."` an optional list of tags associated with this operation (define tag multiple times to add to the list)
503/// - `security_scope(...)` an optional list representing which security scopes apply for a given operation with
504/// - `name = "..."` a mandatory name referencing one of the security definitions
505/// - `scope(...)` a list of scopes applying to this operation
506/// - `error_code = 00` an optional list of error codes to document only theses
507/// - `consumes = "..."` allow to override body content type
508/// - `produces = "..."` allow to override response content type
509///
510/// If `summary` or `description` are not provided, a default value will be extracted from the comments. The first line will be used as summary while the rest will be part of the description.
511///
512/// For example:
513/// ```rust
514/// use actix_web::web::Json;
515/// use std::fmt::Display;
516/// use actix_web::http::StatusCode;
517/// use actix_web::ResponseError;
518/// use core::fmt::Formatter;
519/// use apistos::actix::CreatedJson;
520/// use apistos::{api_operation, ApiComponent, ApiErrorComponent};
521/// use schemars::JsonSchema;
522/// use serde::{Serialize, Deserialize};
523///
524/// #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, ApiComponent)]
525/// pub struct Test {
526/// pub test: String
527/// }
528///
529/// #[derive(Serialize, Deserialize, Debug, Clone, ApiErrorComponent)]
530/// #[openapi_error(
531/// status(code = 405, description = "Invalid input"),
532/// )]
533/// pub enum ErrorResponse {
534/// MethodNotAllowed(String),
535/// }
536///
537/// impl Display for ErrorResponse {
538/// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
539/// todo!()
540/// }
541/// }
542///
543/// impl ResponseError for ErrorResponse {
544/// fn status_code(&self) -> StatusCode {
545/// todo!()
546/// }
547/// }
548///
549/// #[api_operation(
550/// tag = "pet",
551/// summary = "Add a new pet to the store",
552/// description = r###"Add a new pet to the store
553/// Plop"###,
554/// )]
555/// pub(crate) async fn test(
556/// body: Json<Test>,
557/// ) -> Result<CreatedJson<Test>, ErrorResponse> {
558/// Ok(CreatedJson(body.0))
559/// }
560/// ```
561///
562/// is equivalent to
563/// ```rust
564/// use std::fmt::Display;
565/// use actix_web::web::Json;
566/// use actix_web::http::StatusCode;
567/// use actix_web::ResponseError;
568/// use core::fmt::Formatter;
569/// use apistos::actix::CreatedJson;
570/// use apistos::{api_operation, ApiComponent, ApiErrorComponent};
571/// use schemars::JsonSchema;
572/// use serde::{Serialize, Deserialize};
573///
574/// #[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, ApiComponent)]
575/// pub struct Test {
576/// pub test: String
577/// }
578///
579/// #[derive(Serialize, Deserialize, Debug, Clone, ApiErrorComponent)]
580/// #[openapi_error(
581/// status(code = 405, description = "Invalid input"),
582/// )]
583/// pub enum ErrorResponse {
584/// MethodNotAllowed(String),
585/// }
586///
587/// impl Display for ErrorResponse {
588/// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
589/// todo!()
590/// }
591/// }
592///
593/// impl ResponseError for ErrorResponse {
594/// fn status_code(&self) -> StatusCode {
595/// todo!()
596/// }
597/// }
598///
599/// /// Add a new pet to the store
600/// /// Add a new pet to the store
601/// /// Plop
602/// #[api_operation(
603/// tag = "pet",
604/// )]
605/// pub(crate) async fn test(
606/// body: Json<Test>,
607/// ) -> Result<CreatedJson<Test>, ErrorResponse> {
608/// Ok(CreatedJson(body.0))
609/// }
610/// ```
611#[proc_macro_error]
612#[proc_macro_attribute]
613pub fn api_operation(attr: TokenStream, item: TokenStream) -> TokenStream {
614 let attr_args = match NestedMeta::parse_meta_list(attr.into()) {
615 Ok(v) => v,
616 Err(e) => {
617 return TokenStream::from(Error::from(e).write_errors());
618 }
619 };
620
621 let operation_attribute = parse_openapi_operation_attrs(&attr_args);
622
623 let default_span = Span::call_site();
624 let item_ast = match syn::parse::<ItemFn>(item) {
625 Ok(v) => v,
626 Err(e) => abort!(e.span(), format!("{e}")),
627 };
628
629 let s_name = format!("{OPENAPI_STRUCT_PREFIX}{}", item_ast.sig.ident);
630 let openapi_struct = Ident::new(&s_name, default_span);
631
632 let generics = &item_ast.sig.generics.clone();
633 let mut generics_call = quote!();
634 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
635 let openapi_struct_def = if !generics.params.is_empty() {
636 let mut generic_types_idents = vec![];
637 for param in &generics.params {
638 match param {
639 GenericParam::Lifetime(_) => {}
640 GenericParam::Const(_) => {}
641 GenericParam::Type(_type) => generic_types_idents.push(_type.ident.clone()),
642 }
643 }
644 let turbofish = ty_generics.as_turbofish();
645 let mut phantom_params = quote!();
646 let mut phantom_params_names = quote!();
647 for generic_types_ident in generic_types_idents {
648 let param_name = Ident::new(
649 &format_ident!("p_{}", generic_types_ident).to_string().to_lowercase(),
650 Span::call_site(),
651 );
652 phantom_params_names.extend(quote!(#param_name: std::marker::PhantomData,));
653 phantom_params.extend(quote!(#param_name: std::marker::PhantomData < #generic_types_ident >,))
654 }
655 generics_call = quote!(#turbofish { #phantom_params_names });
656
657 quote!(struct #openapi_struct #impl_generics #where_clause { #phantom_params })
658 } else {
659 quote!(struct #openapi_struct;)
660 };
661
662 let (responder_wrapper, generated_item_ast) =
663 gen_item_ast(default_span, item_ast, &openapi_struct, &ty_generics, &generics_call);
664 let generated_item_fn = match syn::parse::<ItemFn>(generated_item_ast.clone().into()) {
665 Ok(v) => v,
666 Err(e) => abort!(e.span(), format!("{e}")),
667 };
668 let open_api_def = gen_open_api_impl(
669 &generated_item_fn,
670 operation_attribute,
671 &openapi_struct,
672 &openapi_struct_def,
673 &impl_generics,
674 &ty_generics,
675 where_clause,
676 &responder_wrapper,
677 );
678
679 quote!(
680 #open_api_def
681
682 #generated_item_ast
683 )
684 .into()
685}
686
687// Imports bellow aim at making clippy happy. Those dependencies are necessary for doc-test.
688#[cfg(test)]
689use apistos as _;
690#[cfg(test)]
691use garde as _;
692#[cfg(test)]
693use schemars as _;
694#[cfg(test)]
695use serde as _;