Skip to main content

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