fastapi_gen/lib.rs
1//! This is **private** fastapi codegen library and is not used alone.
2//!
3//! The library contains macro implementations for fastapi library. Content
4//! of the library documentation is available through **fastapi** library itself.
5//! Consider browsing via the **fastapi** crate so all links will work correctly.
6
7#![cfg_attr(doc_cfg, feature(doc_cfg))]
8#![warn(missing_docs)]
9#![warn(rustdoc::broken_intra_doc_links)]
10
11#[cfg(all(feature = "decimal", feature = "decimal_float"))]
12compile_error!("`decimal` and `decimal_float` are mutually exclusive feature flags");
13
14#[cfg(all(
15 feature = "actix_extras",
16 feature = "axum_extras",
17 feature = "rocket_extras"
18))]
19compile_error!(
20 "`actix_extras`, `axum_extras` and `rocket_extras` are mutually exclusive feature flags"
21);
22
23use std::{
24 borrow::{Borrow, Cow},
25 error::Error,
26 fmt::Display,
27 mem,
28 ops::Deref,
29};
30
31use component::schema::Schema;
32use doc_comment::CommentAttributes;
33
34use component::into_params::IntoParams;
35use ext::{PathOperationResolver, PathOperations, PathResolver};
36use openapi::OpenApi;
37use proc_macro::TokenStream;
38use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
39
40use proc_macro2::{Group, Ident, Punct, Span, TokenStream as TokenStream2};
41use syn::{
42 bracketed,
43 parse::{Parse, ParseStream},
44 punctuated::Punctuated,
45 token::Bracket,
46 DeriveInput, ExprPath, GenericParam, ItemFn, Lit, LitStr, Member, Token,
47};
48
49mod component;
50mod doc_comment;
51mod ext;
52mod openapi;
53mod path;
54mod schema_type;
55mod security_requirement;
56
57use crate::path::{Path, PathAttr};
58
59use self::{
60 component::{
61 features::{self, Feature},
62 ComponentSchema, ComponentSchemaProps, TypeTree,
63 },
64 openapi::parse_openapi_attrs,
65 path::response::derive::{IntoResponses, ToResponse},
66};
67
68#[cfg(feature = "config")]
69static CONFIG: once_cell::sync::Lazy<fastapi_config::Config> =
70 once_cell::sync::Lazy::new(fastapi_config::Config::read_from_file);
71
72#[proc_macro_derive(ToSchema, attributes(schema))]
73/// Generate reusable OpenAPI schema to be used
74/// together with [`OpenApi`][openapi_derive].
75///
76/// This is `#[derive]` implementation for [`ToSchema`][to_schema] trait. The macro accepts one
77/// `schema`
78/// attribute optionally which can be used to enhance generated documentation. The attribute can be placed
79/// at item level or field and variant levels in structs and enum.
80///
81/// You can use the Rust's own `#[deprecated]` attribute on any struct, enum or field to mark it as deprecated and it will
82/// reflect to the generated OpenAPI spec.
83///
84/// `#[deprecated]` attribute supports adding additional details such as a reason and or since version but this is is not supported in
85/// OpenAPI. OpenAPI has only a boolean flag to determine deprecation. While it is totally okay to declare deprecated with reason
86/// `#[deprecated = "There is better way to do this"]` the reason would not render in OpenAPI spec.
87///
88/// Doc comments on fields will resolve to field descriptions in generated OpenAPI doc. On struct
89/// level doc comments will resolve to object descriptions.
90///
91/// Schemas derived with `ToSchema` will be automatically collected from usage. In case of looping
92/// schema tree _`no_recursion`_ attribute must be used to break from recurring into infinite loop.
93/// See [more details from example][derive@ToSchema#examples]. All arguments of generic schemas
94/// must implement `ToSchema` trait.
95///
96/// ```rust
97/// /// This is a pet
98/// #[derive(fastapi::ToSchema)]
99/// struct Pet {
100/// /// Name for your pet
101/// name: String,
102/// }
103/// ```
104///
105/// # Named Field Struct Optional Configuration Options for `#[schema(...)]`
106///
107/// * `description = ...` Can be literal string or Rust expression e.g. _`const`_ reference or
108/// `include_str!(...)` statement. This can be used to override **default** description what is
109/// resolved from doc comments of the type.
110/// * `example = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
111/// **Deprecated since OpenAPI 3.0, using `examples` is preferred instead.**
112/// * `examples(..., ...)` Comma separated list defining multiple _`examples`_ for the schema. Each
113/// _`example`_ Can be any value e.g. literal, method reference or _`json!(...)`_.
114/// * `xml(...)` Can be used to define [`Xml`][xml] object properties applicable to Structs.
115/// * `title = ...` Literal string value. Can be used to define title for struct in OpenAPI
116/// document. Some OpenAPI code generation libraries also use this field as a name for the
117/// struct.
118/// * `rename_all = ...` Supports same syntax as _serde_ _`rename_all`_ attribute. Will rename all fields
119/// of the structs accordingly. If both _serde_ `rename_all` and _schema_ _`rename_all`_ are defined
120/// __serde__ will take precedence.
121/// * `as = ...` Can be used to define alternative path and name for the schema what will be used in
122/// the OpenAPI. E.g _`as = path::to::Pet`_. This would make the schema appear in the generated
123/// OpenAPI spec as _`path.to.Pet`_. This same name will be used throughout the OpenAPI generated
124/// with `fastapi` when the type is being referenced in [`OpenApi`][openapi_derive] derive macro
125/// or in [`fastapi::path(...)`][path_macro] macro.
126/// * `bound = ...` Can be used to override default trait bounds on generated `impl`s.
127/// See [Generic schemas section](#generic-schemas) below for more details.
128/// * `default` Can be used to populate default values on all fields using the struct's
129/// [`Default`] implementation.
130/// * `deprecated` Can be used to mark all fields as deprecated in the generated OpenAPI spec but
131/// not in the code. If you'd like to mark the fields as deprecated in the code as well use
132/// Rust's own `#[deprecated]` attribute instead.
133/// * `max_properties = ...` Can be used to define maximum number of properties this struct can
134/// contain. Value must be a number.
135/// * `min_properties = ...` Can be used to define minimum number of properties this struct can
136/// contain. Value must be a number.
137///* `no_recursion` Is used to break from recursion in case of looping schema tree e.g. `Pet` ->
138/// `Owner` -> `Pet`. _`no_recursion`_ attribute must be used within `Ower` type not to allow
139/// recurring into `Pet`. Failing to do so will cause infinite loop and runtime **panic**. On
140/// struct level the _`no_recursion`_ rule will be applied to all of its fields.
141///
142/// ## Named Fields Optional Configuration Options for `#[schema(...)]`
143///
144/// * `example = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
145/// **Deprecated since OpenAPI 3.0, using `examples` is preferred instead.**
146/// * `examples(..., ...)` Comma separated list defining multiple _`examples`_ for the schema. Each
147/// _`example`_ Can be any value e.g. literal, method reference or _`json!(...)`_.
148/// * `default = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
149/// * `format = ...` May either be variant of the [`KnownFormat`][known_format] enum, or otherwise
150/// an open value as a string. By default the format is derived from the type of the property
151/// according OpenApi spec.
152/// * `write_only` Defines property is only used in **write** operations *POST,PUT,PATCH* but not in *GET*
153/// * `read_only` Defines property is only used in **read** operations *GET* but not in *POST,PUT,PATCH*
154/// * `xml(...)` Can be used to define [`Xml`][xml] object properties applicable to named fields.
155/// See configuration options at xml attributes of [`ToSchema`][to_schema_xml]
156/// * `value_type = ...` Can be used to override default type derived from type of the field used in OpenAPI spec.
157/// This is useful in cases where the default type does not correspond to the actual type e.g. when
158/// any third-party types are used which are not [`ToSchema`][to_schema]s nor [`primitive` types][primitive].
159/// The value can be any Rust type what normally could be used to serialize to JSON, or either virtual type _`Object`_
160/// or _`Value`_.
161/// _`Object`_ will be rendered as generic OpenAPI object _(`type: object`)_.
162/// _`Value`_ will be rendered as any OpenAPI value (i.e. no `type` restriction).
163/// * `inline` If the type of this field implements [`ToSchema`][to_schema], then the schema definition
164/// will be inlined. **warning:** Don't use this for recursive data types!
165///
166/// **Note!**<br>Using `inline` with generic arguments might lead to incorrect spec generation.
167/// This is due to the fact that during compilation we cannot know how to treat the generic
168/// argument and there is difference whether it is a primitive type or another generic type.
169/// * `required = ...` Can be used to enforce required status for the field. [See
170/// rules][derive@ToSchema#field-nullability-and-required-rules]
171/// * `nullable` Defines property is nullable (note this is different to non-required).
172/// * `rename = ...` Supports same syntax as _serde_ _`rename`_ attribute. Will rename field
173/// accordingly. If both _serde_ `rename` and _schema_ _`rename`_ are defined __serde__ will take
174/// precedence.
175/// * `multiple_of = ...` Can be used to define multiplier for a value. Value is considered valid
176/// division will result an `integer`. Value must be strictly above _`0`_.
177/// * `maximum = ...` Can be used to define inclusive upper bound to a `number` value.
178/// * `minimum = ...` Can be used to define inclusive lower bound to a `number` value.
179/// * `exclusive_maximum = ...` Can be used to define exclusive upper bound to a `number` value.
180/// * `exclusive_minimum = ...` Can be used to define exclusive lower bound to a `number` value.
181/// * `max_length = ...` Can be used to define maximum length for `string` types.
182/// * `min_length = ...` Can be used to define minimum length for `string` types.
183/// * `pattern = ...` Can be used to define valid regular expression in _ECMA-262_ dialect the field value must match.
184/// * `max_items = ...` Can be used to define maximum items allowed for `array` fields. Value must
185/// be non-negative integer.
186/// * `min_items = ...` Can be used to define minimum items allowed for `array` fields. Value must
187/// be non-negative integer.
188/// * `schema_with = ...` Use _`schema`_ created by provided function reference instead of the
189/// default derived _`schema`_. The function must match to `fn() -> Into<RefOr<Schema>>`. It does
190/// not accept arguments and must return anything that can be converted into `RefOr<Schema>`.
191/// * `additional_properties = ...` Can be used to define free form types for maps such as
192/// [`HashMap`](std::collections::HashMap) and [`BTreeMap`](std::collections::BTreeMap).
193/// Free form type enables use of arbitrary types within map values.
194/// Supports formats _`additional_properties`_ and _`additional_properties = true`_.
195/// * `deprecated` Can be used to mark the field as deprecated in the generated OpenAPI spec but
196/// not in the code. If you'd like to mark the field as deprecated in the code as well use
197/// Rust's own `#[deprecated]` attribute instead.
198/// * `content_encoding = ...` Can be used to define content encoding used for underlying schema object.
199/// See [`Object::content_encoding`][schema_object_encoding]
200/// * `content_media_type = ...` Can be used to define MIME type of a string for underlying schema object.
201/// See [`Object::content_media_type`][schema_object_media_type]
202///* `ignore` or `ignore = ...` Can be used to skip the field from being serialized to OpenAPI schema. It accepts either a literal `bool` value
203/// or a path to a function that returns `bool` (`Fn() -> bool`).
204///* `no_recursion` Is used to break from recursion in case of looping schema tree e.g. `Pet` ->
205/// `Owner` -> `Pet`. _`no_recursion`_ attribute must be used within `Ower` type not to allow
206/// recurring into `Pet`. Failing to do so will cause infinite loop and runtime **panic**.
207///
208/// #### Field nullability and required rules
209///
210/// Field is considered _`required`_ if
211/// * it is not `Option` field
212/// * and it does not have _`skip_serializing_if`_ property
213/// * and it does not have _`serde_with`_ _[`double_option`](https://docs.rs/serde_with/latest/serde_with/rust/double_option/index.html)_
214/// * and it does not have default value provided with serde _`default`_
215/// attribute
216///
217/// Field is considered _`nullable`_ when field type is _`Option`_.
218///
219/// ## Xml attribute Configuration Options
220///
221/// * `xml(name = "...")` Will set name for property or type.
222/// * `xml(namespace = "...")` Will set namespace for xml element which needs to be valid uri.
223/// * `xml(prefix = "...")` Will set prefix for name.
224/// * `xml(attribute)` Will translate property to xml attribute instead of xml element.
225/// * `xml(wrapped)` Will make wrapped xml element.
226/// * `xml(wrapped(name = "wrap_name"))` Will override the wrapper elements name.
227///
228/// See [`Xml`][xml] for more details.
229///
230/// # Unnamed Field Struct Optional Configuration Options for `#[schema(...)]`
231///
232/// * `description = ...` Can be literal string or Rust expression e.g. [_`const`_][const] reference or
233/// `include_str!(...)` statement. This can be used to override **default** description what is
234/// resolved from doc comments of the type.
235/// * `example = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
236/// **Deprecated since OpenAPI 3.0, using `examples` is preferred instead.**
237/// * `examples(..., ...)` Comma separated list defining multiple _`examples`_ for the schema. Each
238/// _`example`_ Can be any value e.g. literal, method reference or _`json!(...)`_.
239/// * `default = ...` Can be any value e.g. literal, method reference or _`json!(...)`_. If no value
240/// is specified, and the struct has only one field, the field's default value in the schema will be
241/// set from the struct's [`Default`] implementation.
242/// * `format = ...` May either be variant of the [`KnownFormat`][known_format] enum, or otherwise
243/// an open value as a string. By default the format is derived from the type of the property
244/// according OpenApi spec.
245/// * `value_type = ...` Can be used to override default type derived from type of the field used in OpenAPI spec.
246/// This is useful in cases where the default type does not correspond to the actual type e.g. when
247/// any third-party types are used which are not [`ToSchema`][to_schema]s nor [`primitive` types][primitive].
248/// The value can be any Rust type what normally could be used to serialize to JSON or either virtual type _`Object`_
249/// or _`Value`_.
250/// _`Object`_ will be rendered as generic OpenAPI object _(`type: object`)_.
251/// _`Value`_ will be rendered as any OpenAPI value (i.e. no `type` restriction).
252/// * `title = ...` Literal string value. Can be used to define title for struct in OpenAPI
253/// document. Some OpenAPI code generation libraries also use this field as a name for the
254/// struct.
255/// * `as = ...` Can be used to define alternative path and name for the schema what will be used in
256/// the OpenAPI. E.g _`as = path::to::Pet`_. This would make the schema appear in the generated
257/// OpenAPI spec as _`path.to.Pet`_. This same name will be used throughout the OpenAPI generated
258/// with `fastapi` when the type is being referenced in [`OpenApi`][openapi_derive] derive macro
259/// or in [`fastapi::path(...)`][path_macro] macro.
260/// * `bound = ...` Can be used to override default trait bounds on generated `impl`s.
261/// See [Generic schemas section](#generic-schemas) below for more details.
262/// * `deprecated` Can be used to mark the field as deprecated in the generated OpenAPI spec but
263/// not in the code. If you'd like to mark the field as deprecated in the code as well use
264/// Rust's own `#[deprecated]` attribute instead.
265/// * `content_encoding = ...` Can be used to define content encoding used for underlying schema object.
266/// See [`Object::content_encoding`][schema_object_encoding]
267/// * `content_media_type = ...` Can be used to define MIME type of a string for underlying schema object.
268/// See [`Object::content_media_type`][schema_object_media_type]
269///* `no_recursion` Is used to break from recursion in case of looping schema tree e.g. `Pet` ->
270/// `Owner` -> `Pet`. _`no_recursion`_ attribute must be used within `Ower` type not to allow
271/// recurring into `Pet`. Failing to do so will cause infinite loop and runtime **panic**.
272///
273/// # Enum Optional Configuration Options for `#[schema(...)]`
274///
275/// ## Plain Enum having only `Unit` variants Optional Configuration Options for `#[schema(...)]`
276///
277/// * `description = ...` Can be literal string or Rust expression e.g. [_`const`_][const] reference or
278/// `include_str!(...)` statement. This can be used to override **default** description what is
279/// resolved from doc comments of the type.
280/// * `example = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
281/// **Deprecated since OpenAPI 3.0, using `examples` is preferred instead.**
282/// * `examples(..., ...)` Comma separated list defining multiple _`examples`_ for the schema. Each
283/// _`example`_ Can be any value e.g. literal, method reference or _`json!(...)`_.
284/// * `default = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
285/// * `title = ...` Literal string value. Can be used to define title for enum in OpenAPI
286/// document. Some OpenAPI code generation libraries also use this field as a name for the
287/// enum.
288/// * `rename_all = ...` Supports same syntax as _serde_ _`rename_all`_ attribute. Will rename all
289/// variants of the enum accordingly. If both _serde_ `rename_all` and _schema_ _`rename_all`_
290/// are defined __serde__ will take precedence.
291/// * `as = ...` Can be used to define alternative path and name for the schema what will be used in
292/// the OpenAPI. E.g _`as = path::to::Pet`_. This would make the schema appear in the generated
293/// OpenAPI spec as _`path.to.Pet`_. This same name will be used throughout the OpenAPI generated
294/// with `fastapi` when the type is being referenced in [`OpenApi`][openapi_derive] derive macro
295/// or in [`fastapi::path(...)`][path_macro] macro.
296/// * `bound = ...` Can be used to override default trait bounds on generated `impl`s.
297/// See [Generic schemas section](#generic-schemas) below for more details.
298/// * `deprecated` Can be used to mark the enum as deprecated in the generated OpenAPI spec but
299/// not in the code. If you'd like to mark the enum as deprecated in the code as well use
300/// Rust's own `#[deprecated]` attribute instead.
301///
302/// ### Plain Enum Variant Optional Configuration Options for `#[schema(...)]`
303///
304/// * `rename = ...` Supports same syntax as _serde_ _`rename`_ attribute. Will rename variant
305/// accordingly. If both _serde_ `rename` and _schema_ _`rename`_ are defined __serde__ will take
306/// precedence. **Note!** [`Repr enum`][macro@ToSchema#repr-attribute-support] variant does not
307/// support _`rename`_.
308///
309/// ## Mixed Enum Optional Configuration Options for `#[schema(...)]`
310///
311/// * `description = ...` Can be literal string or Rust expression e.g. [_`const`_][const] reference or
312/// `include_str!(...)` statement. This can be used to override **default** description what is
313/// resolved from doc comments of the type.
314/// * `example = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
315/// **Deprecated since OpenAPI 3.0, using `examples` is preferred instead.**
316/// * `examples(..., ...)` Comma separated list defining multiple _`examples`_ for the schema. Each
317/// * `default = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
318/// * `title = ...` Literal string value. Can be used to define title for enum in OpenAPI
319/// document. Some OpenAPI code generation libraries also use this field as a name for the
320/// enum.
321/// * `rename_all = ...` Supports same syntax as _serde_ _`rename_all`_ attribute. Will rename all
322/// variants of the enum accordingly. If both _serde_ `rename_all` and _schema_ _`rename_all`_
323/// are defined __serde__ will take precedence.
324/// * `as = ...` Can be used to define alternative path and name for the schema what will be used in
325/// the OpenAPI. E.g _`as = path::to::Pet`_. This would make the schema appear in the generated
326/// OpenAPI spec as _`path.to.Pet`_. This same name will be used throughout the OpenAPI generated
327/// with `fastapi` when the type is being referenced in [`OpenApi`][openapi_derive] derive macro
328/// or in [`fastapi::path(...)`][path_macro] macro.
329/// * `bound = ...` Can be used to override default trait bounds on generated `impl`s.
330/// See [Generic schemas section](#generic-schemas) below for more details.
331/// * `deprecated` Can be used to mark the enum as deprecated in the generated OpenAPI spec but
332/// not in the code. If you'd like to mark the enum as deprecated in the code as well use
333/// Rust's own `#[deprecated]` attribute instead.
334/// * `discriminator = ...` or `discriminator(...)` Can be used to define OpenAPI discriminator
335/// field for enums with single unnamed _`ToSchema`_ reference field. See the [discriminator
336/// syntax][derive@ToSchema#schemadiscriminator-syntax].
337///* `no_recursion` Is used to break from recursion in case of looping schema tree e.g. `Pet` ->
338/// `Owner` -> `Pet`. _`no_recursion`_ attribute must be used within `Ower` type not to allow
339/// recurring into `Pet`. Failing to do so will cause infinite loop and runtime **panic**. On
340/// enum level the _`no_recursion`_ rule will be applied to all of its variants.
341///
342/// ### `#[schema(discriminator)]` syntax
343///
344/// Discriminator can **only** be used with enums having **`#[serde(untagged)]`** attribute and
345/// each variant must have only one unnamed field schema reference to type implementing
346/// _`ToSchema`_.
347///
348/// **Simple form `discriminator = ...`**
349///
350/// Can be literal string or expression e.g. [_`const`_][const] reference. It can be defined as
351/// _`discriminator = "value"`_ where the assigned value is the
352/// discriminator field that must exists in each variant referencing schema.
353///
354/// **Complex form `discriminator(...)`**
355///
356/// * `property_name = ...` Can be literal string or expression e.g. [_`const`_][const] reference.
357/// * mapping `key` Can be literal string or expression e.g. [_`const`_][const] reference.
358/// * mapping `value` Can be literal string or expression e.g. [_`const`_][const] reference.
359///
360/// Additionally discriminator can be defined with custom mappings as show below. The _`mapping`_
361/// values defines _**key = value**_ pairs where _**key**_ is the expected value for _**property_name**_ field
362/// and _**value**_ is schema to map.
363/// ```text
364/// discriminator(property_name = "my_field", mapping(
365/// ("value" = "#/components/schemas/Schema1"),
366/// ("value2" = "#/components/schemas/Schema2")
367/// ))
368/// ```
369///
370/// ### Mixed Enum Named Field Variant Optional Configuration Options for `#[serde(schema)]`
371///
372/// * `example = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
373/// **Deprecated since OpenAPI 3.0, using `examples` is preferred instead.**
374/// * `examples(..., ...)` Comma separated list defining multiple _`examples`_ for the schema. Each
375/// * `default = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
376/// * `title = ...` Literal string value. Can be used to define title for enum variant in OpenAPI
377/// document. Some OpenAPI code generation libraries also use this field as a name for the
378/// enum.
379/// * `xml(...)` Can be used to define [`Xml`][xml] object properties applicable to Structs.
380/// * `rename = ...` Supports same syntax as _serde_ _`rename`_ attribute. Will rename variant
381/// accordingly. If both _serde_ `rename` and _schema_ _`rename`_ are defined __serde__ will take
382/// precedence.
383/// * `rename_all = ...` Supports same syntax as _serde_ _`rename_all`_ attribute. Will rename all
384/// variant fields accordingly. If both _serde_ `rename_all` and _schema_ _`rename_all`_
385/// are defined __serde__ will take precedence.
386/// * `deprecated` Can be used to mark the enum as deprecated in the generated OpenAPI spec but
387/// not in the code. If you'd like to mark the enum as deprecated in the code as well use
388/// Rust's own `#[deprecated]` attribute instead.
389/// * `max_properties = ...` Can be used to define maximum number of properties this struct can
390/// contain. Value must be a number.
391/// * `min_properties = ...` Can be used to define minimum number of properties this struct can
392/// contain. Value must be a number.
393///* `no_recursion` Is used to break from recursion in case of looping schema tree e.g. `Pet` ->
394/// `Owner` -> `Pet`. _`no_recursion`_ attribute must be used within `Ower` type not to allow
395/// recurring into `Pet`. Failing to do so will cause infinite loop and runtime **panic**. On
396/// named field variant level the _`no_recursion`_ rule will be applied to all of its fields.
397///
398/// ## Mixed Enum Unnamed Field Variant Optional Configuration Options for `#[serde(schema)]`
399///
400/// * `example = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
401/// **Deprecated since OpenAPI 3.0, using `examples` is preferred instead.**
402/// * `examples(..., ...)` Comma separated list defining multiple _`examples`_ for the schema. Each
403/// _`example`_ Can be any value e.g. literal, method reference or _`json!(...)`_.
404/// * `default = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
405/// * `title = ...` Literal string value. Can be used to define title for enum variant in OpenAPI
406/// document. Some OpenAPI code generation libraries also use this field as a name for the
407/// struct.
408/// * `rename = ...` Supports same syntax as _serde_ _`rename`_ attribute. Will rename variant
409/// accordingly. If both _serde_ `rename` and _schema_ _`rename`_ are defined __serde__ will take
410/// precedence.
411/// * `format = ...` May either be variant of the [`KnownFormat`][known_format] enum, or otherwise
412/// an open value as a string. By default the format is derived from the type of the property
413/// according OpenApi spec.
414/// * `value_type = ...` Can be used to override default type derived from type of the field used in OpenAPI spec.
415/// This is useful in cases where the default type does not correspond to the actual type e.g. when
416/// any third-party types are used which are not [`ToSchema`][to_schema]s nor [`primitive` types][primitive].
417/// The value can be any Rust type what normally could be used to serialize to JSON or either virtual type _`Object`_
418/// or _`Value`_.
419/// _`Object`_ will be rendered as generic OpenAPI object _(`type: object`)_.
420/// _`Value`_ will be rendered as any OpenAPI value (i.e. no `type` restriction).
421/// * `deprecated` Can be used to mark the field as deprecated in the generated OpenAPI spec but
422/// not in the code. If you'd like to mark the field as deprecated in the code as well use
423/// Rust's own `#[deprecated]` attribute instead.
424///* `no_recursion` Is used to break from recursion in case of looping schema tree e.g. `Pet` ->
425/// `Owner` -> `Pet`. _`no_recursion`_ attribute must be used within `Ower` type not to allow
426/// recurring into `Pet`. Failing to do so will cause infinite loop and runtime **panic**.
427///
428/// #### Mixed Enum Unnamed Field Variant's Field Configuration Options
429///
430/// * `inline` If the type of this field implements [`ToSchema`][to_schema], then the schema definition
431/// will be inlined. **warning:** Don't use this for recursive data types!
432///
433/// **Note!**<br>Using `inline` with generic arguments might lead to incorrect spec generation.
434/// This is due to the fact that during compilation we cannot know how to treat the generic
435/// argument and there is difference whether it is a primitive type or another generic type.
436///
437/// _**Inline unnamed field variant schemas.**_
438/// ```rust
439/// # use fastapi::ToSchema;
440/// # #[derive(ToSchema)]
441/// # enum Number {
442/// # One,
443/// # }
444/// #
445/// # #[derive(ToSchema)]
446/// # enum Color {
447/// # Spade,
448/// # }
449/// #[derive(ToSchema)]
450/// enum Card {
451/// Number(#[schema(inline)] Number),
452/// Color(#[schema(inline)] Color),
453/// }
454/// ```
455///
456/// ## Mixed Enum Unit Field Variant Optional Configuration Options for `#[serde(schema)]`
457///
458/// * `example = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
459/// **Deprecated since OpenAPI 3.0, using `examples` is preferred instead.**
460/// * `examples(..., ...)` Comma separated list defining multiple _`examples`_ for the schema. Each
461/// _`example`_ Can be any value e.g. literal, method reference or _`json!(...)`_.
462/// * `title = ...` Literal string value. Can be used to define title for enum variant in OpenAPI
463/// document. Some OpenAPI code generation libraries also use this field as a name for the
464/// struct.
465/// * `rename = ...` Supports same syntax as _serde_ _`rename`_ attribute. Will rename variant
466/// accordingly. If both _serde_ `rename` and _schema_ _`rename`_ are defined __serde__ will take
467/// precedence.
468/// * `deprecated` Can be used to mark the field as deprecated in the generated OpenAPI spec but
469/// not in the code. If you'd like to mark the field as deprecated in the code as well use
470/// Rust's own `#[deprecated]` attribute instead.
471///
472/// # Partial `#[serde(...)]` attributes support
473///
474/// ToSchema derive has partial support for [serde attributes]. These supported attributes will reflect to the
475/// generated OpenAPI doc. For example if _`#[serde(skip)]`_ is defined the attribute will not show up in the OpenAPI spec at all since it will not never
476/// be serialized anyway. Similarly the _`rename`_ and _`rename_all`_ will reflect to the generated OpenAPI doc.
477///
478/// * `rename_all = "..."` Supported at the container level.
479/// * `rename = "..."` Supported **only** at the field or variant level.
480/// * `skip = "..."` Supported **only** at the field or variant level.
481/// * `skip_serializing = "..."` Supported **only** at the field or variant level.
482/// * `skip_deserializing = "..."` Supported **only** at the field or variant level.
483/// * `skip_serializing_if = "..."` Supported **only** at the field level.
484/// * `with = ...` Supported **only at field level.**
485/// * `tag = "..."` Supported at the container level.
486/// * `content = "..."` Supported at the container level, allows [adjacently-tagged enums](https://serde.rs/enum-representations.html#adjacently-tagged).
487/// This attribute requires that a `tag` is present, otherwise serde will trigger a compile-time
488/// failure.
489/// * `untagged` Supported at the container level. Allows [untagged
490/// enum representation](https://serde.rs/enum-representations.html#untagged).
491/// * `default` Supported at the container level and field level according to [serde attributes].
492/// * `deny_unknown_fields` Supported at the container level.
493/// * `flatten` Supported at the field level.
494///
495/// Other _`serde`_ attributes works as is but does not have any effect on the generated OpenAPI doc.
496///
497/// **Note!** `tag` attribute has some limitations like it cannot be used with **tuple types**. See more at
498/// [enum representation docs](https://serde.rs/enum-representations.html).
499///
500/// **Note!** `with` attribute is used in tandem with [serde_with](https://github.com/jonasbb/serde_with) to recognize
501/// _[`double_option`](https://docs.rs/serde_with/latest/serde_with/rust/double_option/index.html)_ from **field value**.
502/// _`double_option`_ is **only** supported attribute from _`serde_with`_ crate.
503///
504/// ```rust
505/// # use serde::Serialize;
506/// # use fastapi::ToSchema;
507/// #[derive(Serialize, ToSchema)]
508/// struct Foo(String);
509///
510/// #[derive(Serialize, ToSchema)]
511/// #[serde(rename_all = "camelCase")]
512/// enum Bar {
513/// UnitValue,
514/// #[serde(rename_all = "camelCase")]
515/// NamedFields {
516/// #[serde(rename = "id")]
517/// named_id: &'static str,
518/// name_list: Option<Vec<String>>
519/// },
520/// UnnamedFields(Foo),
521/// #[serde(skip)]
522/// SkipMe,
523/// }
524/// ```
525///
526/// _**Add custom `tag` to change JSON representation to be internally tagged.**_
527/// ```rust
528/// # use serde::Serialize;
529/// # use fastapi::ToSchema;
530/// #[derive(Serialize, ToSchema)]
531/// struct Foo(String);
532///
533/// #[derive(Serialize, ToSchema)]
534/// #[serde(tag = "tag")]
535/// enum Bar {
536/// UnitValue,
537/// NamedFields {
538/// id: &'static str,
539/// names: Option<Vec<String>>
540/// },
541/// }
542/// ```
543///
544/// _**Add serde `default` attribute for MyValue struct. Similarly `default` could be added to
545/// individual fields as well. If `default` is given the field's affected will be treated
546/// as optional.**_
547/// ```rust
548/// #[derive(fastapi::ToSchema, serde::Deserialize, Default)]
549/// #[serde(default)]
550/// struct MyValue {
551/// field: String
552/// }
553/// ```
554///
555/// # `#[repr(...)]` attribute support
556///
557/// [Serde repr](https://github.com/dtolnay/serde-repr) allows field-less enums be represented by
558/// their numeric value.
559///
560/// * `repr(u*)` for unsigned integer.
561/// * `repr(i*)` for signed integer.
562///
563/// **Supported schema attributes**
564///
565/// * `example = ...` Can be any value e.g. literal, method reference or _`json!(...)`_.
566/// **Deprecated since OpenAPI 3.0, using `examples` is preferred instead.**
567/// * `examples(..., ...)` Comma separated list defining multiple _`examples`_ for the schema. Each
568/// _`example`_ Can be any value e.g. literal, method reference or _`json!(...)`_.
569/// * `title = ...` Literal string value. Can be used to define title for enum in OpenAPI
570/// document. Some OpenAPI code generation libraries also use this field as a name for the
571/// struct.
572/// * `as = ...` Can be used to define alternative path and name for the schema what will be used in
573/// the OpenAPI. E.g _`as = path::to::Pet`_. This would make the schema appear in the generated
574/// OpenAPI spec as _`path.to.Pet`_. This same name will be used throughout the OpenAPI generated
575/// with `fastapi` when the type is being referenced in [`OpenApi`][openapi_derive] derive macro
576/// or in [`fastapi::path(...)`][path_macro] macro.
577///
578/// _**Create enum with numeric values.**_
579/// ```rust
580/// # use fastapi::ToSchema;
581/// #[derive(ToSchema)]
582/// #[repr(u8)]
583/// #[schema(default = default_value, example = 2)]
584/// enum Mode {
585/// One = 1,
586/// Two,
587/// }
588///
589/// fn default_value() -> u8 {
590/// 1
591/// }
592/// ```
593///
594/// _**You can use `skip` and `tag` attributes from serde.**_
595/// ```rust
596/// # use fastapi::ToSchema;
597/// #[derive(ToSchema, serde::Serialize)]
598/// #[repr(i8)]
599/// #[serde(tag = "code")]
600/// enum ExitCode {
601/// Error = -1,
602/// #[serde(skip)]
603/// Unknown = 0,
604/// Ok = 1,
605/// }
606/// ```
607///
608/// # Generic schemas
609///
610/// Fastapi supports full set of deeply nested generics as shown below. The type will implement
611/// [`ToSchema`][to_schema] if and only if all the generic types implement `ToSchema` by default.
612/// That is in Rust `impl<T> ToSchema for MyType<T> where T: Schema { ... }`.
613/// You can also specify `bound = ...` on the item to override the default auto bounds.
614///
615/// The _`as = ...`_ attribute is used to define the prefixed or alternative name for the component
616/// in question. This same name will be used throughout the OpenAPI generated with `fastapi` when
617/// the type is being referenced in [`OpenApi`][openapi_derive] derive macro or in [`fastapi::path(...)`][path_macro] macro.
618///
619/// ```rust
620/// # use fastapi::ToSchema;
621/// # use std::borrow::Cow;
622/// #[derive(ToSchema)]
623/// #[schema(as = path::MyType<T>)]
624/// struct Type<T> {
625/// t: T,
626/// }
627///
628/// #[derive(ToSchema)]
629/// struct Person<'p, T: Sized, P> {
630/// id: usize,
631/// name: Option<Cow<'p, str>>,
632/// field: T,
633/// t: P,
634/// }
635///
636/// #[derive(ToSchema)]
637/// #[schema(as = path::to::PageList)]
638/// struct Page<T> {
639/// total: usize,
640/// page: usize,
641/// pages: usize,
642/// items: Vec<T>,
643/// }
644///
645/// #[derive(ToSchema)]
646/// #[schema(as = path::to::Element<T>)]
647/// enum E<T> {
648/// One(T),
649/// Many(Vec<T>),
650/// }
651/// ```
652/// When generic types are registered to the `OpenApi` the full type declaration must be provided.
653/// See the full example in test [schema_generics.rs](https://github.com/nxpkg/fastapi/blob/master/fastapi-gen/tests/schema_generics.rs)
654///
655/// # Examples
656///
657/// _**Simple example of a Pet with descriptions and object level example.**_
658/// ```rust
659/// # use fastapi::ToSchema;
660/// /// This is a pet.
661/// #[derive(ToSchema)]
662/// #[schema(example = json!({"name": "bob the cat", "id": 0}))]
663/// struct Pet {
664/// /// Unique id of a pet.
665/// id: u64,
666/// /// Name of a pet.
667/// name: String,
668/// /// Age of a pet if known.
669/// age: Option<i32>,
670/// }
671/// ```
672///
673/// _**The `schema` attribute can also be placed at field level as follows.**_
674/// ```rust
675/// # use fastapi::ToSchema;
676/// #[derive(ToSchema)]
677/// struct Pet {
678/// #[schema(example = 1, default = 0)]
679/// id: u64,
680/// name: String,
681/// age: Option<i32>,
682/// }
683/// ```
684///
685/// _**You can also use method reference for attribute values.**_
686/// ```rust
687/// # use fastapi::ToSchema;
688/// #[derive(ToSchema)]
689/// struct Pet {
690/// #[schema(example = u64::default, default = u64::default)]
691/// id: u64,
692/// #[schema(default = default_name)]
693/// name: String,
694/// age: Option<i32>,
695/// }
696///
697/// fn default_name() -> String {
698/// "bob".to_string()
699/// }
700/// ```
701///
702/// _**For enums and unnamed field structs you can define `schema` at type level.**_
703/// ```rust
704/// # use fastapi::ToSchema;
705/// #[derive(ToSchema)]
706/// #[schema(example = "Bus")]
707/// enum VehicleType {
708/// Rocket, Car, Bus, Submarine
709/// }
710/// ```
711///
712/// _**Also you write mixed enum combining all above types.**_
713/// ```rust
714/// # use fastapi::ToSchema;
715/// #[derive(ToSchema)]
716/// enum ErrorResponse {
717/// InvalidCredentials,
718/// #[schema(default = String::default, example = "Pet not found")]
719/// NotFound(String),
720/// System {
721/// #[schema(example = "Unknown system failure")]
722/// details: String,
723/// }
724/// }
725/// ```
726///
727/// _**It is possible to specify the title of each variant to help generators create named structures.**_
728/// ```rust
729/// # use fastapi::ToSchema;
730/// #[derive(ToSchema)]
731/// enum ErrorResponse {
732/// #[schema(title = "InvalidCredentials")]
733/// InvalidCredentials,
734/// #[schema(title = "NotFound")]
735/// NotFound(String),
736/// }
737/// ```
738///
739/// _**Use `xml` attribute to manipulate xml output.**_
740/// ```rust
741/// # use fastapi::ToSchema;
742/// #[derive(ToSchema)]
743/// #[schema(xml(name = "user", prefix = "u", namespace = "https://user.xml.schema.test"))]
744/// struct User {
745/// #[schema(xml(attribute, prefix = "u"))]
746/// id: i64,
747/// #[schema(xml(name = "user_name", prefix = "u"))]
748/// username: String,
749/// #[schema(xml(wrapped(name = "linkList"), name = "link"))]
750/// links: Vec<String>,
751/// #[schema(xml(wrapped, name = "photo_url"))]
752/// photos_urls: Vec<String>
753/// }
754/// ```
755///
756/// _**Use of Rust's own `#[deprecated]` attribute will reflect to generated OpenAPI spec.**_
757/// ```rust
758/// # use fastapi::ToSchema;
759/// #[derive(ToSchema)]
760/// #[deprecated]
761/// struct User {
762/// id: i64,
763/// username: String,
764/// links: Vec<String>,
765/// #[deprecated]
766/// photos_urls: Vec<String>
767/// }
768/// ```
769///
770/// _**Enforce type being used in OpenAPI spec to [`String`] with `value_type` and set format to octet stream
771/// with [`SchemaFormat::KnownFormat(KnownFormat::Binary)`][binary].**_
772/// ```rust
773/// # use fastapi::ToSchema;
774/// #[derive(ToSchema)]
775/// struct Post {
776/// id: i32,
777/// #[schema(value_type = String, format = Binary)]
778/// value: Vec<u8>,
779/// }
780/// ```
781///
782/// _**Enforce type being used in OpenAPI spec to [`String`] with `value_type` option.**_
783/// ```rust
784/// # use fastapi::ToSchema;
785/// #[derive(ToSchema)]
786/// #[schema(value_type = String)]
787/// struct Value(i64);
788/// ```
789///
790/// _**Override the `Bar` reference with a `custom::NewBar` reference.**_
791/// ```rust
792/// # use fastapi::ToSchema;
793/// # mod custom {
794/// # #[derive(fastapi::ToSchema)]
795/// # pub struct NewBar;
796/// # }
797/// #
798/// # struct Bar;
799/// #[derive(ToSchema)]
800/// struct Value {
801/// #[schema(value_type = custom::NewBar)]
802/// field: Bar,
803/// };
804/// ```
805///
806/// _**Use a virtual `Object` type to render generic `object` _(`type: object`)_ in OpenAPI spec.**_
807/// ```rust
808/// # use fastapi::ToSchema;
809/// # mod custom {
810/// # struct NewBar;
811/// # }
812/// #
813/// # struct Bar;
814/// #[derive(ToSchema)]
815/// struct Value {
816/// #[schema(value_type = Object)]
817/// field: Bar,
818/// };
819/// ```
820/// More examples for _`value_type`_ in [`IntoParams` derive docs][into_params].
821///
822/// _**Serde `rename` / `rename_all` will take precedence over schema `rename` / `rename_all`.**_
823/// ```rust
824/// #[derive(fastapi::ToSchema, serde::Deserialize)]
825/// #[serde(rename_all = "lowercase")]
826/// #[schema(rename_all = "UPPERCASE")]
827/// enum Random {
828/// #[serde(rename = "string_value")]
829/// #[schema(rename = "custom_value")]
830/// String(String),
831///
832/// Number {
833/// id: i32,
834/// }
835/// }
836/// ```
837///
838/// _**Add `title` to the enum.**_
839/// ```rust
840/// #[derive(fastapi::ToSchema)]
841/// #[schema(title = "UserType")]
842/// enum UserType {
843/// Admin,
844/// Moderator,
845/// User,
846/// }
847/// ```
848///
849/// _**Example with validation attributes.**_
850/// ```rust
851/// #[derive(fastapi::ToSchema)]
852/// struct Item {
853/// #[schema(maximum = 10, minimum = 5, multiple_of = 2.5)]
854/// id: i32,
855/// #[schema(max_length = 10, min_length = 5, pattern = "[a-z]*")]
856/// value: String,
857/// #[schema(max_items = 5, min_items = 1)]
858/// items: Vec<String>,
859/// }
860/// ````
861///
862/// _**Use `schema_with` to manually implement schema for a field.**_
863/// ```rust
864/// # use fastapi::openapi::schema::{Object, ObjectBuilder};
865/// fn custom_type() -> Object {
866/// ObjectBuilder::new()
867/// .schema_type(fastapi::openapi::schema::Type::String)
868/// .format(Some(fastapi::openapi::SchemaFormat::Custom(
869/// "email".to_string(),
870/// )))
871/// .description(Some("this is the description"))
872/// .build()
873/// }
874///
875/// #[derive(fastapi::ToSchema)]
876/// struct Value {
877/// #[schema(schema_with = custom_type)]
878/// id: String,
879/// }
880/// ```
881///
882/// _**Use `as` attribute to change the name and the path of the schema in the generated OpenAPI
883/// spec.**_
884/// ```rust
885/// #[derive(fastapi::ToSchema)]
886/// #[schema(as = api::models::person::Person)]
887/// struct Person {
888/// name: String,
889/// }
890/// ```
891///
892/// _**Use `bound` attribute to override the default impl bounds.**_
893///
894/// `bound = ...` accepts a string containing zero or more where-predicates separated by comma, as
895/// the similar syntax to [`serde(bound = ...)`](https://serde.rs/container-attrs.html#bound).
896/// If `bound = ...` exists, the default auto bounds (requiring all generic types to implement
897/// `ToSchema`) will not be applied anymore, and only the specified predicates are added to the
898/// `where` clause of generated `impl` blocks.
899///
900/// ```rust
901/// // Override the default bounds to only require `T: ToSchema`, ignoring unused `U`.
902/// #[derive(fastapi::ToSchema, serde::Serialize)]
903/// #[schema(bound = "T: fastapi::ToSchema")]
904/// struct Partial<T, U> {
905/// used_in_api: T,
906/// #[serde(skip)]
907/// not_in_api: std::marker::PhantomData<U>,
908/// }
909///
910/// // Just remove the auto-bounds. So we got `Unused<T>: ToSchema` for any `T`.
911/// #[derive(fastapi::ToSchema, serde::Serialize)]
912/// #[schema(bound = "")]
913/// struct Unused<T> {
914/// #[serde(skip)]
915/// _marker: std::marker::PhantomData<T>,
916/// }
917/// ```
918///
919/// _**Use `no_recursion` attribute to break from looping schema tree e.g. `Pet` -> `Owner` ->
920/// `Pet`.**_
921///
922/// `no_recursion` attribute can be provided on named field of a struct, on unnamed struct or unnamed
923/// enum variant. It must be provided in case of looping schema tree in order to stop recursion.
924/// Failing to do so will cause runtime **panic**.
925/// ```rust
926/// # use fastapi::ToSchema;
927/// #
928/// #[derive(ToSchema)]
929/// pub struct Pet {
930/// name: String,
931/// owner: Owner,
932/// }
933///
934/// #[derive(ToSchema)]
935/// pub struct Owner {
936/// name: String,
937/// #[schema(no_recursion)]
938/// pets: Vec<Pet>,
939/// }
940/// ```
941///
942/// [to_schema]: trait.ToSchema.html
943/// [known_format]: openapi/schema/enum.KnownFormat.html
944/// [binary]: openapi/schema/enum.KnownFormat.html#variant.Binary
945/// [xml]: openapi/xml/struct.Xml.html
946/// [into_params]: derive.IntoParams.html
947/// [primitive]: https://doc.rust-lang.org/std/primitive/index.html
948/// [serde attributes]: https://serde.rs/attributes.html
949/// [discriminator]: openapi/schema/struct.Discriminator.html
950/// [enum_schema]: derive.ToSchema.html#enum-optional-configuration-options-for-schema
951/// [openapi_derive]: derive.OpenApi.html
952/// [to_schema_xml]: macro@ToSchema#xml-attribute-configuration-options
953/// [schema_object_encoding]: openapi/schema/struct.Object.html#structfield.content_encoding
954/// [schema_object_media_type]: openapi/schema/struct.Object.html#structfield.content_media_type
955/// [path_macro]: macro@path
956/// [const]: https://doc.rust-lang.org/std/keyword.const.html
957pub fn derive_to_schema(input: TokenStream) -> TokenStream {
958 let DeriveInput {
959 attrs,
960 ident,
961 data,
962 generics,
963 ..
964 } = syn::parse_macro_input!(input);
965
966 Schema::new(&data, &attrs, &ident, &generics)
967 .as_ref()
968 .map_or_else(Diagnostics::to_token_stream, Schema::to_token_stream)
969 .into()
970}
971
972#[proc_macro_attribute]
973/// Path attribute macro implements OpenAPI path for the decorated function.
974///
975/// This is a `#[derive]` implementation for [`Path`][path] trait. Macro accepts set of attributes that can
976/// be used to configure and override default values what are resolved automatically.
977///
978/// You can use the Rust's own `#[deprecated]` attribute on functions to mark it as deprecated and it will
979/// reflect to the generated OpenAPI spec. Only **parameters** has a special **deprecated** attribute to define them as deprecated.
980///
981/// `#[deprecated]` attribute supports adding additional details such as a reason and or since version but this is is not supported in
982/// OpenAPI. OpenAPI has only a boolean flag to determine deprecation. While it is totally okay to declare deprecated with reason
983/// `#[deprecated = "There is better way to do this"]` the reason would not render in OpenAPI spec.
984///
985/// Doc comment at decorated function will be used for _`description`_ and _`summary`_ of the path.
986/// First line of the doc comment will be used as the _`summary`_ while the remaining lines will be
987/// used as _`description`_.
988/// ```rust
989/// /// This is a summary of the operation
990/// ///
991/// /// The rest of the doc comment will be included to operation description.
992/// #[fastapi::path(get, path = "/operation")]
993/// fn operation() {}
994/// ```
995///
996/// # Path Attributes
997///
998/// * `operation` _**Must be first parameter!**_ Accepted values are known HTTP operations such as
999/// _`get, post, put, delete, head, options, patch, trace`_.
1000///
1001/// * `method(get, head, ...)` Http methods for the operation. This allows defining multiple
1002/// HTTP methods at once for single operation. Either _`operation`_ or _`method(...)`_ _**must be
1003/// provided.**_
1004///
1005/// * `path = "..."` Must be OpenAPI format compatible str with arguments within curly braces. E.g _`{id}`_
1006///
1007/// * `impl_for = ...` Optional type to implement the [`Path`][path] trait. By default a new type
1008/// is used for the implementation.
1009///
1010/// * `operation_id = ...` Unique operation id for the endpoint. By default this is mapped to function name.
1011/// The operation_id can be any valid expression (e.g. string literals, macro invocations, variables) so long
1012/// as its result can be converted to a `String` using `String::from`.
1013///
1014/// * `context_path = "..."` Can add optional scope for **path**. The **context_path** will be prepended to beginning of **path**.
1015/// This is particularly useful when **path** does not contain the full path to the endpoint. For example if web framework
1016/// allows operation to be defined under some context path or scope which does not reflect to the resolved path then this
1017/// **context_path** can become handy to alter the path.
1018///
1019/// * `tag = "..."` Can be used to group operations. Operations with same tag are grouped together. By default
1020/// this is derived from the module path of the handler that is given to [`OpenApi`][openapi].
1021///
1022/// * `tags = ["tag1", ...]` Can be used to group operations. Operations with same tag are grouped
1023/// together. Tags attribute can be used to add additional _tags_ for the operation. If both
1024/// _`tag`_ and _`tags`_ are provided then they will be combined to a single _`tags`_ array.
1025///
1026/// * `request_body = ... | request_body(...)` Defining request body indicates that the request is expecting request body within
1027/// the performed request.
1028///
1029/// * `responses(...)` Slice of responses the endpoint is going to possibly return to the caller.
1030///
1031/// * `params(...)` Slice of params that the endpoint accepts.
1032///
1033/// * `security(...)` List of [`SecurityRequirement`][security]s local to the path operation.
1034///
1035/// # Request Body Attributes
1036///
1037/// ## Simple format definition by `request_body = ...`
1038/// * _`request_body = Type`_, _`request_body = inline(Type)`_ or _`request_body = ref("...")`_.
1039/// The given _`Type`_ can be any Rust type that is JSON parseable. It can be Option, Vec or Map etc.
1040/// With _`inline(...)`_ the schema will be inlined instead of a referenced which is the default for
1041/// [`ToSchema`][to_schema] types. _`ref("./external.json")`_ can be used to reference external
1042/// json file for body schema. **Note!** Fastapi does **not** guarantee that free form _`ref`_ is accessible via
1043/// OpenAPI doc or Swagger UI, users are responsible for making these guarantees.
1044///
1045/// ## Advanced format definition by `request_body(...)`
1046///
1047/// With advanced format the request body supports defining either one or multiple request bodies by `content` attribute.
1048///
1049/// ### Common request body attributes
1050///
1051/// * `description = "..."` Define the description for the request body object as str.
1052///
1053/// * `example = ...` Can be _`json!(...)`_. _`json!(...)`_ should be something that
1054/// _`serde_json::json!`_ can parse as a _`serde_json::Value`_.
1055///
1056/// * `examples(...)` Define multiple examples for single request body. This attribute is mutually
1057/// exclusive to the _`example`_ attribute and if both are defined this will override the _`example`_.
1058/// This has same syntax as _`examples(...)`_ in [Response Attributes](#response-attributes)
1059/// _examples(...)_
1060///
1061/// ### Single request body content
1062///
1063/// * `content = ...` Can be _`content = Type`_, _`content = inline(Type)`_ or _`content = ref("...")`_. The
1064/// given _`Type`_ can be any Rust type that is JSON parseable. It can be Option, Vec
1065/// or Map etc. With _`inline(...)`_ the schema will be inlined instead of a referenced
1066/// which is the default for [`ToSchema`][to_schema] types. _`ref("./external.json")`_
1067/// can be used to reference external json file for body schema. **Note!** Fastapi does **not** guarantee
1068/// that free form _`ref`_ is accessible via OpenAPI doc or Swagger UI, users are responsible for making
1069/// these guarantees.
1070///
1071/// * `content_type = "..."` Can be used to override the default behavior
1072/// of auto resolving the content type from the `content` attribute. If defined the value should be valid
1073/// content type such as _`application/json`_ . By default the content type is _`text/plain`_
1074/// for [primitive Rust types][primitive], `application/octet-stream` for _`[u8]`_ and _`application/json`_
1075/// for struct and mixed enum types.
1076///
1077/// _**Example of single request body definitions.**_
1078/// ```text
1079/// request_body(content = String, description = "Xml as string request", content_type = "text/xml"),
1080/// request_body(content_type = "application/json"),
1081/// request_body = Pet,
1082/// request_body = Option<[Pet]>,
1083/// ```
1084///
1085/// ### Multiple request body content
1086///
1087/// * `content(...)` Can be tuple of content tuples according to format below.
1088/// ```text
1089/// ( schema )
1090/// ( schema = "content/type", example = ..., examples(..., ...) )
1091/// ( "content/type", ),
1092/// ( "content/type", example = ..., examples(..., ...) )
1093/// ```
1094///
1095/// First argument of content tuple is _`schema`_, which is optional as long as either _`schema`_
1096/// or _`content/type`_ is defined. The _`schema`_ and _`content/type`_ is separated with equals
1097/// (=) sign. Optionally content tuple supports defining _`example`_ and _`examples`_ arguments. See
1098/// [common request body attributes][macro@path#common-request-body-attributes]
1099///
1100/// _**Example of multiple request body definitions.**_
1101///
1102/// ```text
1103/// // guess the content type for Pet and Pet2
1104/// request_body(description = "Common description",
1105/// content(
1106/// (Pet),
1107/// (Pet2)
1108/// )
1109/// ),
1110/// // define explicit content types
1111/// request_body(description = "Common description",
1112/// content(
1113/// (Pet = "application/json", examples(..., ...), example = ...),
1114/// (Pet2 = "text/xml", examples(..., ...), example = ...)
1115/// )
1116/// ),
1117/// // omit schema and accept arbitrary content types
1118/// request_body(description = "Common description",
1119/// content(
1120/// ("application/json"),
1121/// ("text/xml", examples(..., ...), example = ...)
1122/// )
1123/// ),
1124/// ```
1125///
1126/// # Response Attributes
1127///
1128/// * `status = ...` Is either a valid http status code integer. E.g. _`200`_ or a string value representing
1129/// a range such as _`"4XX"`_ or `"default"` or a valid _`http::status::StatusCode`_.
1130/// _`StatusCode`_ can either be use path to the status code or _status code_ constant directly.
1131///
1132/// * `description = "..."` Define description for the response as str.
1133///
1134/// * `body = ...` Optional response body object type. When left empty response does not expect to send any
1135/// response body. Can be _`body = Type`_, _`body = inline(Type)`_, or _`body = ref("...")`_.
1136/// The given _`Type`_ can be any Rust type that is JSON parseable. It can be Option, Vec or Map etc.
1137/// With _`inline(...)`_ the schema will be inlined instead of a referenced which is the default for
1138/// [`ToSchema`][to_schema] types. _`ref("./external.json")`_
1139/// can be used to reference external json file for body schema. **Note!** Fastapi does **not** guarantee
1140/// that free form _`ref`_ is accessible via OpenAPI doc or Swagger UI, users are responsible for making
1141/// these guarantees.
1142///
1143/// * `content_type = "..."` Can be used to override the default behavior
1144/// of auto resolving the content type from the `body` attribute. If defined the value should be valid
1145/// content type such as _`application/json`_ . By default the content type is _`text/plain`_
1146/// for [primitive Rust types][primitive], `application/octet-stream` for _`[u8]`_ and _`application/json`_
1147/// for struct and mixed enum types.
1148///
1149/// * `headers(...)` Slice of response headers that are returned back to a caller.
1150///
1151/// * `example = ...` Can be _`json!(...)`_. _`json!(...)`_ should be something that
1152/// _`serde_json::json!`_ can parse as a _`serde_json::Value`_.
1153///
1154/// * `response = ...` Type what implements [`ToResponse`][to_response_trait] trait. This can alternatively be used to
1155/// define response attributes. _`response`_ attribute cannot co-exist with other than _`status`_ attribute.
1156///
1157/// * `content((...), (...))` Can be used to define multiple return types for single response status. Supports same syntax as
1158/// [multiple request body content][`macro@path#multiple-request-body-content`].
1159///
1160/// * `examples(...)` Define multiple examples for single response. This attribute is mutually
1161/// exclusive to the _`example`_ attribute and if both are defined this will override the _`example`_.
1162///
1163/// * `links(...)` Define a map of operations links that can be followed from the response.
1164///
1165/// ## Response `examples(...)` syntax
1166///
1167/// * `name = ...` This is first attribute and value must be literal string.
1168/// * `summary = ...` Short description of example. Value must be literal string.
1169/// * `description = ...` Long description of example. Attribute supports markdown for rich text
1170/// representation. Value must be literal string.
1171/// * `value = ...` Example value. It must be _`json!(...)`_. _`json!(...)`_ should be something that
1172/// _`serde_json::json!`_ can parse as a _`serde_json::Value`_.
1173/// * `external_value = ...` Define URI to literal example value. This is mutually exclusive to
1174/// the _`value`_ attribute. Value must be literal string.
1175///
1176/// _**Example of example definition.**_
1177/// ```text
1178/// ("John" = (summary = "This is John", value = json!({"name": "John"})))
1179/// ```
1180///
1181/// ## Response `links(...)` syntax
1182///
1183/// * `operation_ref = ...` Define a relative or absolute URI reference to an OAS operation. This field is
1184/// mutually exclusive of the _`operation_id`_ field, and **must** point to an [Operation Object][operation].
1185/// Value can be be [`str`] or an expression such as [`include_str!`][include_str] or static
1186/// [`const`][const] reference.
1187///
1188/// * `operation_id = ...` Define the name of an existing, resolvable OAS operation, as defined with a unique
1189/// _`operation_id`_. This field is mutually exclusive of the _`operation_ref`_ field.
1190/// Value can be be [`str`] or an expression such as [`include_str!`][include_str] or static
1191/// [`const`][const] reference.
1192///
1193/// * `parameters(...)` A map representing parameters to pass to an operation as specified with _`operation_id`_
1194/// or identified by _`operation_ref`_. The key is parameter name to be used and value can
1195/// be any value supported by JSON or an [expression][expression] e.g. `$path.id`
1196/// * `name = ...` Define name for the parameter.
1197/// Value can be be [`str`] or an expression such as [`include_str!`][include_str] or static
1198/// [`const`][const] reference.
1199/// * `value` = Any value that can be supported by JSON or an [expression][expression].
1200///
1201/// _**Example of parameters syntax:**_
1202/// ```text
1203/// parameters(
1204/// ("name" = value),
1205/// ("name" = value)
1206/// ),
1207/// ```
1208///
1209/// * `request_body = ...` Define a literal value or an [expression][expression] to be used as request body when
1210/// operation is called
1211///
1212/// * `description = ...` Define description of the link. Value supports Markdown syntax.Value can be be [`str`] or
1213/// an expression such as [`include_str!`][include_str] or static [`const`][const] reference.
1214///
1215/// * `server(...)` Define [Server][server] object to be used by the target operation. See
1216/// [server syntax][server_derive_syntax]
1217///
1218/// **Links syntax example:** See the full example below in [examples](#examples).
1219/// ```text
1220/// responses(
1221/// (status = 200, description = "success response",
1222/// links(
1223/// ("link_name" = (
1224/// operation_id = "test_links",
1225/// parameters(("key" = "value"), ("json_value" = json!(1))),
1226/// request_body = "this is body",
1227/// server(url = "http://localhost")
1228/// ))
1229/// )
1230/// )
1231/// )
1232/// ```
1233///
1234/// **Minimal response format:**
1235/// ```text
1236/// responses(
1237/// (status = 200, description = "success response"),
1238/// (status = 404, description = "resource missing"),
1239/// (status = "5XX", description = "server error"),
1240/// (status = StatusCode::INTERNAL_SERVER_ERROR, description = "internal server error"),
1241/// (status = IM_A_TEAPOT, description = "happy easter")
1242/// )
1243/// ```
1244///
1245/// **More complete Response:**
1246/// ```text
1247/// responses(
1248/// (status = 200, description = "Success response", body = Pet, content_type = "application/json",
1249/// headers(...),
1250/// example = json!({"id": 1, "name": "bob the cat"})
1251/// )
1252/// )
1253/// ```
1254///
1255/// **Multiple response return types with _`content(...)`_ attribute:**
1256///
1257/// _**Define multiple response return types for single response status with their own example.**_
1258/// ```text
1259/// responses(
1260/// (status = 200, content(
1261/// (User = "application/vnd.user.v1+json", example = json!(User {id: "id".to_string()})),
1262/// (User2 = "application/vnd.user.v2+json", example = json!(User2 {id: 2}))
1263/// )
1264/// )
1265/// )
1266/// ```
1267///
1268/// ### Using `ToResponse` for reusable responses
1269///
1270/// _**`ReusableResponse` must be a type that implements [`ToResponse`][to_response_trait].**_
1271/// ```text
1272/// responses(
1273/// (status = 200, response = ReusableResponse)
1274/// )
1275/// ```
1276///
1277/// _**[`ToResponse`][to_response_trait] can also be inlined to the responses map.**_
1278/// ```text
1279/// responses(
1280/// (status = 200, response = inline(ReusableResponse))
1281/// )
1282/// ```
1283///
1284/// ## Responses from `IntoResponses`
1285///
1286/// _**Responses for a path can be specified with one or more types that implement
1287/// [`IntoResponses`][into_responses_trait].**_
1288/// ```text
1289/// responses(MyResponse)
1290/// ```
1291///
1292/// # Response Header Attributes
1293///
1294/// * `name` Name of the header. E.g. _`x-csrf-token`_
1295///
1296/// * `type` Additional type of the header value. Can be `Type` or `inline(Type)`.
1297/// The given _`Type`_ can be any Rust type that is JSON parseable. It can be Option, Vec or Map etc.
1298/// With _`inline(...)`_ the schema will be inlined instead of a referenced which is the default for
1299/// [`ToSchema`][to_schema] types. **Reminder!** It's up to the user to use valid type for the
1300/// response header.
1301///
1302/// * `description = "..."` Can be used to define optional description for the response header as str.
1303///
1304/// **Header supported formats:**
1305///
1306/// ```text
1307/// ("x-csrf-token"),
1308/// ("x-csrf-token" = String, description = "New csrf token"),
1309/// ```
1310///
1311/// # Params Attributes
1312///
1313/// The list of attributes inside the `params(...)` attribute can take two forms: [Tuples](#tuples) or [IntoParams
1314/// Type](#intoparams-type).
1315///
1316/// ## Tuples
1317///
1318/// In the tuples format, parameters are specified using the following attributes inside a list of
1319/// tuples separated by commas:
1320///
1321/// * `name` _**Must be the first argument**_. Define the name for parameter.
1322///
1323/// * `parameter_type` Define possible type for the parameter. Can be `Type` or `inline(Type)`.
1324/// The given _`Type`_ can be any Rust type that is JSON parseable. It can be Option, Vec or Map etc.
1325/// With _`inline(...)`_ the schema will be inlined instead of a referenced which is the default for
1326/// [`ToSchema`][to_schema] types. Parameter type is placed after `name` with
1327/// equals sign E.g. _`"id" = string`_
1328///
1329/// * `in` _**Must be placed after name or parameter_type**_. Define the place of the parameter.
1330/// This must be one of the variants of [`openapi::path::ParameterIn`][in_enum].
1331/// E.g. _`Path, Query, Header, Cookie`_
1332///
1333/// * `deprecated` Define whether the parameter is deprecated or not. Can optionally be defined
1334/// with explicit `bool` value as _`deprecated = bool`_.
1335///
1336/// * `description = "..."` Define possible description for the parameter as str.
1337///
1338/// * `style = ...` Defines how parameters are serialized by [`ParameterStyle`][style]. Default values are based on _`in`_ attribute.
1339///
1340/// * `explode` Defines whether new _`parameter=value`_ is created for each parameter within _`object`_ or _`array`_.
1341///
1342/// * `allow_reserved` Defines whether reserved characters _`:/?#[]@!$&'()*+,;=`_ is allowed within value.
1343///
1344/// * `example = ...` Can method reference or _`json!(...)`_. Given example
1345/// will override any example in underlying parameter type.
1346///
1347/// ##### Parameter type attributes
1348///
1349/// These attributes supported when _`parameter_type`_ is present. Either by manually providing one
1350/// or otherwise resolved e.g from path macro argument when _`actix_extras`_ crate feature is
1351/// enabled.
1352///
1353/// * `format = ...` May either be variant of the [`KnownFormat`][known_format] enum, or otherwise
1354/// an open value as a string. By default the format is derived from the type of the property
1355/// according OpenApi spec.
1356///
1357/// * `write_only` Defines property is only used in **write** operations *POST,PUT,PATCH* but not in *GET*
1358///
1359/// * `read_only` Defines property is only used in **read** operations *GET* but not in *POST,PUT,PATCH*
1360///
1361/// * `xml(...)` Can be used to define [`Xml`][xml] object properties for the parameter type.
1362/// See configuration options at xml attributes of [`ToSchema`][to_schema_xml]
1363///
1364/// * `nullable` Defines property is nullable (note this is different to non-required).
1365///
1366/// * `multiple_of = ...` Can be used to define multiplier for a value. Value is considered valid
1367/// division will result an `integer`. Value must be strictly above _`0`_.
1368///
1369/// * `maximum = ...` Can be used to define inclusive upper bound to a `number` value.
1370///
1371/// * `minimum = ...` Can be used to define inclusive lower bound to a `number` value.
1372///
1373/// * `exclusive_maximum = ...` Can be used to define exclusive upper bound to a `number` value.
1374///
1375/// * `exclusive_minimum = ...` Can be used to define exclusive lower bound to a `number` value.
1376///
1377/// * `max_length = ...` Can be used to define maximum length for `string` types.
1378///
1379/// * `min_length = ...` Can be used to define minimum length for `string` types.
1380///
1381/// * `pattern = ...` Can be used to define valid regular expression in _ECMA-262_ dialect the field value must match.
1382///
1383/// * `max_items = ...` Can be used to define maximum items allowed for `array` fields. Value must
1384/// be non-negative integer.
1385///
1386/// * `min_items = ...` Can be used to define minimum items allowed for `array` fields. Value must
1387/// be non-negative integer.
1388///
1389/// ##### Parameter Formats
1390/// ```test
1391/// ("name" = ParameterType, ParameterIn, ...)
1392/// ("name", ParameterIn, ...)
1393/// ```
1394///
1395/// **For example:**
1396///
1397/// ```text
1398/// params(
1399/// ("limit" = i32, Query),
1400/// ("x-custom-header" = String, Header, description = "Custom header"),
1401/// ("id" = String, Path, deprecated, description = "Pet database id"),
1402/// ("name", Path, deprecated, description = "Pet name"),
1403/// (
1404/// "value" = inline(Option<[String]>),
1405/// Query,
1406/// description = "Value description",
1407/// style = Form,
1408/// allow_reserved,
1409/// deprecated,
1410/// explode,
1411/// example = json!(["Value"])),
1412/// max_length = 10,
1413/// min_items = 1
1414/// )
1415/// )
1416/// ```
1417///
1418/// ## IntoParams Type
1419///
1420/// In the IntoParams parameters format, the parameters are specified using an identifier for a type
1421/// that implements [`IntoParams`][into_params]. See [`IntoParams`][into_params] for an
1422/// example.
1423///
1424/// ```text
1425/// params(MyParameters)
1426/// ```
1427///
1428/// **Note!** that `MyParameters` can also be used in combination with the [tuples
1429/// representation](#tuples) or other structs.
1430/// ```text
1431/// params(
1432/// MyParameters1,
1433/// MyParameters2,
1434/// ("id" = String, Path, deprecated, description = "Pet database id"),
1435/// )
1436/// ```
1437///
1438/// # Security Requirement Attributes
1439///
1440/// * `name` Define the name for security requirement. This must match to name of existing
1441/// [`SecurityScheme`][security_scheme].
1442/// * `scopes = [...]` Define the list of scopes needed. These must be scopes defined already in
1443/// existing [`SecurityScheme`][security_scheme].
1444///
1445/// **Security Requirement supported formats:**
1446///
1447/// ```text
1448/// (),
1449/// ("name" = []),
1450/// ("name" = ["scope1", "scope2"]),
1451/// ("name" = ["scope1", "scope2"], "name2" = []),
1452/// ```
1453///
1454/// Leaving empty _`()`_ creates an empty [`SecurityRequirement`][security] this is useful when
1455/// security requirement is optional for operation.
1456///
1457/// You can define multiple security requirements within same parenthesis separated by comma. This
1458/// allows you to define keys that must be simultaneously provided for the endpoint / API.
1459///
1460/// _**Following could be explained as: Security is optional and if provided it must either contain
1461/// `api_key` or `key AND key2`.**_
1462/// ```text
1463/// (),
1464/// ("api_key" = []),
1465/// ("key" = [], "key2" = []),
1466/// ```
1467///
1468/// # actix_extras feature support for actix-web
1469///
1470/// **actix_extras** feature gives **fastapi** ability to parse path operation information from **actix-web** types and macros.
1471///
1472/// 1. Ability to parse `path` from **actix-web** path attribute macros e.g. _`#[get(...)]`_ or
1473/// `#[route(...)]`.
1474/// 2. Ability to parse [`std::primitive`] or [`String`] or [`tuple`] typed `path` parameters from **actix-web** _`web::Path<...>`_.
1475/// 3. Ability to parse `path` and `query` parameters form **actix-web** _`web::Path<...>`_, _`web::Query<...>`_ types
1476/// with [`IntoParams`][into_params] trait.
1477///
1478/// See the **actix_extras** in action in examples [todo-actix](https://github.com/nxpkg/fastapi/tree/master/examples/todo-actix).
1479///
1480/// With **actix_extras** feature enabled the you can leave out definitions for **path**, **operation**
1481/// and **parameter types**.
1482/// ```rust
1483/// use actix_web::{get, web, HttpResponse, Responder};
1484/// use serde_json::json;
1485///
1486/// /// Get Pet by id
1487/// #[fastapi::path(
1488/// responses(
1489/// (status = 200, description = "Pet found from database")
1490/// ),
1491/// params(
1492/// ("id", description = "Pet id"),
1493/// )
1494/// )]
1495/// #[get("/pet/{id}")]
1496/// async fn get_pet_by_id(id: web::Path<i32>) -> impl Responder {
1497/// HttpResponse::Ok().json(json!({ "pet": format!("{:?}", &id.into_inner()) }))
1498/// }
1499/// ```
1500///
1501/// With **actix_extras** you may also not to list any _**params**_ if you do not want to specify any description for them. Params are
1502/// resolved from path and the argument types of handler
1503/// ```rust
1504/// use actix_web::{get, web, HttpResponse, Responder};
1505/// use serde_json::json;
1506///
1507/// /// Get Pet by id
1508/// #[fastapi::path(
1509/// responses(
1510/// (status = 200, description = "Pet found from database")
1511/// )
1512/// )]
1513/// #[get("/pet/{id}")]
1514/// async fn get_pet_by_id(id: web::Path<i32>) -> impl Responder {
1515/// HttpResponse::Ok().json(json!({ "pet": format!("{:?}", &id.into_inner()) }))
1516/// }
1517/// ```
1518///
1519/// # rocket_extras feature support for rocket
1520///
1521/// **rocket_extras** feature enhances path operation parameter support. It gives **fastapi** ability to parse `path`, `path parameters`
1522/// and `query parameters` based on arguments given to **rocket** proc macros such as _**`#[get(...)]`**_.
1523///
1524/// 1. It is able to parse parameter types for [primitive types][primitive], [`String`], [`Vec`], [`Option`] or [`std::path::PathBuf`]
1525/// type.
1526/// 2. It is able to determine `parameter_in` for [`IntoParams`][into_params] trait used for `FromForm` type of query parameters.
1527///
1528/// See the **rocket_extras** in action in examples [rocket-todo](https://github.com/nxpkg/fastapi/tree/master/examples/rocket-todo).
1529///
1530///
1531/// # axum_extras feature support for axum
1532///
1533/// **axum_extras** feature enhances parameter support for path operation in following ways.
1534///
1535/// 1. It allows users to use tuple style path parameters e.g. _`Path((id, name)): Path<(i32, String)>`_ and resolves
1536/// parameter names and types from it.
1537/// 2. It enhances [`IntoParams` derive][into_params_derive] functionality by automatically resolving _`parameter_in`_ from
1538/// _`Path<...>`_ or _`Query<...>`_ handler function arguments.
1539///
1540/// _**Resole path argument types from tuple style handler arguments.**_
1541/// ```rust
1542/// # use axum::extract::Path;
1543/// /// Get todo by id and name.
1544/// #[fastapi::path(
1545/// get,
1546/// path = "/todo/{id}",
1547/// params(
1548/// ("id", description = "Todo id"),
1549/// ("name", description = "Todo name")
1550/// ),
1551/// responses(
1552/// (status = 200, description = "Get todo success", body = String)
1553/// )
1554/// )]
1555/// async fn get_todo(
1556/// Path((id, name)): Path<(i32, String)>
1557/// ) -> String {
1558/// String::new()
1559/// }
1560/// ```
1561///
1562/// _**Use `IntoParams` to resolve query parameters.**_
1563/// ```rust
1564/// # use serde::Deserialize;
1565/// # use fastapi::IntoParams;
1566/// # use axum::{extract::Query, Json};
1567/// #[derive(Deserialize, IntoParams)]
1568/// struct TodoSearchQuery {
1569/// /// Search by value. Search is incase sensitive.
1570/// value: String,
1571/// /// Search by `done` status.
1572/// done: bool,
1573/// }
1574///
1575/// /// Search Todos by query params.
1576/// #[fastapi::path(
1577/// get,
1578/// path = "/todo/search",
1579/// params(
1580/// TodoSearchQuery
1581/// ),
1582/// responses(
1583/// (status = 200, description = "List matching todos by query", body = [String])
1584/// )
1585/// )]
1586/// async fn search_todos(
1587/// query: Query<TodoSearchQuery>,
1588/// ) -> Json<Vec<String>> {
1589/// Json(vec![])
1590/// }
1591/// ```
1592///
1593/// # Defining file uploads
1594///
1595/// File uploads can be defined in accordance to Open API specification [file uploads][file_uploads].
1596///
1597///
1598/// _**Example sending `jpg` and `png` images as `application/octet-stream`.**_
1599/// ```rust
1600/// #[fastapi::path(
1601/// post,
1602/// request_body(
1603/// content(
1604/// ("image/png"),
1605/// ("image/jpg"),
1606/// ),
1607/// ),
1608/// path = "/test_images"
1609/// )]
1610/// async fn test_images(_body: Vec<u8>) {}
1611/// ```
1612///
1613/// _**Example of sending `multipart` form.**_
1614/// ```rust
1615/// #[derive(fastapi::ToSchema)]
1616/// struct MyForm {
1617/// order_id: i32,
1618/// #[schema(content_media_type = "application/octet-stream")]
1619/// file_bytes: Vec<u8>,
1620/// }
1621///
1622/// #[fastapi::path(
1623/// post,
1624/// request_body(content = inline(MyForm), content_type = "multipart/form-data"),
1625/// path = "/test_multipart"
1626/// )]
1627/// async fn test_multipart(_body: MyForm) {}
1628/// ```
1629///
1630/// _**Example of sending arbitrary binary content as `application/octet-stream`.**_
1631/// ```rust
1632/// #[fastapi::path(
1633/// post,
1634/// request_body = Vec<u8>,
1635/// path = "/test-octet-stream",
1636/// responses(
1637/// (status = 200, description = "success response")
1638/// ),
1639/// )]
1640/// async fn test_octet_stream(_body: Vec<u8>) {}
1641/// ```
1642///
1643/// _**Example of sending `png` image as `base64` encoded.**_
1644/// ```rust
1645/// #[derive(fastapi::ToSchema)]
1646/// #[schema(content_encoding = "base64")]
1647/// struct MyPng(String);
1648///
1649/// #[fastapi::path(
1650/// post,
1651/// request_body(content = inline(MyPng), content_type = "image/png"),
1652/// path = "/test_png",
1653/// responses(
1654/// (status = 200, description = "success response")
1655/// ),
1656/// )]
1657/// async fn test_png(_body: MyPng) {}
1658/// ```
1659///
1660/// # Examples
1661///
1662/// _**More complete example.**_
1663/// ```rust
1664/// # #[derive(fastapi::ToSchema)]
1665/// # struct Pet {
1666/// # id: u64,
1667/// # name: String,
1668/// # }
1669/// #
1670/// #[fastapi::path(
1671/// post,
1672/// operation_id = "custom_post_pet",
1673/// path = "/pet",
1674/// tag = "pet_handlers",
1675/// request_body(content = Pet, description = "Pet to store the database", content_type = "application/json"),
1676/// responses(
1677/// (status = 200, description = "Pet stored successfully", body = Pet, content_type = "application/json",
1678/// headers(
1679/// ("x-cache-len" = String, description = "Cache length")
1680/// ),
1681/// example = json!({"id": 1, "name": "bob the cat"})
1682/// ),
1683/// ),
1684/// params(
1685/// ("x-csrf-token" = String, Header, deprecated, description = "Current csrf token of user"),
1686/// ),
1687/// security(
1688/// (),
1689/// ("my_auth" = ["read:items", "edit:items"]),
1690/// ("token_jwt" = [])
1691/// )
1692/// )]
1693/// fn post_pet(pet: Pet) -> Pet {
1694/// Pet {
1695/// id: 4,
1696/// name: "bob the cat".to_string(),
1697/// }
1698/// }
1699/// ```
1700///
1701/// _**More minimal example with the defaults.**_
1702/// ```rust
1703/// # #[derive(fastapi::ToSchema)]
1704/// # struct Pet {
1705/// # id: u64,
1706/// # name: String,
1707/// # }
1708/// #
1709/// #[fastapi::path(
1710/// post,
1711/// path = "/pet",
1712/// request_body = Pet,
1713/// responses(
1714/// (status = 200, description = "Pet stored successfully", body = Pet,
1715/// headers(
1716/// ("x-cache-len", description = "Cache length")
1717/// )
1718/// ),
1719/// ),
1720/// params(
1721/// ("x-csrf-token", Header, description = "Current csrf token of user"),
1722/// )
1723/// )]
1724/// fn post_pet(pet: Pet) -> Pet {
1725/// Pet {
1726/// id: 4,
1727/// name: "bob the cat".to_string(),
1728/// }
1729/// }
1730/// ```
1731///
1732/// _**Use of Rust's own `#[deprecated]` attribute will reflect to the generated OpenAPI spec and mark this operation as deprecated.**_
1733/// ```rust
1734/// # use actix_web::{get, web, HttpResponse, Responder};
1735/// # use serde_json::json;
1736/// #[fastapi::path(
1737/// responses(
1738/// (status = 200, description = "Pet found from database")
1739/// ),
1740/// params(
1741/// ("id", description = "Pet id"),
1742/// )
1743/// )]
1744/// #[get("/pet/{id}")]
1745/// #[deprecated]
1746/// async fn get_pet_by_id(id: web::Path<i32>) -> impl Responder {
1747/// HttpResponse::Ok().json(json!({ "pet": format!("{:?}", &id.into_inner()) }))
1748/// }
1749/// ```
1750///
1751/// _**Define context path for endpoint. The resolved **path** shown in OpenAPI doc will be `/api/pet/{id}`.**_
1752/// ```rust
1753/// # use actix_web::{get, web, HttpResponse, Responder};
1754/// # use serde_json::json;
1755/// #[fastapi::path(
1756/// context_path = "/api",
1757/// responses(
1758/// (status = 200, description = "Pet found from database")
1759/// )
1760/// )]
1761/// #[get("/pet/{id}")]
1762/// async fn get_pet_by_id(id: web::Path<i32>) -> impl Responder {
1763/// HttpResponse::Ok().json(json!({ "pet": format!("{:?}", &id.into_inner()) }))
1764/// }
1765/// ```
1766///
1767/// _**Example with multiple return types**_
1768/// ```rust
1769/// # trait User {}
1770/// # #[derive(fastapi::ToSchema)]
1771/// # struct User1 {
1772/// # id: String
1773/// # }
1774/// # impl User for User1 {}
1775/// # #[derive(fastapi::ToSchema)]
1776/// # struct User2 {
1777/// # id: String
1778/// # }
1779/// # impl User for User2 {}
1780/// #[fastapi::path(
1781/// get,
1782/// path = "/user",
1783/// responses(
1784/// (status = 200, content(
1785/// (User1 = "application/vnd.user.v1+json", example = json!({"id": "id".to_string()})),
1786/// (User2 = "application/vnd.user.v2+json", example = json!({"id": 2}))
1787/// )
1788/// )
1789/// )
1790/// )]
1791/// fn get_user() -> Box<dyn User> {
1792/// Box::new(User1 {id: "id".to_string()})
1793/// }
1794/// ````
1795///
1796/// _**Example with multiple examples on single response.**_
1797/// ```rust
1798/// # #[derive(serde::Serialize, serde::Deserialize, fastapi::ToSchema)]
1799/// # struct User {
1800/// # name: String
1801/// # }
1802/// #[fastapi::path(
1803/// get,
1804/// path = "/user",
1805/// responses(
1806/// (status = 200, body = User,
1807/// examples(
1808/// ("Demo" = (summary = "This is summary", description = "Long description",
1809/// value = json!(User{name: "Demo".to_string()}))),
1810/// ("John" = (summary = "Another user", value = json!({"name": "John"})))
1811/// )
1812/// )
1813/// )
1814/// )]
1815/// fn get_user() -> User {
1816/// User {name: "John".to_string()}
1817/// }
1818/// ```
1819///
1820/// _**Example of using links in response.**_
1821/// ```rust
1822/// # use serde_json::json;
1823/// #[fastapi::path(
1824/// get,
1825/// path = "/test-links",
1826/// responses(
1827/// (status = 200, description = "success response",
1828/// links(
1829/// ("getFoo" = (
1830/// operation_id = "test_links",
1831/// parameters(("key" = "value"), ("json_value" = json!(1))),
1832/// request_body = "this is body",
1833/// server(url = "http://localhost")
1834/// )),
1835/// ("getBar" = (
1836/// operation_ref = "this is ref"
1837/// ))
1838/// )
1839/// )
1840/// ),
1841/// )]
1842/// async fn test_links() -> &'static str {
1843/// ""
1844/// }
1845/// ```
1846///
1847/// [in_enum]: openapi/path/enum.ParameterIn.html
1848/// [path]: trait.Path.html
1849/// [to_schema]: trait.ToSchema.html
1850/// [openapi]: derive.OpenApi.html
1851/// [security]: openapi/security/struct.SecurityRequirement.html
1852/// [security_scheme]: openapi/security/enum.SecurityScheme.html
1853/// [primitive]: https://doc.rust-lang.org/std/primitive/index.html
1854/// [into_params]: trait.IntoParams.html
1855/// [style]: openapi/path/enum.ParameterStyle.html
1856/// [into_responses_trait]: trait.IntoResponses.html
1857/// [into_params_derive]: derive.IntoParams.html
1858/// [to_response_trait]: trait.ToResponse.html
1859/// [known_format]: openapi/schema/enum.KnownFormat.html
1860/// [xml]: openapi/xml/struct.Xml.html
1861/// [to_schema_xml]: macro@ToSchema#xml-attribute-configuration-options
1862/// [relative_references]: https://spec.openapis.org/oas/latest.html#relative-references-in-uris
1863/// [operation]: openapi/path/struct.Operation.html
1864/// [expression]: https://spec.openapis.org/oas/latest.html#runtime-expressions
1865/// [const]: https://doc.rust-lang.org/std/keyword.const.html
1866/// [include_str]: https://doc.rust-lang.org/std/macro.include_str.html
1867/// [server_derive_syntax]: derive.OpenApi.html#servers-attribute-syntax
1868/// [server]: openapi/server/struct.Server.html
1869/// [file_uploads]: <https://spec.openapis.org/oas/v3.1.0.html#considerations-for-file-uploads>
1870pub fn path(attr: TokenStream, item: TokenStream) -> TokenStream {
1871 let path_attribute = syn::parse_macro_input!(attr as PathAttr);
1872
1873 #[cfg(any(
1874 feature = "actix_extras",
1875 feature = "rocket_extras",
1876 feature = "axum_extras",
1877 feature = "auto_into_responses"
1878 ))]
1879 let mut path_attribute = path_attribute;
1880
1881 let ast_fn = match syn::parse::<ItemFn>(item) {
1882 Ok(ast_fn) => ast_fn,
1883 Err(error) => return error.into_compile_error().into_token_stream().into(),
1884 };
1885
1886 #[cfg(feature = "auto_into_responses")]
1887 {
1888 if let Some(responses) = ext::auto_types::parse_fn_operation_responses(&ast_fn) {
1889 path_attribute.responses_from_into_responses(responses);
1890 };
1891 }
1892
1893 let mut resolved_methods = match PathOperations::resolve_operation(&ast_fn) {
1894 Ok(operation) => operation,
1895 Err(diagnostics) => return diagnostics.into_token_stream().into(),
1896 };
1897 let resolved_path = PathOperations::resolve_path(
1898 &resolved_methods
1899 .as_mut()
1900 .map(|operation| mem::take(&mut operation.path).to_string())
1901 .or_else(|| path_attribute.path.as_ref().map(|path| path.to_string())), // cannot use mem take because we need this later
1902 );
1903
1904 #[cfg(any(
1905 feature = "actix_extras",
1906 feature = "rocket_extras",
1907 feature = "axum_extras"
1908 ))]
1909 let mut resolved_path = resolved_path;
1910
1911 #[cfg(any(
1912 feature = "actix_extras",
1913 feature = "rocket_extras",
1914 feature = "axum_extras"
1915 ))]
1916 {
1917 use ext::ArgumentResolver;
1918 use path::parameter::Parameter;
1919 let path_args = resolved_path.as_mut().map(|path| mem::take(&mut path.args));
1920 let body = resolved_methods
1921 .as_mut()
1922 .map(|path| mem::take(&mut path.body))
1923 .unwrap_or_default();
1924
1925 let (arguments, into_params_types, body) =
1926 match PathOperations::resolve_arguments(&ast_fn.sig.inputs, path_args, body) {
1927 Ok(args) => args,
1928 Err(diagnostics) => return diagnostics.into_token_stream().into(),
1929 };
1930
1931 let parameters = arguments
1932 .into_iter()
1933 .flatten()
1934 .map(Parameter::from)
1935 .chain(into_params_types.into_iter().flatten().map(Parameter::from));
1936 path_attribute.update_parameters_ext(parameters);
1937
1938 path_attribute.update_request_body(body);
1939 }
1940
1941 let path = Path::new(path_attribute, &ast_fn.sig.ident)
1942 .ext_methods(resolved_methods.map(|operation| operation.methods))
1943 .path(resolved_path.map(|path| path.path))
1944 .doc_comments(CommentAttributes::from_attributes(&ast_fn.attrs).0)
1945 .deprecated(ast_fn.attrs.has_deprecated());
1946
1947 let handler = path::handler::Handler {
1948 path,
1949 handler_fn: &ast_fn,
1950 };
1951 handler.to_token_stream().into()
1952}
1953
1954#[proc_macro_derive(OpenApi, attributes(openapi))]
1955/// Generate OpenApi base object with defaults from
1956/// project settings.
1957///
1958/// This is `#[derive]` implementation for [`OpenApi`][openapi] trait. The macro accepts one `openapi` argument.
1959///
1960/// # OpenApi `#[openapi(...)]` attributes
1961///
1962/// * `paths(...)` List of method references having attribute [`#[fastapi::path]`][path] macro.
1963/// * `components(schemas(...), responses(...))` Takes available _`component`_ configurations. Currently only
1964/// _`schema`_ and _`response`_ components are supported.
1965/// * `schemas(...)` List of [`ToSchema`][to_schema]s in OpenAPI schema.
1966/// * `responses(...)` List of types that implement [`ToResponse`][to_response_trait].
1967/// * `modifiers(...)` List of items implementing [`Modify`][modify] trait for runtime OpenApi modification.
1968/// See the [trait documentation][modify] for more details.
1969/// * `security(...)` List of [`SecurityRequirement`][security]s global to all operations.
1970/// See more details in [`#[fastapi::path(...)]`][path] [attribute macro security options][path_security].
1971/// * `tags(...)` List of [`Tag`][tags]s which must match the tag _**path operation**_. Tags can be used to
1972/// define extra information for the API to produce richer documentation. See [tags attribute syntax][tags_syntax].
1973/// * `external_docs(...)` Can be used to reference external resource to the OpenAPI doc for extended documentation.
1974/// External docs can be in [`OpenApi`][openapi_struct] or in [`Tag`][tags] level.
1975/// * `servers(...)` Define [`servers`][servers] as derive argument to the _`OpenApi`_. Servers
1976/// are completely optional and thus can be omitted from the declaration. See [servers attribute
1977/// syntax][servers_syntax]
1978/// * `info(...)` Declare [`Info`][info] attribute values used to override the default values
1979/// generated from Cargo environment variables. **Note!** Defined attributes will override the
1980/// whole attribute from generated values of Cargo environment variables. E.g. defining
1981/// `contact(name = ...)` will ultimately override whole contact of info and not just partially
1982/// the name. See [info attribute syntax][info_syntax]
1983/// * `nest(...)` Allows nesting [`OpenApi`][openapi_struct]s to this _`OpenApi`_ instance. Nest
1984/// takes comma separated list of tuples of nested `OpenApi`s. _`OpenApi`_ instance must
1985/// implement [`OpenApi`][openapi] trait. Nesting allows defining one `OpenApi` per defined path.
1986/// If more instances is defined only latest one will be rentained.
1987/// See the _[nest(...) attribute syntax below]( #nest-attribute-syntax )_
1988///
1989///
1990/// OpenApi derive macro will also derive [`Info`][info] for OpenApi specification using Cargo
1991/// environment variables.
1992///
1993/// * env `CARGO_PKG_NAME` map to info `title`
1994/// * env `CARGO_PKG_VERSION` map to info `version`
1995/// * env `CARGO_PKG_DESCRIPTION` map info `description`
1996/// * env `CARGO_PKG_AUTHORS` map to contact `name` and `email` **only first author will be used**
1997/// * env `CARGO_PKG_LICENSE` map to info `license`
1998///
1999/// # `info(...)` attribute syntax
2000///
2001/// * `title = ...` Define title of the API. It can be [`str`] or an
2002/// expression such as [`include_str!`][include_str] or static [`const`][const] reference.
2003/// * `terms_of_service = ...` Define URL to the Terms of Service for the API. It can be [`str`] or an
2004/// expression such as [`include_str!`][include_str] or static [`const`][const] reference. Value
2005/// must be valid URL.
2006/// * `description = ...` Define description of the API. Markdown can be used for rich text
2007/// representation. It can be [`str`] or an expression such as [`include_str!`][include_str] or static
2008/// [`const`][const] reference.
2009/// * `version = ...` Override default version from _`Cargo.toml`_. Value can be [`str`] or an
2010/// expression such as [`include_str!`][include_str] or static [`const`][const] reference.
2011/// * `contact(...)` Used to override the whole contact generated from environment variables.
2012/// * `name = ...` Define identifying name of contact person / organization. It Can be a literal string.
2013/// * `email = ...` Define email address of the contact person / organization. It can be a literal string.
2014/// * `url = ...` Define URL pointing to the contact information. It must be in URL formatted string.
2015/// * `license(...)` Used to override the whole license generated from environment variables.
2016/// * `name = ...` License name of the API. It can be a literal string.
2017/// * `url = ...` Define optional URL of the license. It must be URL formatted string.
2018///
2019/// # `tags(...)` attribute syntax
2020///
2021/// * `name = ...` Must be provided, can be [`str`] or an expression such as [`include_str!`][include_str]
2022/// or static [`const`][const] reference.
2023/// * `description = ...` Optional description for the tag. Can be either or static [`str`]
2024/// or an expression e.g. _`include_str!(...)`_ macro call or reference to static [`const`][const].
2025/// * `external_docs(...)` Optional links to external documents.
2026/// * `url = ...` Mandatory URL for external documentation.
2027/// * `description = ...` Optional description for the _`url`_ link.
2028///
2029/// # `servers(...)` attribute syntax
2030///
2031/// * `url = ...` Define the url for server. It can be literal string.
2032/// * `description = ...` Define description for the server. It can be literal string.
2033/// * `variables(...)` Can be used to define variables for the url.
2034/// * `name = ...` Is the first argument within parentheses. It must be literal string.
2035/// * `default = ...` Defines a default value for the variable if nothing else will be
2036/// provided. If _`enum_values`_ is defined the _`default`_ must be found within the enum
2037/// options. It can be a literal string.
2038/// * `description = ...` Define the description for the variable. It can be a literal string.
2039/// * `enum_values(...)` Define list of possible values for the variable. Values must be
2040/// literal strings.
2041///
2042/// _**Example server variable definition.**_
2043/// ```text
2044/// ("username" = (default = "demo", description = "Default username for API")),
2045/// ("port" = (enum_values("8080", "5000", "4545")))
2046/// ```
2047///
2048/// # `nest(...)` attribute syntax
2049///
2050/// * `path = ...` Define mandatory path for nesting the [`OpenApi`][openapi_struct].
2051/// * `api = ...` Define mandatory path to struct that implements [`OpenApi`][openapi] trait.
2052/// The fully qualified path (_`path::to`_) will become the default _`tag`_ for the nested
2053/// `OpenApi` endpoints if provided.
2054/// * `tags = [...]` Define optional tags what are appended to the existing list of tags.
2055///
2056/// _**Example of nest definition**_
2057/// ```text
2058/// (path = "path/to/nest", api = path::to::NestableApi),
2059/// (path = "path/to/nest", api = path::to::NestableApi, tags = ["nestableapi", ...])
2060/// ```
2061///
2062/// # Examples
2063///
2064/// _**Define OpenApi schema with some paths and components.**_
2065/// ```rust
2066/// # use fastapi::{OpenApi, ToSchema};
2067/// #
2068/// #[derive(ToSchema)]
2069/// struct Pet {
2070/// name: String,
2071/// age: i32,
2072/// }
2073///
2074/// #[derive(ToSchema)]
2075/// enum Status {
2076/// Active, InActive, Locked,
2077/// }
2078///
2079/// #[fastapi::path(get, path = "/pet")]
2080/// fn get_pet() -> Pet {
2081/// Pet {
2082/// name: "bob".to_string(),
2083/// age: 8,
2084/// }
2085/// }
2086///
2087/// #[fastapi::path(get, path = "/status")]
2088/// fn get_status() -> Status {
2089/// Status::Active
2090/// }
2091///
2092/// #[derive(OpenApi)]
2093/// #[openapi(
2094/// paths(get_pet, get_status),
2095/// components(schemas(Pet, Status)),
2096/// security(
2097/// (),
2098/// ("my_auth" = ["read:items", "edit:items"]),
2099/// ("token_jwt" = [])
2100/// ),
2101/// tags(
2102/// (name = "pets::api", description = "All about pets",
2103/// external_docs(url = "http://more.about.pets.api", description = "Find out more"))
2104/// ),
2105/// external_docs(url = "http://more.about.our.apis", description = "More about our APIs")
2106/// )]
2107/// struct ApiDoc;
2108/// ```
2109///
2110/// _**Define servers to OpenApi.**_
2111/// ```rust
2112/// # use fastapi::OpenApi;
2113/// #[derive(OpenApi)]
2114/// #[openapi(
2115/// servers(
2116/// (url = "http://localhost:8989", description = "Local server"),
2117/// (url = "http://api.{username}:{port}", description = "Remote API",
2118/// variables(
2119/// ("username" = (default = "demo", description = "Default username for API")),
2120/// ("port" = (default = "8080", enum_values("8080", "5000", "3030"), description = "Supported ports for API"))
2121/// )
2122/// )
2123/// )
2124/// )]
2125/// struct ApiDoc;
2126/// ```
2127///
2128/// _**Define info attribute values used to override auto generated ones from Cargo environment
2129/// variables.**_
2130/// ```compile_fail
2131/// # use fastapi::OpenApi;
2132/// #[derive(OpenApi)]
2133/// #[openapi(info(
2134/// title = "title override",
2135/// description = include_str!("./path/to/content"), // fail compile cause no such file
2136/// contact(name = "Test")
2137/// ))]
2138/// struct ApiDoc;
2139/// ```
2140///
2141/// _**Create OpenAPI with reusable response.**_
2142/// ```rust
2143/// #[derive(fastapi::ToSchema)]
2144/// struct Person {
2145/// name: String,
2146/// }
2147///
2148/// /// Person list response
2149/// #[derive(fastapi::ToResponse)]
2150/// struct PersonList(Vec<Person>);
2151///
2152/// #[fastapi::path(
2153/// get,
2154/// path = "/person-list",
2155/// responses(
2156/// (status = 200, response = PersonList)
2157/// )
2158/// )]
2159/// fn get_persons() -> Vec<Person> {
2160/// vec![]
2161/// }
2162///
2163/// #[derive(fastapi::OpenApi)]
2164/// #[openapi(
2165/// components(
2166/// schemas(Person),
2167/// responses(PersonList)
2168/// )
2169/// )]
2170/// struct ApiDoc;
2171/// ```
2172///
2173/// _**Nest _`UserApi`_ to the current api doc instance.**_
2174/// ```rust
2175/// # use fastapi::OpenApi;
2176/// #
2177/// #[fastapi::path(get, path = "/api/v1/status")]
2178/// fn test_path_status() {}
2179///
2180/// #[fastapi::path(get, path = "/test")]
2181/// fn user_test_path() {}
2182///
2183/// #[derive(OpenApi)]
2184/// #[openapi(paths(user_test_path))]
2185/// struct UserApi;
2186///
2187/// #[derive(OpenApi)]
2188/// #[openapi(
2189/// paths(
2190/// test_path_status
2191/// ),
2192/// nest(
2193/// (path = "/api/v1/user", api = UserApi),
2194/// )
2195/// )]
2196/// struct ApiDoc;
2197/// ```
2198///
2199/// [openapi]: trait.OpenApi.html
2200/// [openapi_struct]: openapi/struct.OpenApi.html
2201/// [to_schema]: derive.ToSchema.html
2202/// [path]: attr.path.html
2203/// [modify]: trait.Modify.html
2204/// [info]: openapi/info/struct.Info.html
2205/// [security]: openapi/security/struct.SecurityRequirement.html
2206/// [path_security]: attr.path.html#security-requirement-attributes
2207/// [tags]: openapi/tag/struct.Tag.html
2208/// [to_response_trait]: trait.ToResponse.html
2209/// [servers]: openapi/server/index.html
2210/// [const]: https://doc.rust-lang.org/std/keyword.const.html
2211/// [tags_syntax]: #tags-attribute-syntax
2212/// [info_syntax]: #info-attribute-syntax
2213/// [servers_syntax]: #servers-attribute-syntax
2214/// [include_str]: https://doc.rust-lang.org/std/macro.include_str.html
2215pub fn openapi(input: TokenStream) -> TokenStream {
2216 let DeriveInput { attrs, ident, .. } = syn::parse_macro_input!(input);
2217
2218 parse_openapi_attrs(&attrs)
2219 .map(|openapi_attr| OpenApi(openapi_attr, ident).to_token_stream())
2220 .map_or_else(syn::Error::into_compile_error, ToTokens::into_token_stream)
2221 .into()
2222}
2223
2224#[proc_macro_derive(IntoParams, attributes(param, into_params))]
2225/// Generate [path parameters][path_params] from struct's
2226/// fields.
2227///
2228/// This is `#[derive]` implementation for [`IntoParams`][into_params] trait.
2229///
2230/// Typically path parameters need to be defined within [`#[fastapi::path(...params(...))]`][path_params] section
2231/// for the endpoint. But this trait eliminates the need for that when [`struct`][struct]s are used to define parameters.
2232/// Still [`std::primitive`] and [`String`] path parameters or [`tuple`] style path parameters need to be defined
2233/// within `params(...)` section if description or other than default configuration need to be given.
2234///
2235/// You can use the Rust's own `#[deprecated]` attribute on field to mark it as
2236/// deprecated and it will reflect to the generated OpenAPI spec.
2237///
2238/// `#[deprecated]` attribute supports adding additional details such as a reason and or since version
2239/// but this is is not supported in OpenAPI. OpenAPI has only a boolean flag to determine deprecation.
2240/// While it is totally okay to declare deprecated with reason
2241/// `#[deprecated = "There is better way to do this"]` the reason would not render in OpenAPI spec.
2242///
2243/// Doc comment on struct fields will be used as description for the generated parameters.
2244/// ```rust
2245/// #[derive(fastapi::IntoParams)]
2246/// struct Query {
2247/// /// Query todo items by name.
2248/// name: String
2249/// }
2250/// ```
2251///
2252/// # IntoParams Container Attributes for `#[into_params(...)]`
2253///
2254/// The following attributes are available for use in on the container attribute `#[into_params(...)]` for the struct
2255/// deriving `IntoParams`:
2256///
2257/// * `names(...)` Define comma separated list of names for unnamed fields of struct used as a path parameter.
2258/// __Only__ supported on __unnamed structs__.
2259/// * `style = ...` Defines how all parameters are serialized by [`ParameterStyle`][style]. Default
2260/// values are based on _`parameter_in`_ attribute.
2261/// * `parameter_in = ...` = Defines where the parameters of this field are used with a value from
2262/// [`openapi::path::ParameterIn`][in_enum]. There is no default value, if this attribute is not
2263/// supplied, then the value is determined by the `parameter_in_provider` in
2264/// [`IntoParams::into_params()`](trait.IntoParams.html#tymethod.into_params).
2265/// * `rename_all = ...` Can be provided to alternatively to the serde's `rename_all` attribute. Effectively provides same functionality.
2266///
2267/// Use `names` to define name for single unnamed argument.
2268/// ```rust
2269/// # use fastapi::IntoParams;
2270/// #
2271/// #[derive(IntoParams)]
2272/// #[into_params(names("id"))]
2273/// struct Id(u64);
2274/// ```
2275///
2276/// Use `names` to define names for multiple unnamed arguments.
2277/// ```rust
2278/// # use fastapi::IntoParams;
2279/// #
2280/// #[derive(IntoParams)]
2281/// #[into_params(names("id", "name"))]
2282/// struct IdAndName(u64, String);
2283/// ```
2284///
2285/// # IntoParams Field Attributes for `#[param(...)]`
2286///
2287/// The following attributes are available for use in the `#[param(...)]` on struct fields:
2288///
2289/// * `style = ...` Defines how the parameter is serialized by [`ParameterStyle`][style]. Default values are based on _`parameter_in`_ attribute.
2290///
2291/// * `explode` Defines whether new _`parameter=value`_ pair is created for each parameter within _`object`_ or _`array`_.
2292///
2293/// * `allow_reserved` Defines whether reserved characters _`:/?#[]@!$&'()*+,;=`_ is allowed within value.
2294///
2295/// * `example = ...` Can be method reference or _`json!(...)`_. Given example
2296/// will override any example in underlying parameter type.
2297///
2298/// * `value_type = ...` Can be used to override default type derived from type of the field used in OpenAPI spec.
2299/// This is useful in cases where the default type does not correspond to the actual type e.g. when
2300/// any third-party types are used which are not [`ToSchema`][to_schema]s nor [`primitive` types][primitive].
2301/// The value can be any Rust type what normally could be used to serialize to JSON, or either virtual type _`Object`_
2302/// or _`Value`_.
2303/// _`Object`_ will be rendered as generic OpenAPI object _(`type: object`)_.
2304/// _`Value`_ will be rendered as any OpenAPI value (i.e. no `type` restriction).
2305///
2306/// * `inline` If set, the schema for this field's type needs to be a [`ToSchema`][to_schema], and
2307/// the schema definition will be inlined.
2308///
2309/// * `default = ...` Can be method reference or _`json!(...)`_.
2310///
2311/// * `format = ...` May either be variant of the [`KnownFormat`][known_format] enum, or otherwise
2312/// an open value as a string. By default the format is derived from the type of the property
2313/// according OpenApi spec.
2314///
2315/// * `write_only` Defines property is only used in **write** operations *POST,PUT,PATCH* but not in *GET*.
2316///
2317/// * `read_only` Defines property is only used in **read** operations *GET* but not in *POST,PUT,PATCH*.
2318///
2319/// * `xml(...)` Can be used to define [`Xml`][xml] object properties applicable to named fields.
2320/// See configuration options at xml attributes of [`ToSchema`][to_schema_xml]
2321///
2322/// * `nullable` Defines property is nullable (note this is different to non-required).
2323///
2324/// * `required = ...` Can be used to enforce required status for the parameter. [See
2325/// rules][derive@IntoParams#field-nullability-and-required-rules]
2326///
2327/// * `rename = ...` Can be provided to alternatively to the serde's `rename` attribute. Effectively provides same functionality.
2328///
2329/// * `multiple_of = ...` Can be used to define multiplier for a value. Value is considered valid
2330/// division will result an `integer`. Value must be strictly above _`0`_.
2331///
2332/// * `maximum = ...` Can be used to define inclusive upper bound to a `number` value.
2333///
2334/// * `minimum = ...` Can be used to define inclusive lower bound to a `number` value.
2335///
2336/// * `exclusive_maximum = ...` Can be used to define exclusive upper bound to a `number` value.
2337///
2338/// * `exclusive_minimum = ...` Can be used to define exclusive lower bound to a `number` value.
2339///
2340/// * `max_length = ...` Can be used to define maximum length for `string` types.
2341///
2342/// * `min_length = ...` Can be used to define minimum length for `string` types.
2343///
2344/// * `pattern = ...` Can be used to define valid regular expression in _ECMA-262_ dialect the field value must match.
2345///
2346/// * `max_items = ...` Can be used to define maximum items allowed for `array` fields. Value must
2347/// be non-negative integer.
2348///
2349/// * `min_items = ...` Can be used to define minimum items allowed for `array` fields. Value must
2350/// be non-negative integer.
2351///
2352/// * `schema_with = ...` Use _`schema`_ created by provided function reference instead of the
2353/// default derived _`schema`_. The function must match to `fn() -> Into<RefOr<Schema>>`. It does
2354/// not accept arguments and must return anything that can be converted into `RefOr<Schema>`.
2355///
2356/// * `additional_properties = ...` Can be used to define free form types for maps such as
2357/// [`HashMap`](std::collections::HashMap) and [`BTreeMap`](std::collections::BTreeMap).
2358/// Free form type enables use of arbitrary types within map values.
2359/// Supports formats _`additional_properties`_ and _`additional_properties = true`_.
2360///
2361/// * `ignore` or `ignore = ...` Can be used to skip the field from being serialized to OpenAPI schema. It accepts either a literal `bool` value
2362/// or a path to a function that returns `bool` (`Fn() -> bool`).
2363///
2364/// #### Field nullability and required rules
2365///
2366/// Same rules for nullability and required status apply for _`IntoParams`_ field attributes as for
2367/// _`ToSchema`_ field attributes. [See the rules][`derive@ToSchema#field-nullability-and-required-rules`].
2368///
2369/// # Partial `#[serde(...)]` attributes support
2370///
2371/// IntoParams derive has partial support for [serde attributes]. These supported attributes will reflect to the
2372/// generated OpenAPI doc. The following attributes are currently supported:
2373///
2374/// * `rename_all = "..."` Supported at the container level.
2375/// * `rename = "..."` Supported **only** at the field level.
2376/// * `default` Supported at the container level and field level according to [serde attributes].
2377/// * `skip_serializing_if = "..."` Supported **only** at the field level.
2378/// * `with = ...` Supported **only** at field level.
2379/// * `skip_serializing = "..."` Supported **only** at the field or variant level.
2380/// * `skip_deserializing = "..."` Supported **only** at the field or variant level.
2381/// * `skip = "..."` Supported **only** at the field level.
2382///
2383/// Other _`serde`_ attributes will impact the serialization but will not be reflected on the generated OpenAPI doc.
2384///
2385/// # Examples
2386///
2387/// _**Demonstrate [`IntoParams`][into_params] usage with resolving `Path` and `Query` parameters
2388/// with _`actix-web`_**_.
2389/// ```rust
2390/// use actix_web::{get, HttpResponse, Responder};
2391/// use actix_web::web::{Path, Query};
2392/// use serde::Deserialize;
2393/// use serde_json::json;
2394/// use fastapi::IntoParams;
2395///
2396/// #[derive(Deserialize, IntoParams)]
2397/// struct PetPathArgs {
2398/// /// Id of pet
2399/// id: i64,
2400/// /// Name of pet
2401/// name: String,
2402/// }
2403///
2404/// #[derive(Deserialize, IntoParams)]
2405/// struct Filter {
2406/// /// Age filter for pets
2407/// #[deprecated]
2408/// #[param(style = Form, explode, allow_reserved, example = json!([10]))]
2409/// age: Option<Vec<i32>>,
2410/// }
2411///
2412/// #[fastapi::path(
2413/// params(PetPathArgs, Filter),
2414/// responses(
2415/// (status = 200, description = "success response")
2416/// )
2417/// )]
2418/// #[get("/pet/{id}/{name}")]
2419/// async fn get_pet(pet: Path<PetPathArgs>, query: Query<Filter>) -> impl Responder {
2420/// HttpResponse::Ok().json(json!({ "id": pet.id }))
2421/// }
2422/// ```
2423///
2424/// _**Demonstrate [`IntoParams`][into_params] usage with the `#[into_params(...)]` container attribute to
2425/// be used as a path query, and inlining a schema query field:**_
2426/// ```rust
2427/// use serde::Deserialize;
2428/// use fastapi::{IntoParams, ToSchema};
2429///
2430/// #[derive(Deserialize, ToSchema)]
2431/// #[serde(rename_all = "snake_case")]
2432/// enum PetKind {
2433/// Dog,
2434/// Cat,
2435/// }
2436///
2437/// #[derive(Deserialize, IntoParams)]
2438/// #[into_params(style = Form, parameter_in = Query)]
2439/// struct PetQuery {
2440/// /// Name of pet
2441/// name: Option<String>,
2442/// /// Age of pet
2443/// age: Option<i32>,
2444/// /// Kind of pet
2445/// #[param(inline)]
2446/// kind: PetKind
2447/// }
2448///
2449/// #[fastapi::path(
2450/// get,
2451/// path = "/get_pet",
2452/// params(PetQuery),
2453/// responses(
2454/// (status = 200, description = "success response")
2455/// )
2456/// )]
2457/// async fn get_pet(query: PetQuery) {
2458/// // ...
2459/// }
2460/// ```
2461///
2462/// _**Override `String` with `i64` using `value_type` attribute.**_
2463/// ```rust
2464/// # use fastapi::IntoParams;
2465/// #
2466/// #[derive(IntoParams)]
2467/// #[into_params(parameter_in = Query)]
2468/// struct Filter {
2469/// #[param(value_type = i64)]
2470/// id: String,
2471/// }
2472/// ```
2473///
2474/// _**Override `String` with `Object` using `value_type` attribute. _`Object`_ will render as `type: object` in OpenAPI spec.**_
2475/// ```rust
2476/// # use fastapi::IntoParams;
2477/// #
2478/// #[derive(IntoParams)]
2479/// #[into_params(parameter_in = Query)]
2480/// struct Filter {
2481/// #[param(value_type = Object)]
2482/// id: String,
2483/// }
2484/// ```
2485///
2486/// _**You can use a generic type to override the default type of the field.**_
2487/// ```rust
2488/// # use fastapi::IntoParams;
2489/// #
2490/// #[derive(IntoParams)]
2491/// #[into_params(parameter_in = Query)]
2492/// struct Filter {
2493/// #[param(value_type = Option<String>)]
2494/// id: String
2495/// }
2496/// ```
2497///
2498/// _**You can even override a [`Vec`] with another one.**_
2499/// ```rust
2500/// # use fastapi::IntoParams;
2501/// #
2502/// #[derive(IntoParams)]
2503/// #[into_params(parameter_in = Query)]
2504/// struct Filter {
2505/// #[param(value_type = Vec<i32>)]
2506/// id: Vec<String>
2507/// }
2508/// ```
2509///
2510/// _**We can override value with another [`ToSchema`][to_schema].**_
2511/// ```rust
2512/// # use fastapi::{IntoParams, ToSchema};
2513/// #
2514/// #[derive(ToSchema)]
2515/// struct Id {
2516/// value: i64,
2517/// }
2518///
2519/// #[derive(IntoParams)]
2520/// #[into_params(parameter_in = Query)]
2521/// struct Filter {
2522/// #[param(value_type = Id)]
2523/// id: String
2524/// }
2525/// ```
2526///
2527/// _**Example with validation attributes.**_
2528/// ```rust
2529/// #[derive(fastapi::IntoParams)]
2530/// struct Item {
2531/// #[param(maximum = 10, minimum = 5, multiple_of = 2.5)]
2532/// id: i32,
2533/// #[param(max_length = 10, min_length = 5, pattern = "[a-z]*")]
2534/// value: String,
2535/// #[param(max_items = 5, min_items = 1)]
2536/// items: Vec<String>,
2537/// }
2538/// ````
2539///
2540/// _**Use `schema_with` to manually implement schema for a field.**_
2541/// ```rust
2542/// # use fastapi::openapi::schema::{Object, ObjectBuilder};
2543/// fn custom_type() -> Object {
2544/// ObjectBuilder::new()
2545/// .schema_type(fastapi::openapi::schema::Type::String)
2546/// .format(Some(fastapi::openapi::SchemaFormat::Custom(
2547/// "email".to_string(),
2548/// )))
2549/// .description(Some("this is the description"))
2550/// .build()
2551/// }
2552///
2553/// #[derive(fastapi::IntoParams)]
2554/// #[into_params(parameter_in = Query)]
2555/// struct Query {
2556/// #[param(schema_with = custom_type)]
2557/// email: String,
2558/// }
2559/// ```
2560///
2561/// [to_schema]: trait.ToSchema.html
2562/// [known_format]: openapi/schema/enum.KnownFormat.html
2563/// [xml]: openapi/xml/struct.Xml.html
2564/// [into_params]: trait.IntoParams.html
2565/// [path_params]: attr.path.html#params-attributes
2566/// [struct]: https://doc.rust-lang.org/std/keyword.struct.html
2567/// [style]: openapi/path/enum.ParameterStyle.html
2568/// [in_enum]: openapi/path/enum.ParameterIn.html
2569/// [primitive]: https://doc.rust-lang.org/std/primitive/index.html
2570/// [serde attributes]: https://serde.rs/attributes.html
2571/// [to_schema_xml]: macro@ToSchema#xml-attribute-configuration-options
2572pub fn into_params(input: TokenStream) -> TokenStream {
2573 let DeriveInput {
2574 attrs,
2575 ident,
2576 generics,
2577 data,
2578 ..
2579 } = syn::parse_macro_input!(input);
2580
2581 let into_params = IntoParams {
2582 attrs,
2583 generics,
2584 data,
2585 ident,
2586 };
2587
2588 into_params.to_token_stream().into()
2589}
2590
2591#[proc_macro_derive(ToResponse, attributes(response, content, to_schema))]
2592/// Generate reusable OpenAPI response that can be used
2593/// in [`fastapi::path`][path] or in [`OpenApi`][openapi].
2594///
2595/// This is `#[derive]` implementation for [`ToResponse`][to_response] trait.
2596///
2597///
2598/// _`#[response]`_ attribute can be used to alter and add [response attributes](#toresponse-response-attributes).
2599///
2600/// _`#[content]`_ attributes is used to make enum variant a content of a specific type for the
2601/// response.
2602///
2603/// _`#[to_schema]`_ attribute is used to inline a schema for a response in unnamed structs or
2604/// enum variants with `#[content]` attribute. **Note!** [`ToSchema`] need to be implemented for
2605/// the field or variant type.
2606///
2607/// Type derived with _`ToResponse`_ uses provided doc comment as a description for the response. It
2608/// can alternatively be overridden with _`description = ...`_ attribute.
2609///
2610/// _`ToResponse`_ can be used in four different ways to generate OpenAPI response component.
2611///
2612/// 1. By decorating `struct` or `enum` with [`derive@ToResponse`] derive macro. This will create a
2613/// response with inlined schema resolved from the fields of the `struct` or `variants` of the
2614/// enum.
2615///
2616/// ```rust
2617/// # use fastapi::ToResponse;
2618/// #[derive(ToResponse)]
2619/// #[response(description = "Person response returns single Person entity")]
2620/// struct Person {
2621/// name: String,
2622/// }
2623/// ```
2624///
2625/// 2. By decorating unnamed field `struct` with [`derive@ToResponse`] derive macro. Unnamed field struct
2626/// allows users to use new type pattern to define one inner field which is used as a schema for
2627/// the generated response. This allows users to define `Vec` and `Option` response types.
2628/// Additionally these types can also be used with `#[to_schema]` attribute to inline the
2629/// field's type schema if it implements [`ToSchema`] derive macro.
2630///
2631/// ```rust
2632/// # #[derive(fastapi::ToSchema)]
2633/// # struct Person {
2634/// # name: String,
2635/// # }
2636/// /// Person list response
2637/// #[derive(fastapi::ToResponse)]
2638/// struct PersonList(Vec<Person>);
2639/// ```
2640///
2641/// 3. By decorating unit struct with [`derive@ToResponse`] derive macro. Unit structs will produce a
2642/// response without body.
2643///
2644/// ```rust
2645/// /// Success response which does not have body.
2646/// #[derive(fastapi::ToResponse)]
2647/// struct SuccessResponse;
2648/// ```
2649///
2650/// 4. By decorating `enum` with variants having `#[content(...)]` attribute. This allows users to
2651/// define multiple response content schemas to single response according to OpenAPI spec.
2652/// **Note!** Enum with _`content`_ attribute in variants cannot have enum level _`example`_ or
2653/// _`examples`_ defined. Instead examples need to be defined per variant basis. Additionally
2654/// these variants can also be used with `#[to_schema]` attribute to inline the variant's type schema
2655/// if it implements [`ToSchema`] derive macro.
2656///
2657/// ```rust
2658/// #[derive(fastapi::ToSchema)]
2659/// struct Admin {
2660/// name: String,
2661/// }
2662/// #[derive(fastapi::ToSchema)]
2663/// struct Admin2 {
2664/// name: String,
2665/// id: i32,
2666/// }
2667///
2668/// #[derive(fastapi::ToResponse)]
2669/// enum Person {
2670/// #[response(examples(
2671/// ("Person1" = (value = json!({"name": "name1"}))),
2672/// ("Person2" = (value = json!({"name": "name2"})))
2673/// ))]
2674/// Admin(#[content("application/vnd-custom-v1+json")] Admin),
2675///
2676/// #[response(example = json!({"name": "name3", "id": 1}))]
2677/// Admin2(#[content("application/vnd-custom-v2+json")] #[to_schema] Admin2),
2678/// }
2679/// ```
2680///
2681/// # ToResponse `#[response(...)]` attributes
2682///
2683/// * `description = "..."` Define description for the response as str. This can be used to
2684/// override the default description resolved from doc comments if present.
2685///
2686/// * `content_type = "..."` Can be used to override the default behavior
2687/// of auto resolving the content type from the `body` attribute. If defined the value should be valid
2688/// content type such as _`application/json`_ . By default the content type is _`text/plain`_
2689/// for [primitive Rust types][primitive], `application/octet-stream` for _`[u8]`_ and _`application/json`_
2690/// for struct and mixed enum types.
2691///
2692/// * `headers(...)` Slice of response headers that are returned back to a caller.
2693///
2694/// * `example = ...` Can be _`json!(...)`_. _`json!(...)`_ should be something that
2695/// _`serde_json::json!`_ can parse as a _`serde_json::Value`_.
2696///
2697/// * `examples(...)` Define multiple examples for single response. This attribute is mutually
2698/// exclusive to the _`example`_ attribute and if both are defined this will override the _`example`_.
2699/// * `name = ...` This is first attribute and value must be literal string.
2700/// * `summary = ...` Short description of example. Value must be literal string.
2701/// * `description = ...` Long description of example. Attribute supports markdown for rich text
2702/// representation. Value must be literal string.
2703/// * `value = ...` Example value. It must be _`json!(...)`_. _`json!(...)`_ should be something that
2704/// _`serde_json::json!`_ can parse as a _`serde_json::Value`_.
2705/// * `external_value = ...` Define URI to literal example value. This is mutually exclusive to
2706/// the _`value`_ attribute. Value must be literal string.
2707///
2708/// _**Example of example definition.**_
2709/// ```text
2710/// ("John" = (summary = "This is John", value = json!({"name": "John"})))
2711/// ```
2712///
2713/// # Examples
2714///
2715/// _**Use reusable response in operation handler.**_
2716/// ```rust
2717/// #[derive(fastapi::ToResponse)]
2718/// struct PersonResponse {
2719/// value: String
2720/// }
2721///
2722/// #[derive(fastapi::OpenApi)]
2723/// #[openapi(components(responses(PersonResponse)))]
2724/// struct Doc;
2725///
2726/// #[fastapi::path(
2727/// get,
2728/// path = "/api/person",
2729/// responses(
2730/// (status = 200, response = PersonResponse)
2731/// )
2732/// )]
2733/// fn get_person() -> PersonResponse {
2734/// PersonResponse { value: "person".to_string() }
2735/// }
2736/// ```
2737///
2738/// _**Create a response from named struct.**_
2739/// ```rust
2740/// /// This is description
2741/// ///
2742/// /// It will also be used in `ToSchema` if present
2743/// #[derive(fastapi::ToSchema, fastapi::ToResponse)]
2744/// #[response(
2745/// description = "Override description for response",
2746/// content_type = "text/xml"
2747/// )]
2748/// #[response(
2749/// example = json!({"name": "the name"}),
2750/// headers(
2751/// ("csrf-token", description = "response csrf token"),
2752/// ("random-id" = i32)
2753/// )
2754/// )]
2755/// struct Person {
2756/// name: String,
2757/// }
2758/// ```
2759///
2760/// _**Create inlined person list response.**_
2761/// ```rust
2762/// # #[derive(fastapi::ToSchema)]
2763/// # struct Person {
2764/// # name: String,
2765/// # }
2766/// /// Person list response
2767/// #[derive(fastapi::ToResponse)]
2768/// struct PersonList(#[to_schema] Vec<Person>);
2769/// ```
2770///
2771/// _**Create enum response from variants.**_
2772/// ```rust
2773/// #[derive(fastapi::ToResponse)]
2774/// enum PersonType {
2775/// Value(String),
2776/// Foobar,
2777/// }
2778/// ```
2779///
2780/// [to_response]: trait.ToResponse.html
2781/// [primitive]: https://doc.rust-lang.org/std/primitive/index.html
2782/// [path]: attr.path.html
2783/// [openapi]: derive.OpenApi.html
2784pub fn to_response(input: TokenStream) -> TokenStream {
2785 let DeriveInput {
2786 attrs,
2787 ident,
2788 generics,
2789 data,
2790 ..
2791 } = syn::parse_macro_input!(input);
2792
2793 ToResponse::new(attrs, &data, generics, ident)
2794 .as_ref()
2795 .map_or_else(Diagnostics::to_token_stream, ToResponse::to_token_stream)
2796 .into()
2797}
2798
2799#[proc_macro_derive(
2800 IntoResponses,
2801 attributes(response, to_schema, ref_response, to_response)
2802)]
2803/// Generate responses with status codes what
2804/// can be attached to the [`fastapi::path`][path_into_responses].
2805///
2806/// This is `#[derive]` implementation of [`IntoResponses`][into_responses] trait. [`derive@IntoResponses`]
2807/// can be used to decorate _`structs`_ and _`enums`_ to generate response maps that can be used in
2808/// [`fastapi::path`][path_into_responses]. If _`struct`_ is decorated with [`derive@IntoResponses`] it will be
2809/// used to create a map of responses containing single response. Decorating _`enum`_ with
2810/// [`derive@IntoResponses`] will create a map of responses with a response for each variant of the _`enum`_.
2811///
2812/// Named field _`struct`_ decorated with [`derive@IntoResponses`] will create a response with inlined schema
2813/// generated from the body of the struct. This is a conveniency which allows users to directly
2814/// create responses with schemas without first creating a separate [response][to_response] type.
2815///
2816/// Unit _`struct`_ behaves similarly to then named field struct. Only difference is that it will create
2817/// a response without content since there is no inner fields.
2818///
2819/// Unnamed field _`struct`_ decorated with [`derive@IntoResponses`] will by default create a response with
2820/// referenced [schema][to_schema] if field is object or schema if type is [primitive
2821/// type][primitive]. _`#[to_schema]`_ attribute at field of unnamed _`struct`_ can be used to inline
2822/// the schema if type of the field implements [`ToSchema`][to_schema] trait. Alternatively
2823/// _`#[to_response]`_ and _`#[ref_response]`_ can be used at field to either reference a reusable
2824/// [response][to_response] or inline a reusable [response][to_response]. In both cases the field
2825/// type is expected to implement [`ToResponse`][to_response] trait.
2826///
2827///
2828/// Enum decorated with [`derive@IntoResponses`] will create a response for each variant of the _`enum`_.
2829/// Each variant must have it's own _`#[response(...)]`_ definition. Unit variant will behave same
2830/// as unit _`struct`_ by creating a response without content. Similarly named field variant and
2831/// unnamed field variant behaves the same as it was named field _`struct`_ and unnamed field
2832/// _`struct`_.
2833///
2834/// _`#[response]`_ attribute can be used at named structs, unnamed structs, unit structs and enum
2835/// variants to alter [response attributes](#intoresponses-response-attributes) of responses.
2836///
2837/// Doc comment on a _`struct`_ or _`enum`_ variant will be used as a description for the response.
2838/// It can also be overridden with _`description = "..."`_ attribute.
2839///
2840/// # IntoResponses `#[response(...)]` attributes
2841///
2842/// * `status = ...` Must be provided. Is either a valid http status code integer. E.g. _`200`_ or a
2843/// string value representing a range such as _`"4XX"`_ or `"default"` or a valid _`http::status::StatusCode`_.
2844/// _`StatusCode`_ can either be use path to the status code or _status code_ constant directly.
2845///
2846/// * `description = "..."` Define description for the response as str. This can be used to
2847/// override the default description resolved from doc comments if present.
2848///
2849/// * `content_type = "..."` Can be used to override the default behavior
2850/// of auto resolving the content type from the `body` attribute. If defined the value should be valid
2851/// content type such as _`application/json`_ . By default the content type is _`text/plain`_
2852/// for [primitive Rust types][primitive], `application/octet-stream` for _`[u8]`_ and _`application/json`_
2853/// for struct and mixed enum types.
2854///
2855/// * `headers(...)` Slice of response headers that are returned back to a caller.
2856///
2857/// * `example = ...` Can be _`json!(...)`_. _`json!(...)`_ should be something that
2858/// _`serde_json::json!`_ can parse as a _`serde_json::Value`_.
2859///
2860/// * `examples(...)` Define multiple examples for single response. This attribute is mutually
2861/// exclusive to the _`example`_ attribute and if both are defined this will override the _`example`_.
2862/// * `name = ...` This is first attribute and value must be literal string.
2863/// * `summary = ...` Short description of example. Value must be literal string.
2864/// * `description = ...` Long description of example. Attribute supports markdown for rich text
2865/// representation. Value must be literal string.
2866/// * `value = ...` Example value. It must be _`json!(...)`_. _`json!(...)`_ should be something that
2867/// _`serde_json::json!`_ can parse as a _`serde_json::Value`_.
2868/// * `external_value = ...` Define URI to literal example value. This is mutually exclusive to
2869/// the _`value`_ attribute. Value must be literal string.
2870///
2871/// _**Example of example definition.**_
2872/// ```text
2873/// ("John" = (summary = "This is John", value = json!({"name": "John"})))
2874/// ```
2875///
2876/// # Examples
2877///
2878/// _**Use `IntoResponses` to define [`fastapi::path`][path] responses.**_
2879/// ```rust
2880/// #[derive(fastapi::ToSchema)]
2881/// struct BadRequest {
2882/// message: String,
2883/// }
2884///
2885/// #[derive(fastapi::IntoResponses)]
2886/// enum UserResponses {
2887/// /// Success response
2888/// #[response(status = 200)]
2889/// Success { value: String },
2890///
2891/// #[response(status = 404)]
2892/// NotFound,
2893///
2894/// #[response(status = 400)]
2895/// BadRequest(BadRequest),
2896/// }
2897///
2898/// #[fastapi::path(
2899/// get,
2900/// path = "/api/user",
2901/// responses(
2902/// UserResponses
2903/// )
2904/// )]
2905/// fn get_user() -> UserResponses {
2906/// UserResponses::NotFound
2907/// }
2908/// ```
2909/// _**Named struct response with inlined schema.**_
2910/// ```rust
2911/// /// This is success response
2912/// #[derive(fastapi::IntoResponses)]
2913/// #[response(status = 200)]
2914/// struct SuccessResponse {
2915/// value: String,
2916/// }
2917/// ```
2918///
2919/// _**Unit struct response without content.**_
2920/// ```rust
2921/// #[derive(fastapi::IntoResponses)]
2922/// #[response(status = NOT_FOUND)]
2923/// struct NotFound;
2924/// ```
2925///
2926/// _**Unnamed struct response with inlined response schema.**_
2927/// ```rust
2928/// # #[derive(fastapi::ToSchema)]
2929/// # struct Foo;
2930/// #[derive(fastapi::IntoResponses)]
2931/// #[response(status = 201)]
2932/// struct CreatedResponse(#[to_schema] Foo);
2933/// ```
2934///
2935/// _**Enum with multiple responses.**_
2936/// ```rust
2937/// # #[derive(fastapi::ToResponse)]
2938/// # struct Response {
2939/// # message: String,
2940/// # }
2941/// # #[derive(fastapi::ToSchema)]
2942/// # struct BadRequest {}
2943/// #[derive(fastapi::IntoResponses)]
2944/// enum UserResponses {
2945/// /// Success response description.
2946/// #[response(status = 200)]
2947/// Success { value: String },
2948///
2949/// #[response(status = 404)]
2950/// NotFound,
2951///
2952/// #[response(status = 400)]
2953/// BadRequest(BadRequest),
2954///
2955/// #[response(status = 500)]
2956/// ServerError(#[ref_response] Response),
2957///
2958/// #[response(status = 418)]
2959/// TeaPot(#[to_response] Response),
2960/// }
2961/// ```
2962///
2963/// [into_responses]: trait.IntoResponses.html
2964/// [to_schema]: trait.ToSchema.html
2965/// [to_response]: trait.ToResponse.html
2966/// [path_into_responses]: attr.path.html#responses-from-intoresponses
2967/// [primitive]: https://doc.rust-lang.org/std/primitive/index.html
2968/// [path]: macro@crate::path
2969pub fn into_responses(input: TokenStream) -> TokenStream {
2970 let DeriveInput {
2971 attrs,
2972 ident,
2973 generics,
2974 data,
2975 ..
2976 } = syn::parse_macro_input!(input);
2977
2978 let into_responses = IntoResponses {
2979 attributes: attrs,
2980 ident,
2981 generics,
2982 data,
2983 };
2984
2985 into_responses.to_token_stream().into()
2986}
2987
2988/// Create OpenAPI Schema from arbitrary type.
2989///
2990/// This macro provides a quick way to render arbitrary types as OpenAPI Schema Objects. It
2991/// supports two call formats.
2992/// 1. With type only
2993/// 2. With _`#[inline]`_ attribute to inline the referenced schemas.
2994///
2995/// By default the macro will create references `($ref)` for non primitive types like _`Pet`_.
2996/// However when used with _`#[inline]`_ the non [`primitive`][primitive] type schemas will
2997/// be inlined to the schema output.
2998///
2999/// ```rust
3000/// # use fastapi::openapi::{RefOr, schema::Schema};
3001/// # #[derive(fastapi::ToSchema)]
3002/// # struct Pet {id: i32};
3003/// let schema: RefOr<Schema> = fastapi::schema!(Vec<Pet>).into();
3004///
3005/// // with inline
3006/// let schema: RefOr<Schema> = fastapi::schema!(#[inline] Vec<Pet>).into();
3007/// ```
3008///
3009/// # Examples
3010///
3011/// _**Create vec of pets schema.**_
3012/// ```rust
3013/// # use fastapi::openapi::schema::{Schema, Array, Object, ObjectBuilder, SchemaFormat,
3014/// # KnownFormat, Type};
3015/// # use fastapi::openapi::RefOr;
3016/// #[derive(fastapi::ToSchema)]
3017/// struct Pet {
3018/// id: i32,
3019/// name: String,
3020/// }
3021///
3022/// let schema: RefOr<Schema> = fastapi::schema!(#[inline] Vec<Pet>).into();
3023/// // will output
3024/// let generated = RefOr::T(Schema::Array(
3025/// Array::new(
3026/// ObjectBuilder::new()
3027/// .property("id", ObjectBuilder::new()
3028/// .schema_type(Type::Integer)
3029/// .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int32)))
3030/// .build())
3031/// .required("id")
3032/// .property("name", Object::with_type(Type::String))
3033/// .required("name")
3034/// )
3035/// ));
3036/// # assert_json_diff::assert_json_eq!(serde_json::to_value(&schema).unwrap(), serde_json::to_value(&generated).unwrap());
3037/// ```
3038///
3039/// [primitive]: https://doc.rust-lang.org/std/primitive/index.html
3040#[proc_macro]
3041pub fn schema(input: TokenStream) -> TokenStream {
3042 struct Schema {
3043 inline: bool,
3044 ty: syn::Type,
3045 }
3046 impl Parse for Schema {
3047 fn parse(input: ParseStream) -> syn::Result<Self> {
3048 let inline = if input.peek(Token![#]) && input.peek2(Bracket) {
3049 input.parse::<Token![#]>()?;
3050
3051 let inline;
3052 bracketed!(inline in input);
3053 let i = inline.parse::<Ident>()?;
3054 i == "inline"
3055 } else {
3056 false
3057 };
3058
3059 let ty = input.parse()?;
3060
3061 Ok(Self { inline, ty })
3062 }
3063 }
3064
3065 let schema = syn::parse_macro_input!(input as Schema);
3066 let type_tree = match TypeTree::from_type(&schema.ty) {
3067 Ok(type_tree) => type_tree,
3068 Err(diagnostics) => return diagnostics.into_token_stream().into(),
3069 };
3070
3071 let generics = match type_tree.get_path_generics() {
3072 Ok(generics) => generics,
3073 Err(error) => return error.into_compile_error().into(),
3074 };
3075
3076 let schema = ComponentSchema::new(ComponentSchemaProps {
3077 features: vec![Feature::Inline(schema.inline.into())],
3078 type_tree: &type_tree,
3079 description: None,
3080 container: &component::Container {
3081 generics: &generics,
3082 },
3083 });
3084
3085 let schema = match schema {
3086 Ok(schema) => schema.to_token_stream(),
3087 Err(diagnostics) => return diagnostics.to_token_stream().into(),
3088 };
3089
3090 quote! {
3091 {
3092 let mut generics: Vec<fastapi::openapi::RefOr<fastapi::openapi::schema::Schema>> = Vec::new();
3093 #schema
3094 }
3095 }
3096 .into()
3097}
3098
3099/// Tokenizes slice or Vec of tokenizable items as array either with reference (`&[...]`)
3100/// or without correctly to OpenAPI JSON.
3101#[cfg_attr(feature = "debug", derive(Debug))]
3102enum Array<'a, T>
3103where
3104 T: Sized + ToTokens,
3105{
3106 Owned(Vec<T>),
3107 #[allow(dead_code)]
3108 Borrowed(&'a [T]),
3109}
3110
3111impl<V> FromIterator<V> for Array<'_, V>
3112where
3113 V: Sized + ToTokens,
3114{
3115 fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
3116 Self::Owned(iter.into_iter().collect())
3117 }
3118}
3119
3120impl<'a, T> Deref for Array<'a, T>
3121where
3122 T: Sized + ToTokens,
3123{
3124 type Target = [T];
3125
3126 fn deref(&self) -> &Self::Target {
3127 match self {
3128 Self::Owned(vec) => vec.as_slice(),
3129 Self::Borrowed(slice) => slice,
3130 }
3131 }
3132}
3133
3134impl<T> ToTokens for Array<'_, T>
3135where
3136 T: Sized + ToTokens,
3137{
3138 fn to_tokens(&self, tokens: &mut TokenStream2) {
3139 let values = match self {
3140 Self::Owned(values) => values.iter(),
3141 Self::Borrowed(values) => values.iter(),
3142 };
3143
3144 tokens.append(Group::new(
3145 proc_macro2::Delimiter::Bracket,
3146 values
3147 .fold(Punctuated::new(), |mut punctuated, item| {
3148 punctuated.push_value(item);
3149 punctuated.push_punct(Punct::new(',', proc_macro2::Spacing::Alone));
3150
3151 punctuated
3152 })
3153 .to_token_stream(),
3154 ));
3155 }
3156}
3157
3158#[cfg_attr(feature = "debug", derive(Debug))]
3159enum Deprecated {
3160 True,
3161 False,
3162}
3163
3164impl From<bool> for Deprecated {
3165 fn from(bool: bool) -> Self {
3166 if bool {
3167 Self::True
3168 } else {
3169 Self::False
3170 }
3171 }
3172}
3173
3174impl ToTokens for Deprecated {
3175 fn to_tokens(&self, tokens: &mut TokenStream2) {
3176 tokens.extend(match self {
3177 Self::False => quote! { fastapi::openapi::Deprecated::False },
3178 Self::True => quote! { fastapi::openapi::Deprecated::True },
3179 })
3180 }
3181}
3182
3183#[derive(PartialEq, Eq)]
3184#[cfg_attr(feature = "debug", derive(Debug))]
3185enum Required {
3186 True,
3187 False,
3188}
3189
3190impl From<bool> for Required {
3191 fn from(bool: bool) -> Self {
3192 if bool {
3193 Self::True
3194 } else {
3195 Self::False
3196 }
3197 }
3198}
3199
3200impl From<features::attributes::Required> for Required {
3201 fn from(value: features::attributes::Required) -> Self {
3202 let features::attributes::Required(required) = value;
3203 crate::Required::from(required)
3204 }
3205}
3206
3207impl ToTokens for Required {
3208 fn to_tokens(&self, tokens: &mut TokenStream2) {
3209 tokens.extend(match self {
3210 Self::False => quote! { fastapi::openapi::Required::False },
3211 Self::True => quote! { fastapi::openapi::Required::True },
3212 })
3213 }
3214}
3215
3216#[derive(Default)]
3217#[cfg_attr(feature = "debug", derive(Debug))]
3218struct ExternalDocs {
3219 url: String,
3220 description: Option<String>,
3221}
3222
3223impl Parse for ExternalDocs {
3224 fn parse(input: ParseStream) -> syn::Result<Self> {
3225 const EXPECTED_ATTRIBUTE: &str = "unexpected attribute, expected any of: url, description";
3226
3227 let mut external_docs = ExternalDocs::default();
3228
3229 while !input.is_empty() {
3230 let ident = input.parse::<Ident>().map_err(|error| {
3231 syn::Error::new(error.span(), format!("{EXPECTED_ATTRIBUTE}, {error}"))
3232 })?;
3233 let attribute_name = &*ident.to_string();
3234
3235 match attribute_name {
3236 "url" => {
3237 external_docs.url = parse_utils::parse_next_literal_str(input)?;
3238 }
3239 "description" => {
3240 external_docs.description = Some(parse_utils::parse_next_literal_str(input)?);
3241 }
3242 _ => return Err(syn::Error::new(ident.span(), EXPECTED_ATTRIBUTE)),
3243 }
3244
3245 if !input.is_empty() {
3246 input.parse::<Token![,]>()?;
3247 }
3248 }
3249
3250 Ok(external_docs)
3251 }
3252}
3253
3254impl ToTokens for ExternalDocs {
3255 fn to_tokens(&self, tokens: &mut TokenStream2) {
3256 let url = &self.url;
3257 tokens.extend(quote! {
3258 fastapi::openapi::external_docs::ExternalDocsBuilder::new()
3259 .url(#url)
3260 });
3261
3262 if let Some(ref description) = self.description {
3263 tokens.extend(quote! {
3264 .description(Some(#description))
3265 });
3266 }
3267
3268 tokens.extend(quote! { .build() })
3269 }
3270}
3271
3272/// Represents OpenAPI Any value used in example and default fields.
3273#[derive(Clone)]
3274#[cfg_attr(feature = "debug", derive(Debug))]
3275enum AnyValue {
3276 String(TokenStream2),
3277 Json(TokenStream2),
3278 DefaultTrait {
3279 struct_ident: Ident,
3280 field_ident: Member,
3281 },
3282}
3283
3284impl AnyValue {
3285 /// Parse `json!(...)` as [`AnyValue::Json`]
3286 fn parse_json(input: ParseStream) -> syn::Result<Self> {
3287 parse_utils::parse_json_token_stream(input).map(AnyValue::Json)
3288 }
3289
3290 fn parse_any(input: ParseStream) -> syn::Result<Self> {
3291 if input.peek(Lit) {
3292 let punct = input.parse::<Option<Token![-]>>()?;
3293 let lit = input.parse::<Lit>().unwrap();
3294
3295 Ok(AnyValue::Json(quote! { #punct #lit}))
3296 } else {
3297 let fork = input.fork();
3298 let is_json = if fork.peek(syn::Ident) && fork.peek2(Token![!]) {
3299 let ident = fork.parse::<Ident>().unwrap();
3300 ident == "json"
3301 } else {
3302 false
3303 };
3304
3305 if is_json {
3306 let json = parse_utils::parse_json_token_stream(input)?;
3307
3308 Ok(AnyValue::Json(json))
3309 } else {
3310 let method = input.parse::<ExprPath>().map_err(|error| {
3311 syn::Error::new(
3312 error.span(),
3313 "expected literal value, json!(...) or method reference",
3314 )
3315 })?;
3316
3317 Ok(AnyValue::Json(quote! { #method() }))
3318 }
3319 }
3320 }
3321
3322 fn parse_lit_str_or_json(input: ParseStream) -> syn::Result<Self> {
3323 if input.peek(LitStr) {
3324 Ok(AnyValue::String(
3325 input.parse::<LitStr>().unwrap().to_token_stream(),
3326 ))
3327 } else {
3328 Ok(AnyValue::Json(parse_utils::parse_json_token_stream(input)?))
3329 }
3330 }
3331
3332 fn new_default_trait(struct_ident: Ident, field_ident: Member) -> Self {
3333 Self::DefaultTrait {
3334 struct_ident,
3335 field_ident,
3336 }
3337 }
3338}
3339
3340impl ToTokens for AnyValue {
3341 fn to_tokens(&self, tokens: &mut TokenStream2) {
3342 match self {
3343 Self::Json(json) => tokens.extend(quote! {
3344 serde_json::json!(#json)
3345 }),
3346 Self::String(string) => string.to_tokens(tokens),
3347 Self::DefaultTrait {
3348 struct_ident,
3349 field_ident,
3350 } => tokens.extend(quote! {
3351 serde_json::to_value(#struct_ident::default().#field_ident).unwrap()
3352 }),
3353 }
3354 }
3355}
3356
3357trait OptionExt<T> {
3358 fn map_try<F, U, E>(self, f: F) -> Result<Option<U>, E>
3359 where
3360 F: FnOnce(T) -> Result<U, E>;
3361 fn and_then_try<F, U, E>(self, f: F) -> Result<Option<U>, E>
3362 where
3363 F: FnOnce(T) -> Result<Option<U>, E>;
3364 fn or_else_try<F, U>(self, f: F) -> Result<Option<T>, U>
3365 where
3366 F: FnOnce() -> Result<Option<T>, U>;
3367}
3368
3369impl<T> OptionExt<T> for Option<T> {
3370 fn map_try<F, U, E>(self, f: F) -> Result<Option<U>, E>
3371 where
3372 F: FnOnce(T) -> Result<U, E>,
3373 {
3374 if let Some(v) = self {
3375 f(v).map(Some)
3376 } else {
3377 Ok(None)
3378 }
3379 }
3380
3381 fn and_then_try<F, U, E>(self, f: F) -> Result<Option<U>, E>
3382 where
3383 F: FnOnce(T) -> Result<Option<U>, E>,
3384 {
3385 if let Some(v) = self {
3386 match f(v) {
3387 Ok(inner) => Ok(inner),
3388 Err(error) => Err(error),
3389 }
3390 } else {
3391 Ok(None)
3392 }
3393 }
3394
3395 fn or_else_try<F, U>(self, f: F) -> Result<Option<T>, U>
3396 where
3397 F: FnOnce() -> Result<Option<T>, U>,
3398 {
3399 if self.is_none() {
3400 f()
3401 } else {
3402 Ok(self)
3403 }
3404 }
3405}
3406
3407trait GenericsExt {
3408 /// Get index of `GenericParam::Type` ignoring other generic param types.
3409 fn get_generic_type_param_index(&self, type_tree: &TypeTree) -> Option<usize>;
3410}
3411
3412impl<'g> GenericsExt for &'g syn::Generics {
3413 fn get_generic_type_param_index(&self, type_tree: &TypeTree) -> Option<usize> {
3414 let ident = &type_tree
3415 .path
3416 .as_ref()
3417 .expect("TypeTree of generic object must have a path")
3418 .segments
3419 .last()
3420 .expect("Generic object path must have at least one segment")
3421 .ident;
3422
3423 self.params
3424 .iter()
3425 .filter(|generic| matches!(generic, GenericParam::Type(_)))
3426 .enumerate()
3427 .find_map(|(index, generic)| {
3428 if matches!(generic, GenericParam::Type(ty) if ty.ident == *ident) {
3429 Some(index)
3430 } else {
3431 None
3432 }
3433 })
3434 }
3435}
3436
3437trait ToTokensDiagnostics {
3438 fn to_tokens(&self, tokens: &mut TokenStream2) -> Result<(), Diagnostics>;
3439
3440 #[allow(unused)]
3441 fn into_token_stream(self) -> TokenStream2
3442 where
3443 Self: std::marker::Sized,
3444 {
3445 ToTokensDiagnostics::to_token_stream(&self)
3446 }
3447
3448 fn to_token_stream(&self) -> TokenStream2 {
3449 let mut tokens = TokenStream2::new();
3450 match ToTokensDiagnostics::to_tokens(self, &mut tokens) {
3451 Ok(_) => tokens,
3452 Err(error_stream) => Into::<Diagnostics>::into(error_stream).into_token_stream(),
3453 }
3454 }
3455
3456 fn try_to_token_stream(&self) -> Result<TokenStream2, Diagnostics> {
3457 let mut tokens = TokenStream2::new();
3458 match ToTokensDiagnostics::to_tokens(self, &mut tokens) {
3459 Ok(_) => Ok(tokens),
3460 Err(diagnostics) => Err(diagnostics),
3461 }
3462 }
3463}
3464
3465macro_rules! as_tokens_or_diagnostics {
3466 ( $type:expr ) => {{
3467 let mut _tokens = proc_macro2::TokenStream::new();
3468 match crate::ToTokensDiagnostics::to_tokens($type, &mut _tokens) {
3469 Ok(_) => _tokens,
3470 Err(diagnostics) => return Err(diagnostics),
3471 }
3472 }};
3473}
3474
3475use as_tokens_or_diagnostics;
3476
3477#[derive(Debug)]
3478struct Diagnostics {
3479 diagnostics: Vec<DiangosticsInner>,
3480}
3481
3482#[derive(Debug)]
3483struct DiangosticsInner {
3484 span: Span,
3485 message: Cow<'static, str>,
3486 suggestions: Vec<Suggestion>,
3487}
3488
3489#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
3490enum Suggestion {
3491 Help(Cow<'static, str>),
3492 Note(Cow<'static, str>),
3493}
3494
3495impl Display for Diagnostics {
3496 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3497 write!(f, "{}", self.message())
3498 }
3499}
3500
3501impl Display for Suggestion {
3502 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3503 match self {
3504 Self::Help(help) => {
3505 let s: &str = help.borrow();
3506 write!(f, "help = {}", s)
3507 }
3508 Self::Note(note) => {
3509 let s: &str = note.borrow();
3510 write!(f, "note = {}", s)
3511 }
3512 }
3513 }
3514}
3515
3516impl Diagnostics {
3517 fn message(&self) -> Cow<'static, str> {
3518 self.diagnostics
3519 .first()
3520 .as_ref()
3521 .map(|diagnostics| diagnostics.message.clone())
3522 .unwrap_or_else(|| Cow::Borrowed(""))
3523 }
3524
3525 pub fn new<S: Into<Cow<'static, str>>>(message: S) -> Self {
3526 Self::with_span(Span::call_site(), message)
3527 }
3528
3529 pub fn with_span<S: Into<Cow<'static, str>>>(span: Span, message: S) -> Self {
3530 Self {
3531 diagnostics: vec![DiangosticsInner {
3532 span,
3533 message: message.into(),
3534 suggestions: Vec::new(),
3535 }],
3536 }
3537 }
3538
3539 pub fn help<S: Into<Cow<'static, str>>>(mut self, help: S) -> Self {
3540 if let Some(diagnostics) = self.diagnostics.first_mut() {
3541 diagnostics.suggestions.push(Suggestion::Help(help.into()));
3542 diagnostics.suggestions.sort();
3543 }
3544
3545 self
3546 }
3547
3548 pub fn note<S: Into<Cow<'static, str>>>(mut self, note: S) -> Self {
3549 if let Some(diagnostics) = self.diagnostics.first_mut() {
3550 diagnostics.suggestions.push(Suggestion::Note(note.into()));
3551 diagnostics.suggestions.sort();
3552 }
3553
3554 self
3555 }
3556}
3557
3558impl From<syn::Error> for Diagnostics {
3559 fn from(value: syn::Error) -> Self {
3560 Self::with_span(value.span(), value.to_string())
3561 }
3562}
3563
3564impl ToTokens for Diagnostics {
3565 fn to_tokens(&self, tokens: &mut TokenStream2) {
3566 for diagnostics in &self.diagnostics {
3567 let span = diagnostics.span;
3568 let message: &str = diagnostics.message.borrow();
3569
3570 let suggestions = diagnostics
3571 .suggestions
3572 .iter()
3573 .map(Suggestion::to_string)
3574 .collect::<Vec<_>>()
3575 .join("\n");
3576
3577 let diagnostics = if !suggestions.is_empty() {
3578 Cow::Owned(format!("{message}\n\n{suggestions}"))
3579 } else {
3580 Cow::Borrowed(message)
3581 };
3582
3583 tokens.extend(quote_spanned! {span=>
3584 ::core::compile_error!(#diagnostics);
3585 })
3586 }
3587 }
3588}
3589
3590impl Error for Diagnostics {}
3591
3592impl FromIterator<Diagnostics> for Option<Diagnostics> {
3593 fn from_iter<T: IntoIterator<Item = Diagnostics>>(iter: T) -> Self {
3594 iter.into_iter().reduce(|mut acc, diagnostics| {
3595 acc.diagnostics.extend(diagnostics.diagnostics);
3596 acc
3597 })
3598 }
3599}
3600
3601trait AttributesExt {
3602 fn has_deprecated(&self) -> bool;
3603}
3604
3605impl AttributesExt for Vec<syn::Attribute> {
3606 fn has_deprecated(&self) -> bool {
3607 let this = &**self;
3608 this.has_deprecated()
3609 }
3610}
3611
3612impl<'a> AttributesExt for &'a [syn::Attribute] {
3613 fn has_deprecated(&self) -> bool {
3614 self.iter().any(|attr| {
3615 matches!(attr.path().get_ident(), Some(ident) if &*ident.to_string() == "deprecated")
3616 })
3617 }
3618}
3619
3620#[cfg(test)]
3621mod tests {
3622 use super::*;
3623
3624 #[test]
3625 fn diagnostics_ordering_help_comes_before_note() {
3626 let diagnostics = Diagnostics::new("this an error")
3627 .note("you could do this to solve the error")
3628 .help("try this thing");
3629
3630 let tokens = diagnostics.into_token_stream();
3631
3632 let expected_tokens = quote::quote!(::core::compile_error!(
3633 "this an error\n\nhelp = try this thing\nnote = you could do this to solve the error"
3634 ););
3635
3636 assert_eq!(tokens.to_string(), expected_tokens.to_string());
3637 }
3638}
3639
3640/// Parsing utils
3641mod parse_utils {
3642 use std::fmt::Display;
3643
3644 use proc_macro2::{Group, Ident, TokenStream};
3645 use quote::{quote, ToTokens};
3646 use syn::{
3647 parenthesized,
3648 parse::{Parse, ParseStream},
3649 punctuated::Punctuated,
3650 spanned::Spanned,
3651 token::Comma,
3652 Error, Expr, ExprPath, LitBool, LitStr, Token,
3653 };
3654
3655 #[cfg_attr(feature = "debug", derive(Debug))]
3656 #[derive(Clone)]
3657 pub enum LitStrOrExpr {
3658 LitStr(LitStr),
3659 Expr(Expr),
3660 }
3661
3662 impl From<String> for LitStrOrExpr {
3663 fn from(value: String) -> Self {
3664 Self::LitStr(LitStr::new(&value, proc_macro2::Span::call_site()))
3665 }
3666 }
3667
3668 impl LitStrOrExpr {
3669 pub(crate) fn is_empty_litstr(&self) -> bool {
3670 matches!(self, Self::LitStr(s) if s.value().is_empty())
3671 }
3672 }
3673
3674 impl Default for LitStrOrExpr {
3675 fn default() -> Self {
3676 Self::LitStr(LitStr::new("", proc_macro2::Span::call_site()))
3677 }
3678 }
3679
3680 impl Parse for LitStrOrExpr {
3681 fn parse(input: ParseStream) -> syn::Result<Self> {
3682 if input.peek(LitStr) {
3683 Ok::<LitStrOrExpr, Error>(LitStrOrExpr::LitStr(input.parse::<LitStr>()?))
3684 } else {
3685 Ok(LitStrOrExpr::Expr(input.parse::<Expr>()?))
3686 }
3687 }
3688 }
3689
3690 impl ToTokens for LitStrOrExpr {
3691 fn to_tokens(&self, tokens: &mut TokenStream) {
3692 match self {
3693 Self::LitStr(str) => str.to_tokens(tokens),
3694 Self::Expr(expr) => expr.to_tokens(tokens),
3695 }
3696 }
3697 }
3698
3699 impl Display for LitStrOrExpr {
3700 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3701 match self {
3702 Self::LitStr(str) => write!(f, "{str}", str = str.value()),
3703 Self::Expr(expr) => write!(f, "{expr}", expr = expr.into_token_stream()),
3704 }
3705 }
3706 }
3707
3708 pub fn parse_next<T: FnOnce() -> Result<R, syn::Error>, R: Sized>(
3709 input: ParseStream,
3710 next: T,
3711 ) -> Result<R, syn::Error> {
3712 input.parse::<Token![=]>()?;
3713 next()
3714 }
3715
3716 pub fn parse_next_literal_str(input: ParseStream) -> syn::Result<String> {
3717 Ok(parse_next(input, || input.parse::<LitStr>())?.value())
3718 }
3719
3720 pub fn parse_next_literal_str_or_expr(input: ParseStream) -> syn::Result<LitStrOrExpr> {
3721 parse_next(input, || LitStrOrExpr::parse(input)).map_err(|error| {
3722 syn::Error::new(
3723 error.span(),
3724 format!("expected literal string or expression argument: {error}"),
3725 )
3726 })
3727 }
3728
3729 pub fn parse_groups_collect<T, R>(input: ParseStream) -> syn::Result<R>
3730 where
3731 T: Sized,
3732 T: Parse,
3733 R: FromIterator<T>,
3734 {
3735 Punctuated::<Group, Comma>::parse_terminated(input).and_then(|groups| {
3736 groups
3737 .into_iter()
3738 .map(|group| syn::parse2::<T>(group.stream()))
3739 .collect::<syn::Result<R>>()
3740 })
3741 }
3742
3743 pub fn parse_parethesized_terminated<T: Parse, S: Parse>(
3744 input: ParseStream,
3745 ) -> syn::Result<Punctuated<T, S>> {
3746 let group;
3747 syn::parenthesized!(group in input);
3748 Punctuated::parse_terminated(&group)
3749 }
3750
3751 pub fn parse_comma_separated_within_parethesis_with<T>(
3752 input: ParseStream,
3753 with: fn(ParseStream) -> syn::Result<T>,
3754 ) -> syn::Result<Punctuated<T, Comma>>
3755 where
3756 T: Parse,
3757 {
3758 let content;
3759 parenthesized!(content in input);
3760 Punctuated::<T, Comma>::parse_terminated_with(&content, with)
3761 }
3762
3763 pub fn parse_comma_separated_within_parenthesis<T>(
3764 input: ParseStream,
3765 ) -> syn::Result<Punctuated<T, Comma>>
3766 where
3767 T: Parse,
3768 {
3769 let content;
3770 parenthesized!(content in input);
3771 Punctuated::<T, Comma>::parse_terminated(&content)
3772 }
3773
3774 pub fn parse_bool_or_true(input: ParseStream) -> syn::Result<bool> {
3775 if input.peek(Token![=]) && input.peek2(LitBool) {
3776 input.parse::<Token![=]>()?;
3777
3778 Ok(input.parse::<LitBool>()?.value())
3779 } else {
3780 Ok(true)
3781 }
3782 }
3783
3784 /// Parse `json!(...)` as a [`TokenStream`].
3785 pub fn parse_json_token_stream(input: ParseStream) -> syn::Result<TokenStream> {
3786 if input.peek(syn::Ident) && input.peek2(Token![!]) {
3787 input.parse::<Ident>().and_then(|ident| {
3788 if ident != "json" {
3789 return Err(Error::new(
3790 ident.span(),
3791 format!("unexpected token {ident}, expected: json!(...)"),
3792 ));
3793 }
3794
3795 Ok(ident)
3796 })?;
3797 input.parse::<Token![!]>()?;
3798
3799 Ok(input.parse::<Group>()?.stream())
3800 } else {
3801 Err(Error::new(
3802 input.span(),
3803 "unexpected token, expected json!(...)",
3804 ))
3805 }
3806 }
3807
3808 #[cfg_attr(feature = "debug", derive(Debug))]
3809 #[derive(Clone)]
3810 pub enum LitBoolOrExprPath {
3811 LitBool(LitBool),
3812 ExprPath(ExprPath),
3813 }
3814
3815 impl From<bool> for LitBoolOrExprPath {
3816 fn from(value: bool) -> Self {
3817 Self::LitBool(LitBool::new(value, proc_macro2::Span::call_site()))
3818 }
3819 }
3820
3821 impl Default for LitBoolOrExprPath {
3822 fn default() -> Self {
3823 Self::LitBool(LitBool::new(false, proc_macro2::Span::call_site()))
3824 }
3825 }
3826
3827 impl Parse for LitBoolOrExprPath {
3828 fn parse(input: ParseStream) -> syn::Result<Self> {
3829 if input.peek(LitBool) {
3830 Ok(LitBoolOrExprPath::LitBool(input.parse::<LitBool>()?))
3831 } else {
3832 let expr = input.parse::<Expr>()?;
3833
3834 match expr {
3835 Expr::Path(expr_path) => Ok(LitBoolOrExprPath::ExprPath(expr_path)),
3836 _ => Err(syn::Error::new(
3837 expr.span(),
3838 format!(
3839 "expected literal bool or path to a function that returns bool, found: {}",
3840 quote! {#expr}
3841 ),
3842 )),
3843 }
3844 }
3845 }
3846 }
3847
3848 impl ToTokens for LitBoolOrExprPath {
3849 fn to_tokens(&self, tokens: &mut TokenStream) {
3850 match self {
3851 Self::LitBool(bool) => bool.to_tokens(tokens),
3852 Self::ExprPath(call) => call.to_tokens(tokens),
3853 }
3854 }
3855 }
3856
3857 pub fn parse_next_literal_bool_or_call(input: ParseStream) -> syn::Result<LitBoolOrExprPath> {
3858 if input.peek(Token![=]) {
3859 parse_next(input, || LitBoolOrExprPath::parse(input))
3860 } else {
3861 Ok(LitBoolOrExprPath::from(true))
3862 }
3863 }
3864}