utoipa/
lib.rs

1#![warn(missing_docs)]
2#![warn(rustdoc::broken_intra_doc_links)]
3#![cfg_attr(doc_cfg, feature(doc_cfg))]
4//! Want to have your API documented with OpenAPI? But you don't want to see the
5//! trouble with manual yaml or json tweaking? Would like it to be so easy that it would almost
6//! be like utopic? Don't worry utoipa is just there to fill this gap. It aims to do if not all then
7//! the most of heavy lifting for you enabling you to focus writing the actual API logic instead of
8//! documentation. It aims to be *minimal*, *simple* and *fast*. It uses simple proc macros which
9//! you can use to annotate your code to have items documented.
10//!
11//! Utoipa crate provides autogenerated OpenAPI documentation for Rust REST APIs. It treats
12//! code first approach as a first class citizen and simplifies API documentation by providing
13//! simple macros for generating the documentation from your code.
14//!
15//! It also contains Rust types of OpenAPI spec allowing you to write the OpenAPI spec only using
16//! Rust if auto-generation is not your flavor or does not fit your purpose.
17//!
18//! Long term goal of the library is to be the place to go when OpenAPI documentation is needed in Rust
19//! codebase.
20//!
21//! Utoipa is framework agnostic and could be used together with any web framework or even without one. While
22//! being portable and standalone one of it's key aspects is simple integration with web frameworks.
23//!
24//! Currently utoipa provides simple integration with actix-web framework but is not limited to the actix-web
25//! framework. All functionalities are not restricted to any specific framework.
26//!
27//! # Choose your flavor and document your API with ice cold IPA
28//!
29//! |Flavor|Support|
30//! |--|--|
31//! |[actix-web](https://github.com/actix/actix-web)|Parse path, path parameters and query parameters, recognize request body and response body, [`utoipa-actix-web` bindings](https://docs.rs/utoipa-actix-web). See more at [docs][actix_path]|
32//! |[axum](https://github.com/tokio-rs/axum)|Parse path and query parameters, recognize request body and response body, [`utoipa-axum` bindings](https://docs.rs/utoipa-axum). See more at [docs][axum_path]|
33//! |[rocket](https://github.com/SergioBenitez/Rocket)| Parse path, path parameters and query parameters, recognize request body and response body. See more at [docs][rocket_path]|
34//! |Others*| Plain `utoipa` without extra flavor. This gives you all the basic benefits listed below in **[Features](#features)** section but with little less automation.|
35//!
36//! > Others* = For example [warp](https://github.com/seanmonstar/warp) but could be anything.
37//!
38//! Refer to the existing [examples](https://github.com/juhaku/utoipa/tree/master/examples) to find out more.
39//!
40//! ## Features
41//!
42//! * OpenAPI 3.1
43//! * Pluggable, easy setup and integration with frameworks.
44//! * No bloat, enable what you need.
45//! * Support for generic types
46//!   * **Note!**<br>
47//!     Tuples, arrays and slices cannot be used as generic arguments on types. Types implementing `ToSchema` manually should not have generic arguments, as
48//!     they are not composeable and will result compile error.
49//! * Automatic schema collection from usages recursively.
50//!   * Request body from either handler function arguments (if supported by framework) or from `request_body` attribute.
51//!   * Response body from response `body` attribute or response `content` attribute.
52//! * Various OpenAPI visualization tools supported out of the box.
53//! * Rust type aliases via [`utoipa-config`][utoipa_config].
54//!
55//! # What's up with the word play?
56//!
57//! The name comes from words `utopic` and `api` where `uto` is the first three letters of _utopic_
58//! and the `ipa` is _api_ reversed. Aaand... `ipa` is also awesome type of beer.
59//!
60//! # Crate Features
61//!
62//! * **`macros`** Enable `utoipa-gen` macros. **This is enabled by default.**
63//! * **`yaml`** Enables **serde_norway** serialization of OpenAPI objects.
64//! * **`actix_extras`** Enhances [actix-web](https://github.com/actix/actix-web/) integration with being able to
65//!   parse `path`, `path` and `query` parameters from actix web path attribute macros. See [actix extras support][actix_path] or
66//!   [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more details.
67//! * **`rocket_extras`** Enhances [rocket](https://github.com/SergioBenitez/Rocket) framework integration with being
68//!   able to parse `path`, `path` and `query` parameters from rocket path attribute macros. See [rocket extras support][rocket_path]
69//!   or [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more details
70//! * **`axum_extras`** Enhances [axum](https://github.com/tokio-rs/axum) framework integration allowing users to use `IntoParams`
71//!   without defining the `parameter_in` attribute. See [axum extras support][axum_path]
72//!   or [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more details.
73//! * **`debug`** Add extra traits such as debug traits to openapi definitions and elsewhere.
74//! * **`chrono`** Add support for [chrono](https://crates.io/crates/chrono) `DateTime`, `Date`, `NaiveDate`, `NaiveTime` and `Duration`
75//!   types. By default these types are parsed to `string` types with additional `format` information.
76//!   `format: date-time` for `DateTime` and `format: date` for `Date` and `NaiveDate` according
77//!   [RFC3339](https://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14) as `ISO-8601`. To
78//!   override default `string` representation users have to use `value_type` attribute to override the type.
79//!   See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html) for more details.
80//! * **`time`** Add support for [time](https://crates.io/crates/time) `OffsetDateTime`, `PrimitiveDateTime`, `Date`, and `Duration` types.
81//!   By default these types are parsed as `string`. `OffsetDateTime` and `PrimitiveDateTime` will use `date-time` format. `Date` will use
82//!   `date` format and `Duration` will not have any format. To override default `string` representation users have to use `value_type` attribute
83//!   to override the type. See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html) for more details.
84//! * **`jiff_0_2`** Add support for [jiff 0.2](https://crates.io/crates/jiff) `Zoned`, and `civil::Date` types.
85//!   By default these types are parsed as `string`. `Zoned` will use `date-time` format. `civil::Date` will use
86//!   `date` format. To override default `string` representation users have to use `value_type` attribute
87//!   to override the type. See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html) for more details.
88//! * **`decimal`** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default**
89//!   it is interpreted as `String`. If you wish to change the format you need to override the type.
90//!   See the `value_type` in [`ToSchema` derive docs][to_schema_derive].
91//! * **`decimal_float`** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default**
92//!   it is interpreted as `Number`. This feature is mutually exclusive with **decimal** and allow to change the default type used in your
93//!   documentation for `Decimal` much like `serde_with_float` feature exposed by rust_decimal.
94//! * **`uuid`** Add support for [uuid](https://github.com/uuid-rs/uuid). `Uuid` type will be presented as `String` with
95//!   format `uuid` in OpenAPI spec.
96//! * **`ulid`** Add support for [ulid](https://github.com/dylanhart/ulid-rs). `Ulid` type will be presented as `String` with
97//!   format `ulid` in OpenAPI spec.
98//! * **`url`** Add support for [url](https://github.com/servo/rust-url). `Url` type will be presented as `String` with
99//!   format `uri` in OpenAPI spec.
100//! * **`smallvec`** Add support for [smallvec](https://crates.io/crates/smallvec). `SmallVec` will be treated as `Vec`.
101//! * **`openapi_extensions`** Adds convenience functions for documenting common scenarios, such as JSON request bodies and responses.
102//!   See the [`request_body`](https://docs.rs/utoipa/latest/utoipa/openapi/request_body/index.html) and
103//!   [`response`](https://docs.rs/utoipa/latest/utoipa/openapi/response/index.html) docs for examples.
104//! * **`repr`** Add support for [repr_serde](https://github.com/dtolnay/serde-repr)'s `repr(u*)` and `repr(i*)` attributes to unit type enums for
105//!   C-like enum representation. See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html) for more details.
106//! * **`preserve_order`** Preserve order of properties when serializing the schema for a component.
107//!   When enabled, the properties are listed in order of fields in the corresponding struct definition.
108//!   When disabled, the properties are listed in alphabetical order.
109//! * **`preserve_path_order`** Preserve order of OpenAPI Paths according to order they have been
110//!   introduced to the `#[openapi(paths(...))]` macro attribute. If disabled the paths will be
111//!   ordered in alphabetical order. **However** the operations order under the path **will** be always constant according to
112//!   [specification](https://spec.openapis.org/oas/latest.html#fixed-fields-6)
113//! * **`indexmap`** Add support for [indexmap](https://crates.io/crates/indexmap). When enabled `IndexMap` will be rendered as a map similar to
114//!   `BTreeMap` and `HashMap`.
115//! * **`non_strict_integers`** Add support for non-standard integer formats `int8`, `int16`, `uint8`, `uint16`, `uint32`, and `uint64`.
116//! * **`rc_schema`** Add `ToSchema` support for `Arc<T>` and `Rc<T>` types. **Note!** serde `rc` feature flag must be enabled separately to allow
117//!   serialization and deserialization of `Arc<T>` and `Rc<T>` types. See more about [serde feature flags](https://serde.rs/feature-flags.html).
118//! * **`config`** Enables [`utoipa-config`](https://docs.rs/utoipa-config/) for the project which allows
119//!   defining global configuration options for `utoipa`.
120//!
121//! ### Default Library Support
122//!
123//! * Implicit partial support for `serde` attributes. See [`ToSchema` derive][serde] for more details.
124//! * Support for [http](https://crates.io/crates/http) `StatusCode` in responses.
125//!
126//! # Install
127//!
128//! Add dependency declaration to Cargo.toml.
129//! ```toml
130//! [dependencies]
131//! utoipa = "5"
132//! ```
133//!
134//! # Examples
135//!
136//! _**Create type with `ToSchema` and use it in `#[utoipa::path(...)]` that is registered to the `OpenApi`.**_
137//!
138//! ```rust
139//! use utoipa::{OpenApi, ToSchema};
140//!
141//! #[derive(ToSchema)]
142//! struct Pet {
143//!    id: u64,
144//!    name: String,
145//!    age: Option<i32>,
146//! }
147//! # #[derive(Debug)]
148//! # struct NotFound;
149//! #
150//! # impl std::error::Error for NotFound {}
151//! #
152//! # impl std::fmt::Display for NotFound {
153//! #    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154//! #        f.write_str("NotFound")
155//! #    }
156//! # }
157//!
158//! /// Get pet by id
159//! ///
160//! /// Get pet from database by pet id
161//! #[utoipa::path(
162//!     get,
163//!     path = "/pets/{id}",
164//!     responses(
165//!         (status = 200, description = "Pet found successfully", body = Pet),
166//!         (status = NOT_FOUND, description = "Pet was not found")
167//!     ),
168//!     params(
169//!         ("id" = u64, Path, description = "Pet database id to get Pet for"),
170//!     )
171//! )]
172//! async fn get_pet_by_id(pet_id: u64) -> Result<Pet, NotFound> {
173//!     Ok(Pet {
174//!         id: pet_id,
175//!         age: None,
176//!         name: "lightning".to_string(),
177//!     })
178//! }
179//!
180//! #[derive(OpenApi)]
181//! #[openapi(paths(get_pet_by_id))]
182//! struct ApiDoc;
183//!
184//! println!("{}", ApiDoc::openapi().to_pretty_json().unwrap());
185//! ```
186//!
187//! # Modify OpenAPI at runtime
188//!
189//! You can modify generated OpenAPI at runtime either via generated types directly or using
190//! [`Modify`] trait.
191//!
192//! _**Modify generated OpenAPI via types directly.**_
193//! ```rust
194//! # use utoipa::OpenApi;
195//! #[derive(OpenApi)]
196//! #[openapi(
197//!     info(description = "My Api description"),
198//! )]
199//! struct ApiDoc;
200//!
201//! let mut doc = ApiDoc::openapi();
202//! doc.info.title = String::from("My Api");
203//! ```
204//!
205//! _**You can even convert the generated [`OpenApi`] to [`openapi::OpenApiBuilder`].**_
206//! ```rust
207//! # use utoipa::openapi::OpenApiBuilder;
208//! # use utoipa::OpenApi;
209//! #[derive(OpenApi)]
210//! #[openapi(
211//!     info(description = "My Api description"),
212//! )]
213//! struct ApiDoc;
214//!
215//! let builder: OpenApiBuilder = ApiDoc::openapi().into();
216//! ```
217//!
218//! See [`Modify`] trait for examples on how to modify generated OpenAPI via it.
219//!
220//! # Go beyond the surface
221//!
222//! * See how to serve OpenAPI doc via Swagger UI check [`utoipa-swagger-ui`][utoipa_swagger] crate for more details.
223//! * Browse to [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more comprehensive examples.
224//! * Check [`derive@IntoResponses`] and [`derive@ToResponse`] for examples on deriving responses.
225//! * More about OpenAPI security in [security documentation][security].
226//! * Dump generated API doc to file at build time. See [issue 214 comment](https://github.com/juhaku/utoipa/issues/214#issuecomment-1179589373).
227//!
228//! [path]: attr.path.html
229//! [rocket_path]: attr.path.html#rocket_extras-feature-support-for-rocket
230//! [actix_path]: attr.path.html#actix_extras-feature-support-for-actix-web
231//! [axum_path]: attr.path.html#axum_extras-feature-support-for-axum
232//! [serde]: derive.ToSchema.html#partial-serde-attributes-support
233//! [utoipa_swagger]: https://docs.rs/utoipa-swagger-ui/
234//! [utoipa_config]: https://docs.rs/utoipa-config/
235//!
236//! [security]: openapi/security/index.html
237//! [to_schema_derive]: derive.ToSchema.html
238
239pub mod openapi;
240
241#[cfg(feature = "macros")]
242#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
243#[doc(hidden)]
244/// Public re-exports for utoipa-gen.
245pub mod gen;
246
247use std::borrow::Cow;
248use std::collections::BTreeMap;
249use std::option::Option;
250
251#[cfg(feature = "macros")]
252#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
253pub use utoipa_gen::*;
254
255/// Trait for implementing OpenAPI specification in Rust.
256///
257/// This trait is derivable and can be used with `#[derive]` attribute. The derived implementation
258/// will use Cargo provided environment variables to implement the default information. For a details of
259/// `#[derive(ToSchema)]` refer to [derive documentation][derive].
260///
261/// # Examples
262///
263/// Below is derived example of `OpenApi`.
264/// ```rust
265/// use utoipa::OpenApi;
266/// #[derive(OpenApi)]
267/// #[openapi()]
268/// struct OpenApiDoc;
269/// ```
270///
271/// This manual `OpenApi` trait implementation is approximately equal to the above derived one except the derive
272/// implementation will by default use the Cargo environment variables to set defaults for *application name,
273/// version, application description, license, author name & email*.
274///
275/// ```rust
276/// struct OpenApiDoc;
277///
278/// impl utoipa::OpenApi for OpenApiDoc {
279///     fn openapi() -> utoipa::openapi::OpenApi {
280///         use utoipa::{ToSchema, Path};
281///         utoipa::openapi::OpenApiBuilder::new()
282///             .info(utoipa::openapi::InfoBuilder::new()
283///                 .title("application name")
284///                 .version("version")
285///                 .description(Some("application description"))
286///                 .license(Some(utoipa::openapi::License::new("MIT")))
287///                 .contact(
288///                     Some(utoipa::openapi::ContactBuilder::new()
289///                         .name(Some("author name"))
290///                         .email(Some("author email")).build()),
291///             ).build())
292///             .paths(utoipa::openapi::path::Paths::new())
293///             .components(Some(utoipa::openapi::Components::new()))
294///             .build()
295///     }
296/// }
297/// ```
298/// [derive]: derive.OpenApi.html
299pub trait OpenApi {
300    /// Return the [`openapi::OpenApi`] instance which can be parsed with serde or served via
301    /// OpenAPI visualization tool such as Swagger UI.
302    fn openapi() -> openapi::OpenApi;
303}
304
305/// Trait for implementing OpenAPI Schema object.
306///
307/// Generated schemas can be referenced or reused in path operations.
308///
309/// This trait is derivable and can be used with `[#derive]` attribute. For a details of
310/// `#[derive(ToSchema)]` refer to [derive documentation][derive].
311///
312/// [derive]: derive.ToSchema.html
313///
314/// # Examples
315///
316/// Use `#[derive]` to implement `ToSchema` trait.
317/// ```rust
318/// # use utoipa::ToSchema;
319/// #[derive(ToSchema)]
320/// #[schema(example = json!({"name": "bob the cat", "id": 1}))]
321/// struct Pet {
322///     id: u64,
323///     name: String,
324///     age: Option<i32>,
325/// }
326/// ```
327///
328/// Following manual implementation is equal to above derive one.
329/// ```rust
330/// # struct Pet {
331/// #     id: u64,
332/// #     name: String,
333/// #     age: Option<i32>,
334/// # }
335/// #
336/// impl utoipa::ToSchema for Pet {
337///     fn name() -> std::borrow::Cow<'static, str> {
338///         std::borrow::Cow::Borrowed("Pet")
339///     }
340/// }
341/// impl utoipa::PartialSchema for Pet {
342///     fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
343///         utoipa::openapi::ObjectBuilder::new()
344///             .property(
345///                 "id",
346///                 utoipa::openapi::ObjectBuilder::new()
347///                     .schema_type(utoipa::openapi::schema::Type::Integer)
348///                     .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(
349///                         utoipa::openapi::KnownFormat::Int64,
350///                     ))),
351///             )
352///             .required("id")
353///             .property(
354///                 "name",
355///                 utoipa::openapi::ObjectBuilder::new()
356///                     .schema_type(utoipa::openapi::schema::Type::String),
357///             )
358///             .required("name")
359///             .property(
360///                 "age",
361///                 utoipa::openapi::ObjectBuilder::new()
362///                     .schema_type(utoipa::openapi::schema::Type::Integer)
363///                     .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(
364///                         utoipa::openapi::KnownFormat::Int32,
365///                     ))),
366///             )
367///             .example(Some(serde_json::json!({
368///               "name":"bob the cat","id":1
369///             })))
370///             .into()
371///     }
372/// }
373/// ```
374pub trait ToSchema: PartialSchema {
375    /// Return name of the schema.
376    ///
377    /// Name is used by referencing objects to point to this schema object returned with
378    /// [`PartialSchema::schema`] within the OpenAPI document.
379    ///
380    /// In case a generic schema the _`name`_ will be used as prefix for the name in the OpenAPI
381    /// documentation.
382    ///
383    /// The default implementation naively takes the TypeName by removing
384    /// the module path and generic elements.
385    /// But you probably don't want to use the default implementation for generic elements.
386    /// That will produce collision between generics. (eq. `Foo<String>` )
387    ///
388    /// # Example
389    ///
390    /// ```rust
391    /// # use utoipa::ToSchema;
392    /// #
393    /// struct Foo<T>(T);
394    ///
395    /// impl<T: ToSchema> ToSchema for Foo<T> {}
396    /// # impl<T: ToSchema> utoipa::PartialSchema for Foo<T> {
397    /// #     fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
398    /// #         Default::default()
399    /// #     }
400    /// # }
401    ///
402    /// assert_eq!(Foo::<()>::name(), std::borrow::Cow::Borrowed("Foo"));
403    /// assert_eq!(Foo::<()>::name(), Foo::<i32>::name()); // WARNING: these types have the same name
404    /// ```
405    fn name() -> Cow<'static, str> {
406        let full_type_name = std::any::type_name::<Self>();
407        let type_name_without_generic = full_type_name
408            .split_once("<")
409            .map(|(s1, _)| s1)
410            .unwrap_or(full_type_name);
411        let type_name = type_name_without_generic
412            .rsplit_once("::")
413            .map(|(_, tn)| tn)
414            .unwrap_or(type_name_without_generic);
415        Cow::Borrowed(type_name)
416    }
417
418    /// Implement reference [`utoipa::openapi::schema::Schema`]s for this type.
419    ///
420    /// When [`ToSchema`] is being derived this is implemented automatically but if one needs to
421    /// manually implement [`ToSchema`] trait then this is needed for `utoipa` to know
422    /// referencing schemas that need to be present in the resulting OpenAPI spec.
423    ///
424    /// The implementation should push to `schemas` [`Vec`] all such field and variant types that
425    /// implement `ToSchema` and then call `<MyType as ToSchema>::schemas(schemas)` on that type
426    /// to forward the recursive reference collection call on that type.
427    ///
428    /// # Examples
429    ///
430    /// _**Implement `ToSchema` manually with references.**_
431    ///
432    /// ```rust
433    /// # use utoipa::{ToSchema, PartialSchema};
434    /// #
435    /// #[derive(ToSchema)]
436    /// struct Owner {
437    ///     name: String
438    /// }
439    ///
440    /// struct Pet {
441    ///     owner: Owner,
442    ///     name: String
443    /// }
444    /// impl PartialSchema for Pet {
445    ///     fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
446    ///         utoipa::openapi::schema::Object::builder()
447    ///             .property("owner", Owner::schema())
448    ///             .property("name", String::schema())
449    ///             .into()
450    ///     }
451    /// }
452    /// impl ToSchema for Pet {
453    ///     fn schemas(schemas:
454    ///         &mut Vec<(String, utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>)>) {
455    ///         schemas.push((Owner::name().into(), Owner::schema()));
456    ///         <Owner as ToSchema>::schemas(schemas);
457    ///     }
458    /// }
459    /// ```
460    #[allow(unused)]
461    fn schemas(
462        schemas: &mut Vec<(
463            String,
464            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
465        )>,
466    ) {
467        // nothing by default
468    }
469}
470
471impl<T: ToSchema> From<T> for openapi::RefOr<openapi::schema::Schema> {
472    fn from(_: T) -> Self {
473        T::schema()
474    }
475}
476
477/// Represents _`nullable`_ type. This can be used anywhere where "nothing" needs to be evaluated.
478/// This will serialize to _`null`_ in JSON and [`openapi::schema::empty`] is used to create the
479/// [`openapi::schema::Schema`] for the type.
480pub type TupleUnit = ();
481
482impl PartialSchema for TupleUnit {
483    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
484        openapi::schema::empty().into()
485    }
486}
487
488impl ToSchema for TupleUnit {
489    fn name() -> Cow<'static, str> {
490        Cow::Borrowed("TupleUnit")
491    }
492}
493
494macro_rules! impl_to_schema {
495    ( $( $ty:ident ),* ) => {
496        $(
497        impl ToSchema for $ty {
498            fn name() -> std::borrow::Cow<'static, str> {
499                std::borrow::Cow::Borrowed(stringify!( $ty ))
500            }
501        }
502        )*
503    };
504}
505
506#[rustfmt::skip]
507impl_to_schema!(
508    i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, bool, f32, f64, String, str, char
509);
510
511impl ToSchema for &str {
512    fn name() -> Cow<'static, str> {
513        str::name()
514    }
515}
516
517#[cfg(feature = "macros")]
518#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
519impl<T: ToSchema> ToSchema for Option<T>
520where
521    Option<T>: PartialSchema,
522{
523    fn schemas(
524        schemas: &mut Vec<(
525            String,
526            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
527        )>,
528    ) {
529        T::schemas(schemas);
530    }
531}
532
533#[cfg(feature = "macros")]
534#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
535impl<T: ToSchema> ToSchema for Vec<T>
536where
537    Vec<T>: PartialSchema,
538{
539    fn schemas(
540        schemas: &mut Vec<(
541            String,
542            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
543        )>,
544    ) {
545        T::schemas(schemas);
546    }
547}
548
549#[cfg(feature = "macros")]
550#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
551impl<T: ToSchema> ToSchema for std::collections::LinkedList<T>
552where
553    std::collections::LinkedList<T>: PartialSchema,
554{
555    fn schemas(
556        schemas: &mut Vec<(
557            String,
558            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
559        )>,
560    ) {
561        T::schemas(schemas);
562    }
563}
564
565#[cfg(feature = "macros")]
566#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
567impl<T: ToSchema> ToSchema for [T]
568where
569    [T]: PartialSchema,
570{
571    fn schemas(
572        schemas: &mut Vec<(
573            String,
574            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
575        )>,
576    ) {
577        T::schemas(schemas);
578    }
579}
580
581#[cfg(feature = "macros")]
582#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
583impl<'t, T: ToSchema> ToSchema for &'t [T]
584where
585    &'t [T]: PartialSchema,
586{
587    fn schemas(
588        schemas: &mut Vec<(
589            String,
590            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
591        )>,
592    ) {
593        T::schemas(schemas);
594    }
595}
596
597#[cfg(feature = "macros")]
598#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
599impl<'t, T: ToSchema> ToSchema for &'t mut [T]
600where
601    &'t mut [T]: PartialSchema,
602{
603    fn schemas(
604        schemas: &mut Vec<(
605            String,
606            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
607        )>,
608    ) {
609        T::schemas(schemas);
610    }
611}
612
613#[cfg(feature = "macros")]
614#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
615impl<K: ToSchema, T: ToSchema, S> ToSchema for std::collections::HashMap<K, T, S>
616where
617    std::collections::HashMap<K, T, S>: PartialSchema,
618{
619    fn schemas(
620        schemas: &mut Vec<(
621            String,
622            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
623        )>,
624    ) {
625        K::schemas(schemas);
626        T::schemas(schemas);
627    }
628}
629
630#[cfg(feature = "macros")]
631#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
632impl<K: ToSchema, T: ToSchema> ToSchema for std::collections::BTreeMap<K, T>
633where
634    std::collections::BTreeMap<K, T>: PartialSchema,
635{
636    fn schemas(
637        schemas: &mut Vec<(
638            String,
639            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
640        )>,
641    ) {
642        K::schemas(schemas);
643        T::schemas(schemas);
644    }
645}
646
647#[cfg(feature = "macros")]
648#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
649impl<K: ToSchema, S> ToSchema for std::collections::HashSet<K, S>
650where
651    std::collections::HashSet<K, S>: PartialSchema,
652{
653    fn schemas(
654        schemas: &mut Vec<(
655            String,
656            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
657        )>,
658    ) {
659        K::schemas(schemas);
660    }
661}
662
663#[cfg(feature = "macros")]
664#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
665impl<K: ToSchema> ToSchema for std::collections::BTreeSet<K>
666where
667    std::collections::BTreeSet<K>: PartialSchema,
668{
669    fn schemas(
670        schemas: &mut Vec<(
671            String,
672            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
673        )>,
674    ) {
675        K::schemas(schemas);
676    }
677}
678
679#[cfg(all(feature = "macros", feature = "indexmap"))]
680#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "indexmap")))]
681impl<K: ToSchema, T: ToSchema> ToSchema for indexmap::IndexMap<K, T>
682where
683    indexmap::IndexMap<K, T>: PartialSchema,
684{
685    fn schemas(
686        schemas: &mut Vec<(
687            String,
688            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
689        )>,
690    ) {
691        K::schemas(schemas);
692        T::schemas(schemas);
693    }
694}
695
696#[cfg(all(feature = "macros", feature = "indexmap"))]
697#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "indexmap")))]
698impl<K: ToSchema> ToSchema for indexmap::IndexSet<K>
699where
700    indexmap::IndexSet<K>: PartialSchema,
701{
702    fn schemas(
703        schemas: &mut Vec<(
704            String,
705            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
706        )>,
707    ) {
708        K::schemas(schemas);
709    }
710}
711
712#[cfg(feature = "macros")]
713#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
714impl<T: ToSchema> ToSchema for std::boxed::Box<T>
715where
716    std::boxed::Box<T>: PartialSchema,
717{
718    fn schemas(
719        schemas: &mut Vec<(
720            String,
721            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
722        )>,
723    ) {
724        T::schemas(schemas);
725    }
726}
727
728#[cfg(feature = "macros")]
729#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
730impl<'a, T: ToSchema + Clone> ToSchema for std::borrow::Cow<'a, T>
731where
732    std::borrow::Cow<'a, T>: PartialSchema,
733{
734    fn schemas(
735        schemas: &mut Vec<(
736            String,
737            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
738        )>,
739    ) {
740        T::schemas(schemas);
741    }
742}
743
744#[cfg(feature = "macros")]
745#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
746impl<T: ToSchema> ToSchema for std::cell::RefCell<T>
747where
748    std::cell::RefCell<T>: PartialSchema,
749{
750    fn schemas(
751        schemas: &mut Vec<(
752            String,
753            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
754        )>,
755    ) {
756        T::schemas(schemas);
757    }
758}
759
760#[cfg(all(feature = "macros", feature = "rc_schema"))]
761#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "rc_schema")))]
762impl<T: ToSchema> ToSchema for std::rc::Rc<T>
763where
764    std::rc::Rc<T>: PartialSchema,
765{
766    fn schemas(
767        schemas: &mut Vec<(
768            String,
769            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
770        )>,
771    ) {
772        T::schemas(schemas);
773    }
774}
775
776#[cfg(all(feature = "macros", feature = "rc_schema"))]
777#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "rc_schema")))]
778impl<T: ToSchema> ToSchema for std::sync::Arc<T>
779where
780    std::sync::Arc<T>: PartialSchema,
781{
782    fn schemas(
783        schemas: &mut Vec<(
784            String,
785            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
786        )>,
787    ) {
788        T::schemas(schemas);
789    }
790}
791
792impl PartialSchema for serde_json::Value {
793    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
794        utoipa::openapi::schema::Object::builder()
795            .schema_type(utoipa::openapi::schema::SchemaType::AnyValue)
796            .into()
797    }
798}
799
800impl ToSchema for serde_json::Value {}
801
802// Create `utoipa` module so we can use `utoipa-gen` directly from `utoipa` crate.
803// ONLY for internal use!
804#[doc(hidden)]
805#[cfg(feature = "macros")]
806#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
807mod utoipa {
808    pub use super::*;
809}
810
811/// Trait used to implement only _`Schema`_ part of the OpenAPI doc.
812///
813/// This trait is by default implemented for Rust [`primitive`][primitive] types and some well known types like
814/// [`Vec`], [`Option`], [`std::collections::HashMap`] and [`BTreeMap`]. The default implementation adds `schema()`
815/// method to the implementing type allowing simple conversion of the type to the OpenAPI Schema
816/// object. Moreover this allows handy way of constructing schema objects manually if ever so
817/// wished.
818///
819/// The trait can be implemented manually easily on any type. This trait comes especially handy
820/// with [`macro@schema`] macro that can be used to generate schema for arbitrary types.
821/// ```rust
822/// # use utoipa::PartialSchema;
823/// # use utoipa::openapi::schema::{SchemaType, KnownFormat, SchemaFormat, ObjectBuilder, Schema};
824/// # use utoipa::openapi::RefOr;
825/// #
826/// struct MyType;
827///
828/// impl PartialSchema for MyType {
829///     fn schema() -> RefOr<Schema> {
830///         // ... impl schema generation here
831///         RefOr::T(Schema::Object(ObjectBuilder::new().build()))
832///     }
833/// }
834/// ```
835///
836/// # Examples
837///
838/// _**Create number schema from u64.**_
839/// ```rust
840/// # use utoipa::PartialSchema;
841/// # use utoipa::openapi::schema::{Type, KnownFormat, SchemaFormat, ObjectBuilder, Schema};
842/// # use utoipa::openapi::RefOr;
843/// #
844/// let number: RefOr<Schema> = i64::schema().into();
845///
846// // would be equal to manual implementation
847/// let number2 = RefOr::T(
848///     Schema::Object(
849///         ObjectBuilder::new()
850///             .schema_type(Type::Integer)
851///             .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int64)))
852///             .build()
853///         )
854///     );
855/// # assert_eq!(serde_json::to_value(&number).unwrap(), serde_json::to_value(&number2).unwrap());
856/// ```
857///
858/// _**Construct a Pet object schema manually.**_
859/// ```rust
860/// # use utoipa::PartialSchema;
861/// # use utoipa::openapi::schema::ObjectBuilder;
862/// struct Pet {
863///     id: i32,
864///     name: String,
865/// }
866///
867/// let pet_schema = ObjectBuilder::new()
868///     .property("id", i32::schema())
869///     .property("name", String::schema())
870///     .required("id").required("name")
871///     .build();
872/// ```
873///
874/// [primitive]: https://doc.rust-lang.org/std/primitive/index.html
875pub trait PartialSchema {
876    /// Return ref or schema of implementing type that can then be used to
877    /// construct combined schemas.
878    fn schema() -> openapi::RefOr<openapi::schema::Schema>;
879}
880
881/// Trait for implementing OpenAPI PathItem object with path.
882///
883/// This trait is implemented via [`#[utoipa::path(...)]`][derive] attribute macro and there
884/// is no need to implement this trait manually.
885///
886/// # Examples
887///
888/// Use `#[utoipa::path(..)]` to implement Path trait
889/// ```rust
890/// # #[derive(utoipa::ToSchema)]
891/// # struct Pet {
892/// #   id: u64,
893/// #   name: String,
894/// # }
895/// #
896/// #
897/// /// Get pet by id
898/// ///
899/// /// Get pet from database by pet database id
900/// #[utoipa::path(
901///     get,
902///     path = "/pets/{id}",
903///     responses(
904///         (status = 200, description = "Pet found successfully", body = Pet),
905///         (status = 404, description = "Pet was not found")
906///     ),
907///     params(
908///         ("id" = u64, Path, description = "Pet database id to get Pet for"),
909///     )
910/// )]
911/// async fn get_pet_by_id(pet_id: u64) -> Pet {
912///     Pet {
913///         id: pet_id,
914///         name: "lightning".to_string(),
915///     }
916/// }
917/// ```
918///
919/// Example of what would manual implementation roughly look like of above `#[utoipa::path(...)]` macro.
920/// ```rust
921/// utoipa::openapi::PathsBuilder::new().path(
922///         "/pets/{id}",
923///         utoipa::openapi::PathItem::new(
924///             utoipa::openapi::HttpMethod::Get,
925///             utoipa::openapi::path::OperationBuilder::new()
926///                 .responses(
927///                     utoipa::openapi::ResponsesBuilder::new()
928///                         .response(
929///                             "200",
930///                             utoipa::openapi::ResponseBuilder::new()
931///                                 .description("Pet found successfully")
932///                                 .content("application/json",
933///                                     utoipa::openapi::Content::new(
934///                                         Some(utoipa::openapi::Ref::from_schema_name("Pet")),
935///                                     ),
936///                             ),
937///                         )
938///                         .response("404", utoipa::openapi::Response::new("Pet was not found")),
939///                 )
940///                 .operation_id(Some("get_pet_by_id"))
941///                 .deprecated(Some(utoipa::openapi::Deprecated::False))
942///                 .summary(Some("Get pet by id"))
943///                 .description(Some("Get pet by id\n\nGet pet from database by pet database id\n"))
944///                 .parameter(
945///                     utoipa::openapi::path::ParameterBuilder::new()
946///                         .name("id")
947///                         .parameter_in(utoipa::openapi::path::ParameterIn::Path)
948///                         .required(utoipa::openapi::Required::True)
949///                         .deprecated(Some(utoipa::openapi::Deprecated::False))
950///                         .description(Some("Pet database id to get Pet for"))
951///                         .schema(
952///                             Some(utoipa::openapi::ObjectBuilder::new()
953///                                 .schema_type(utoipa::openapi::schema::Type::Integer)
954///                                 .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int64)))),
955///                         ),
956///                 )
957///                 .tag("pet_api"),
958///         ),
959///     );
960/// ```
961///
962/// [derive]: attr.path.html
963pub trait Path {
964    /// List of HTTP methods this path operation is served at.
965    fn methods() -> Vec<openapi::path::HttpMethod>;
966
967    /// The path this operation is served at.
968    fn path() -> String;
969
970    /// [`openapi::path::Operation`] describing http operation details such as request bodies,
971    /// parameters and responses.
972    fn operation() -> openapi::path::Operation;
973}
974
975/// Trait that allows OpenApi modification at runtime.
976///
977/// Implement this trait if you wish to modify the OpenApi at runtime before it is being consumed
978/// *(Before `utoipa::OpenApi::openapi()` function returns)*.
979/// This is trait can be used to add or change already generated OpenApi spec to alter the generated
980/// specification by user defined condition. For example you can add definitions that should be loaded
981/// from some configuration at runtime what may not be available during compile time.
982///
983/// See more about [`OpenApi`][derive] derive at [derive documentation][derive].
984///
985/// [derive]: derive.OpenApi.html
986/// [security_scheme]: openapi/security/enum.SecurityScheme.html
987///
988/// # Examples
989///
990/// Add custom JWT [`SecurityScheme`][security_scheme] to [`OpenApi`][`openapi::OpenApi`].
991/// ```rust
992/// # use utoipa::{OpenApi, Modify};
993/// # use utoipa::openapi::security::{SecurityScheme, HttpBuilder, HttpAuthScheme};
994/// #[derive(OpenApi)]
995/// #[openapi(modifiers(&SecurityAddon))]
996/// struct ApiDoc;
997///
998/// struct SecurityAddon;
999///
1000/// impl Modify for SecurityAddon {
1001///     fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
1002///          openapi.components = Some(
1003///              utoipa::openapi::ComponentsBuilder::new()
1004///                  .security_scheme(
1005///                      "api_jwt_token",
1006///                      SecurityScheme::Http(
1007///                          HttpBuilder::new()
1008///                              .scheme(HttpAuthScheme::Bearer)
1009///                              .bearer_format("JWT")
1010///                              .build(),
1011///                      ),
1012///                  )
1013///                  .build(),
1014///          )
1015///      }
1016/// }
1017/// ```
1018///
1019/// Add [OpenAPI Server Object][server] to alter the target server url. This can be used to give context
1020/// path for api operations.
1021/// ```rust
1022/// # use utoipa::{OpenApi, Modify};
1023/// # use utoipa::openapi::Server;
1024/// #[derive(OpenApi)]
1025/// #[openapi(modifiers(&ServerAddon))]
1026/// struct ApiDoc;
1027///
1028/// struct ServerAddon;
1029///
1030/// impl Modify for ServerAddon {
1031///     fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
1032///         openapi.servers = Some(vec![Server::new("/api")])
1033///     }
1034/// }
1035/// ```
1036///
1037/// [server]: https://spec.openapis.org/oas/latest.html#server-object
1038pub trait Modify {
1039    /// Apply mutation for [`openapi::OpenApi`] instance before it is returned by
1040    /// [`openapi::OpenApi::openapi`] method call.
1041    ///
1042    /// This function allows users to run arbitrary code to change the generated
1043    /// [`utoipa::OpenApi`] before it is served.
1044    fn modify(&self, openapi: &mut openapi::OpenApi);
1045}
1046
1047/// Trait used to convert implementing type to OpenAPI parameters.
1048///
1049/// This trait is [derivable][derive] for structs which are used to describe `path` or `query` parameters.
1050/// For more details of `#[derive(IntoParams)]` refer to [derive documentation][derive].
1051///
1052/// # Examples
1053///
1054/// Derive [`IntoParams`] implementation. This example will fail to compile because [`IntoParams`] cannot
1055/// be used alone and it need to be used together with endpoint using the params as well. See
1056/// [derive documentation][derive] for more details.
1057/// ```
1058/// use utoipa::{IntoParams};
1059///
1060/// #[derive(IntoParams)]
1061/// struct PetParams {
1062///     /// Id of pet
1063///     id: i64,
1064///     /// Name of pet
1065///     name: String,
1066/// }
1067/// ```
1068///
1069/// Roughly equal manual implementation of [`IntoParams`] trait.
1070/// ```rust
1071/// # struct PetParams {
1072/// #    /// Id of pet
1073/// #    id: i64,
1074/// #    /// Name of pet
1075/// #    name: String,
1076/// # }
1077/// impl utoipa::IntoParams for PetParams {
1078///     fn into_params(
1079///         parameter_in_provider: impl Fn() -> Option<utoipa::openapi::path::ParameterIn>
1080///     ) -> Vec<utoipa::openapi::path::Parameter> {
1081///         vec![
1082///             utoipa::openapi::path::ParameterBuilder::new()
1083///                 .name("id")
1084///                 .required(utoipa::openapi::Required::True)
1085///                 .parameter_in(parameter_in_provider().unwrap_or_default())
1086///                 .description(Some("Id of pet"))
1087///                 .schema(Some(
1088///                     utoipa::openapi::ObjectBuilder::new()
1089///                         .schema_type(utoipa::openapi::schema::Type::Integer)
1090///                         .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int64))),
1091///                 ))
1092///                 .build(),
1093///             utoipa::openapi::path::ParameterBuilder::new()
1094///                 .name("name")
1095///                 .required(utoipa::openapi::Required::True)
1096///                 .parameter_in(parameter_in_provider().unwrap_or_default())
1097///                 .description(Some("Name of pet"))
1098///                 .schema(Some(
1099///                     utoipa::openapi::ObjectBuilder::new()
1100///                         .schema_type(utoipa::openapi::schema::Type::String),
1101///                 ))
1102///                 .build(),
1103///         ]
1104///     }
1105/// }
1106/// ```
1107/// [derive]: derive.IntoParams.html
1108pub trait IntoParams {
1109    /// Provide [`Vec`] of [`openapi::path::Parameter`]s to caller. The result is used in `utoipa-gen` library to
1110    /// provide OpenAPI parameter information for the endpoint using the parameters.
1111    fn into_params(
1112        parameter_in_provider: impl Fn() -> Option<openapi::path::ParameterIn>,
1113    ) -> Vec<openapi::path::Parameter>;
1114}
1115
1116/// This trait is implemented to document a type (like an enum) which can represent multiple
1117/// responses, to be used in operation.
1118///
1119/// # Examples
1120///
1121/// ```
1122/// use std::collections::BTreeMap;
1123/// use utoipa::{
1124///     openapi::{Response, ResponseBuilder, ResponsesBuilder, RefOr},
1125///     IntoResponses,
1126/// };
1127///
1128/// enum MyResponse {
1129///     Ok,
1130///     NotFound,
1131/// }
1132///
1133/// impl IntoResponses for MyResponse {
1134///     fn responses() -> BTreeMap<String, RefOr<Response>> {
1135///         ResponsesBuilder::new()
1136///             .response("200", ResponseBuilder::new().description("Ok"))
1137///             .response("404", ResponseBuilder::new().description("Not Found"))
1138///             .build()
1139///             .into()
1140///     }
1141/// }
1142/// ```
1143pub trait IntoResponses {
1144    /// Returns an ordered map of response codes to responses.
1145    fn responses() -> BTreeMap<String, openapi::RefOr<openapi::response::Response>>;
1146}
1147
1148#[cfg(feature = "auto_into_responses")]
1149impl<T: IntoResponses, E: IntoResponses> IntoResponses for Result<T, E> {
1150    fn responses() -> BTreeMap<String, openapi::RefOr<openapi::response::Response>> {
1151        let mut responses = T::responses();
1152        responses.append(&mut E::responses());
1153
1154        responses
1155    }
1156}
1157
1158#[cfg(feature = "auto_into_responses")]
1159impl IntoResponses for () {
1160    fn responses() -> BTreeMap<String, openapi::RefOr<openapi::response::Response>> {
1161        BTreeMap::new()
1162    }
1163}
1164
1165/// This trait is implemented to document a type which represents a single response which can be
1166/// referenced or reused as a component in multiple operations.
1167///
1168/// _`ToResponse`_ trait can also be derived with [`#[derive(ToResponse)]`][derive].
1169///
1170/// # Examples
1171///
1172/// ```
1173/// use utoipa::{
1174///     openapi::{RefOr, Response, ResponseBuilder},
1175///     ToResponse,
1176/// };
1177///
1178/// struct MyResponse;
1179///
1180/// impl<'__r> ToResponse<'__r> for MyResponse {
1181///     fn response() -> (&'__r str, RefOr<Response>) {
1182///         (
1183///             "MyResponse",
1184///             ResponseBuilder::new().description("My Response").build().into(),
1185///         )
1186///     }
1187/// }
1188/// ```
1189///
1190/// [derive]: derive.ToResponse.html
1191pub trait ToResponse<'__r> {
1192    /// Returns a tuple of response component name (to be referenced) to a response.
1193    fn response() -> (&'__r str, openapi::RefOr<openapi::response::Response>);
1194}
1195
1196/// Flexible number wrapper used by validation schema attributes to seamlessly support different
1197/// number syntaxes.
1198///
1199/// # Examples
1200///
1201/// _**Define object with two different number fields with minimum validation attribute.**_
1202///
1203/// ```rust
1204/// # use utoipa::Number;
1205/// # use utoipa::openapi::schema::{ObjectBuilder, SchemaType, Type};
1206/// let _ = ObjectBuilder::new()
1207///             .property("int_value", ObjectBuilder::new()
1208///                 .schema_type(Type::Integer).minimum(Some(1))
1209///             )
1210///             .property("float_value", ObjectBuilder::new()
1211///                 .schema_type(Type::Number).minimum(Some(-2.5))
1212///             )
1213///             .build();
1214/// ```
1215#[derive(Clone, serde::Deserialize, serde::Serialize)]
1216#[cfg_attr(feature = "debug", derive(Debug))]
1217#[serde(untagged)]
1218pub enum Number {
1219    /// Signed integer e.g. `1` or `-2`
1220    Int(isize),
1221    /// Unsigned integer value e.g. `0`. Unsigned integer cannot be below zero.
1222    UInt(usize),
1223    /// Floating point number e.g. `1.34`
1224    Float(f64),
1225}
1226
1227impl Eq for Number {}
1228
1229impl PartialEq for Number {
1230    fn eq(&self, other: &Self) -> bool {
1231        match (self, other) {
1232            (Self::Int(left), Self::Int(right)) => left == right,
1233            (Self::UInt(left), Self::UInt(right)) => left == right,
1234            (Self::Float(left), Self::Float(right)) => left == right,
1235            _ => false,
1236        }
1237    }
1238}
1239
1240macro_rules! impl_from_for_number {
1241    ( $( $ty:ident => $pat:ident $( as $as:ident )? ),* ) => {
1242        $(
1243        impl From<$ty> for Number {
1244            fn from(value: $ty) -> Self {
1245                Self::$pat(value $( as $as )?)
1246            }
1247        }
1248        )*
1249    };
1250}
1251
1252#[rustfmt::skip]
1253impl_from_for_number!(
1254    f32 => Float as f64, f64 => Float,
1255    i8 => Int as isize, i16 => Int as isize, i32 => Int as isize, i64 => Int as isize,
1256    u8 => UInt as usize, u16 => UInt as usize, u32 => UInt as usize, u64 => UInt as usize,
1257    isize => Int, usize => UInt
1258);
1259
1260/// Internal dev module used internally by utoipa-gen
1261#[doc(hidden)]
1262#[cfg(feature = "macros")]
1263#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
1264pub mod __dev {
1265    use utoipa_gen::schema;
1266
1267    use crate::{utoipa, OpenApi, PartialSchema};
1268
1269    pub trait PathConfig {
1270        fn path() -> String;
1271
1272        fn methods() -> Vec<crate::openapi::path::HttpMethod>;
1273
1274        fn tags_and_operation() -> (Vec<&'static str>, utoipa::openapi::path::Operation);
1275    }
1276
1277    pub trait Tags<'t> {
1278        fn tags() -> Vec<&'t str>;
1279    }
1280
1281    impl<T: PathConfig> utoipa::Path for T {
1282        fn path() -> String {
1283            <Self as PathConfig>::path()
1284        }
1285
1286        fn methods() -> Vec<crate::openapi::path::HttpMethod> {
1287            <Self as PathConfig>::methods()
1288        }
1289
1290        fn operation() -> crate::openapi::path::Operation {
1291            let (tags, mut operation) = <Self as PathConfig>::tags_and_operation();
1292
1293            let operation_tags = operation.tags.get_or_insert(Vec::new());
1294            operation_tags.extend(tags.iter().map(ToString::to_string));
1295
1296            operation
1297        }
1298    }
1299
1300    pub trait NestedApiConfig {
1301        fn config() -> (utoipa::openapi::OpenApi, Vec<&'static str>, &'static str);
1302    }
1303
1304    impl<T: NestedApiConfig> OpenApi for T {
1305        fn openapi() -> crate::openapi::OpenApi {
1306            let (mut api, tags, module_path) = T::config();
1307
1308            api.paths.paths.iter_mut().for_each(|(_, path_item)| {
1309                let update_tags = |operation: Option<&mut crate::openapi::path::Operation>| {
1310                    if let Some(operation) = operation {
1311                        let operation_tags = operation.tags.get_or_insert(Vec::new());
1312                        operation_tags.extend(tags.iter().map(ToString::to_string));
1313                        if operation_tags.is_empty() && !module_path.is_empty() {
1314                            operation_tags.push(module_path.to_string());
1315                        }
1316                    }
1317                };
1318
1319                update_tags(path_item.get.as_mut());
1320                update_tags(path_item.put.as_mut());
1321                update_tags(path_item.post.as_mut());
1322                update_tags(path_item.delete.as_mut());
1323                update_tags(path_item.options.as_mut());
1324                update_tags(path_item.head.as_mut());
1325                update_tags(path_item.patch.as_mut());
1326                update_tags(path_item.trace.as_mut());
1327            });
1328
1329            api
1330        }
1331    }
1332
1333    pub trait ComposeSchema {
1334        fn compose(
1335            new_generics: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1336        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>;
1337    }
1338
1339    macro_rules! impl_compose_schema {
1340        ( $( $ty:ident ),* ) => {
1341            $(
1342            impl ComposeSchema for $ty {
1343                fn compose(_: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1344                    schema!( $ty ).into()
1345                }
1346            }
1347            )*
1348        };
1349    }
1350
1351    #[rustfmt::skip]
1352    impl_compose_schema!(
1353        i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, bool, f32, f64, String, str, char
1354    );
1355
1356    fn schema_or_compose<T: ComposeSchema>(
1357        schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1358        index: usize,
1359    ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1360        if let Some(schema) = schemas.get(index) {
1361            schema.clone()
1362        } else {
1363            T::compose(schemas)
1364        }
1365    }
1366
1367    impl ComposeSchema for &str {
1368        fn compose(
1369            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1370        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1371            str::compose(schemas)
1372        }
1373    }
1374
1375    impl<T: ComposeSchema + ?Sized> PartialSchema for T {
1376        fn schema() -> crate::openapi::RefOr<crate::openapi::schema::Schema> {
1377            T::compose(Vec::new())
1378        }
1379    }
1380    impl<T: ComposeSchema> ComposeSchema for Option<T> {
1381        fn compose(
1382            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1383        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1384            utoipa::openapi::schema::OneOfBuilder::new()
1385                .item(
1386                    utoipa::openapi::schema::ObjectBuilder::new()
1387                        .schema_type(utoipa::openapi::schema::Type::Null),
1388                )
1389                .item(schema_or_compose::<T>(schemas, 0))
1390                .into()
1391        }
1392    }
1393
1394    impl<T: ComposeSchema> ComposeSchema for Vec<T> {
1395        fn compose(
1396            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1397        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1398            utoipa::openapi::schema::ArrayBuilder::new()
1399                .items(schema_or_compose::<T>(schemas, 0))
1400                .into()
1401        }
1402    }
1403
1404    impl<T: ComposeSchema> ComposeSchema for std::collections::LinkedList<T> {
1405        fn compose(
1406            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1407        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1408            utoipa::openapi::schema::ArrayBuilder::new()
1409                .items(schema_or_compose::<T>(schemas, 0))
1410                .into()
1411        }
1412    }
1413
1414    impl<T: ComposeSchema> ComposeSchema for [T] {
1415        fn compose(
1416            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1417        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1418            utoipa::openapi::schema::ArrayBuilder::new()
1419                .items(schema_or_compose::<T>(schemas, 0))
1420                .into()
1421        }
1422    }
1423
1424    impl<T: ComposeSchema> ComposeSchema for &[T] {
1425        fn compose(
1426            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1427        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1428            utoipa::openapi::schema::ArrayBuilder::new()
1429                .items(schema_or_compose::<T>(schemas, 0))
1430                .into()
1431        }
1432    }
1433
1434    impl<T: ComposeSchema> ComposeSchema for &mut [T] {
1435        fn compose(
1436            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1437        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1438            utoipa::openapi::schema::ArrayBuilder::new()
1439                .items(schema_or_compose::<T>(schemas, 0))
1440                .into()
1441        }
1442    }
1443
1444    impl<K: ComposeSchema, T: ComposeSchema, S> ComposeSchema for std::collections::HashMap<K, T, S> {
1445        fn compose(
1446            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1447        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1448            utoipa::openapi::ObjectBuilder::new()
1449                .property_names(Some(schema_or_compose::<K>(schemas.clone(), 0)))
1450                .additional_properties(Some(schema_or_compose::<T>(schemas, 1)))
1451                .into()
1452        }
1453    }
1454
1455    impl<K: ComposeSchema, T: ComposeSchema> ComposeSchema for std::collections::BTreeMap<K, T> {
1456        fn compose(
1457            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1458        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1459            utoipa::openapi::ObjectBuilder::new()
1460                .property_names(Some(schema_or_compose::<K>(schemas.clone(), 0)))
1461                .additional_properties(Some(schema_or_compose::<T>(schemas, 1)))
1462                .into()
1463        }
1464    }
1465
1466    impl<K: ComposeSchema, S> ComposeSchema for std::collections::HashSet<K, S> {
1467        fn compose(
1468            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1469        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1470            utoipa::openapi::schema::ArrayBuilder::new()
1471                .items(schema_or_compose::<K>(schemas, 0))
1472                .unique_items(true)
1473                .into()
1474        }
1475    }
1476
1477    impl<K: ComposeSchema> ComposeSchema for std::collections::BTreeSet<K> {
1478        fn compose(
1479            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1480        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1481            utoipa::openapi::schema::ArrayBuilder::new()
1482                .items(schema_or_compose::<K>(schemas, 0))
1483                .unique_items(true)
1484                .into()
1485        }
1486    }
1487
1488    #[cfg(feature = "indexmap")]
1489    #[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "indexmap")))]
1490    impl<K: ComposeSchema, T: ComposeSchema> ComposeSchema for indexmap::IndexMap<K, T> {
1491        fn compose(
1492            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1493        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1494            utoipa::openapi::ObjectBuilder::new()
1495                .property_names(Some(schema_or_compose::<K>(schemas.clone(), 0)))
1496                .additional_properties(Some(schema_or_compose::<T>(schemas, 1)))
1497                .into()
1498        }
1499    }
1500
1501    #[cfg(feature = "indexmap")]
1502    #[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "indexmap")))]
1503    impl<K: ComposeSchema> ComposeSchema for indexmap::IndexSet<K> {
1504        fn compose(
1505            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1506        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1507            utoipa::openapi::schema::ArrayBuilder::new()
1508                .items(schema_or_compose::<K>(schemas, 0))
1509                .unique_items(true)
1510                .into()
1511        }
1512    }
1513
1514    impl<'a, T: ComposeSchema + Clone> ComposeSchema for std::borrow::Cow<'a, T> {
1515        fn compose(
1516            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1517        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1518            schema_or_compose::<T>(schemas, 0)
1519        }
1520    }
1521
1522    impl<T: ComposeSchema> ComposeSchema for std::boxed::Box<T> {
1523        fn compose(
1524            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1525        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1526            schema_or_compose::<T>(schemas, 0)
1527        }
1528    }
1529
1530    impl<T: ComposeSchema> ComposeSchema for std::cell::RefCell<T> {
1531        fn compose(
1532            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1533        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1534            schema_or_compose::<T>(schemas, 0)
1535        }
1536    }
1537
1538    #[cfg(feature = "rc_schema")]
1539    #[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "rc_schema")))]
1540    impl<T: ComposeSchema> ComposeSchema for std::rc::Rc<T> {
1541        fn compose(
1542            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1543        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1544            schema_or_compose::<T>(schemas, 0)
1545        }
1546    }
1547
1548    #[cfg(feature = "rc_schema")]
1549    #[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "rc_schema")))]
1550    impl<T: ComposeSchema> ComposeSchema for std::sync::Arc<T> {
1551        fn compose(
1552            schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
1553        ) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1554            schema_or_compose::<T>(schemas, 0)
1555        }
1556    }
1557
1558    // For types not implementing `ToSchema`
1559    pub trait SchemaReferences {
1560        fn schemas(
1561            schemas: &mut Vec<(
1562                String,
1563                utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
1564            )>,
1565        );
1566    }
1567}
1568
1569#[cfg(test)]
1570mod tests {
1571    use insta::assert_compact_json_snapshot;
1572    use serde_json::json;
1573
1574    use super::*;
1575
1576    #[test]
1577    fn test_toschema_name() {
1578        struct Foo;
1579        impl ToSchema for Foo {}
1580        impl PartialSchema for Foo {
1581            fn schema() -> openapi::RefOr<openapi::schema::Schema> {
1582                Default::default()
1583            }
1584        }
1585        assert_eq!(Foo::name(), Cow::Borrowed("Foo"));
1586
1587        struct FooGeneric<T: ToSchema, U: ToSchema>(T, U);
1588        impl<T: ToSchema, U: ToSchema> ToSchema for FooGeneric<T, U> {}
1589        impl<T: ToSchema, U: ToSchema> PartialSchema for FooGeneric<T, U> {
1590            fn schema() -> openapi::RefOr<openapi::schema::Schema> {
1591                Default::default()
1592            }
1593        }
1594        assert_eq!(
1595            FooGeneric::<Foo, String>::name(),
1596            Cow::Borrowed("FooGeneric")
1597        );
1598        assert_eq!(
1599            FooGeneric::<Foo, String>::name(),
1600            FooGeneric::<(), ()>::name(),
1601        );
1602    }
1603
1604    #[cfg(not(feature = "non_strict_integers"))]
1605    #[test]
1606    fn test_partial_schema_strict_integers() {
1607        assert_compact_json_snapshot!(i8::schema(), @r#"{"type": "integer", "format": "int32"}"#);
1608        assert_compact_json_snapshot!(i16::schema(), @r#"{"type": "integer", "format": "int32"}"#);
1609        assert_compact_json_snapshot!(i32::schema(), @r#"{"type": "integer", "format": "int32"}"#);
1610        assert_compact_json_snapshot!(i64::schema(), @r#"{"type": "integer", "format": "int64"}"#);
1611        assert_compact_json_snapshot!(i128::schema(), @r#"{"type": "integer"}"#);
1612        assert_compact_json_snapshot!(isize::schema(), @r#"{"type": "integer"}"#);
1613        assert_compact_json_snapshot!(u8::schema(), @r#"{"type": "integer", "format": "int32", "minimum": 0}"#);
1614        assert_compact_json_snapshot!(u16::schema(), @r#"{"type": "integer", "format": "int32", "minimum": 0}"#);
1615        assert_compact_json_snapshot!(u32::schema(), @r#"{"type": "integer", "format": "int32", "minimum": 0}"#);
1616        assert_compact_json_snapshot!(u64::schema(), @r#"{"type": "integer", "format": "int64", "minimum": 0}"#);
1617    }
1618
1619    #[cfg(feature = "non_strict_integers")]
1620    #[test]
1621    fn test_partial_schema_non_strict_integers() {
1622        assert_compact_json_snapshot!(i8::schema(), @r#"{"type": "integer", "format": "int8"}"#);
1623        assert_compact_json_snapshot!(i16::schema(), @r#"{"type": "integer", "format": "int16"}"#);
1624        assert_compact_json_snapshot!(i32::schema(), @r#"{"type": "integer", "format": "int32"}"#);
1625        assert_compact_json_snapshot!(i64::schema(), @r#"{"type": "integer", "format": "int64"}"#);
1626        assert_compact_json_snapshot!(i128::schema(), @r#"{"type": "integer"}"#);
1627        assert_compact_json_snapshot!(isize::schema(), @r#"{"type": "integer"}"#);
1628        assert_compact_json_snapshot!(u8::schema(), @r#"{"type": "integer", "format": "uint8", "minimum": 0}"#);
1629        assert_compact_json_snapshot!(u16::schema(), @r#"{"type": "integer", "format": "uint16", "minimum": 0}"#);
1630        assert_compact_json_snapshot!(u32::schema(), @r#"{"type": "integer", "format": "int32", "minimum": 0}"#);
1631        assert_compact_json_snapshot!(u64::schema(), @r#"{"type": "integer", "format": "int64", "minimum": 0}"#);
1632    }
1633
1634    #[test]
1635    fn test_partial_schema() {
1636        for (name, schema, value) in [
1637            ("bool", bool::schema(), json!({"type": "boolean"})),
1638            ("str", str::schema(), json!({"type": "string"})),
1639            ("String", String::schema(), json!({"type": "string"})),
1640            ("char", char::schema(), json!({"type": "string"})),
1641            (
1642                "f32",
1643                f32::schema(),
1644                json!({"type": "number", "format": "float"}),
1645            ),
1646            (
1647                "f64",
1648                f64::schema(),
1649                json!({"type": "number", "format": "double"}),
1650            ),
1651        ] {
1652            println!(
1653                "{name}: {json}",
1654                json = serde_json::to_string(&schema).unwrap()
1655            );
1656            let schema = serde_json::to_value(schema).unwrap();
1657            assert_eq!(schema, value);
1658        }
1659    }
1660}