utoipa/
lib.rs

1#![cfg_attr(doc_cfg, feature(doc_cfg))]
2//! Want to have your API documented with OpenAPI? But you don't want to see the
3//! trouble with manual yaml or json tweaking? Would like it to be so easy that it would almost
4//! be like utopic? Don't worry utoipa is just there to fill this gap. It aims to do if not all then
5//! the most of heavy lifting for you enabling you to focus writing the actual API logic instead of
6//! documentation. It aims to be *minimal*, *simple* and *fast*. It uses simple proc macros which
7//! you can use to annotate your code to have items documented.
8//!
9//! Utoipa crate provides autogenerated OpenAPI documentation for Rust REST APIs. It treats
10//! code first approach as a first class citizen and simplifies API documentation by providing
11//! simple macros for generating the documentation from your code.
12//!
13//! It also contains Rust types of OpenAPI spec allowing you to write the OpenAPI spec only using
14//! Rust if auto-generation is not your flavor or does not fit your purpose.
15//!
16//! Long term goal of the library is to be the place to go when OpenAPI documentation is needed in Rust
17//! codebase.
18//!
19//! Utoipa is framework agnostic and could be used together with any web framework or even without one. While
20//! being portable and standalone one of it's key aspects is simple integration with web frameworks.
21//!
22//! Currently utoipa provides simple integration with actix-web framework but is not limited to the actix-web
23//! framework. All functionalities are not restricted to any specific framework.
24//!
25//! # Choose your flavor and document your API with ice cold IPA
26//!
27//! Existing [examples](https://github.com/juhaku/utoipa/tree/master/examples) for following frameworks:
28//!
29//! * **actix-web**
30//! * **axum**
31//! * **warp**
32//! * **tide**
33//! * **rocket**
34//!
35//! Even if there is no example for your favorite framework `utoipa` can be used with any
36//! web framework which supports decorating functions with macros similarly to **warp** and **tide** examples.
37//!
38//! # What's up with the word play?
39//!
40//! The name comes from words `utopic` and `api` where `uto` is the first three letters of _utopic_
41//! and the `ipa` is _api_ reversed. Aaand... `ipa` is also awesome type of beer.
42//!
43//! # Crate Features
44//!
45//! * **yaml** Enables **serde_yaml** serialization of OpenAPI objects.
46//! * **actix_extras** Enhances [actix-web](https://github.com/actix/actix-web/) integration with being able to
47//!   parse `path`, `path` and `query` parameters from actix web path attribute macros. See [actix extras support][actix_path] or
48//!   [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more details.
49//! * **rocket_extras** Enhances [rocket](https://github.com/SergioBenitez/Rocket) framework integration with being
50//!   able to parse `path`, `path` and `query` parameters from rocket path attribute macros. See [rocket extras support][rocket_path]
51//!   or [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more details
52//! * **axum_extras** Enhances [axum](https://github.com/tokio-rs/axum) framework integration allowing users to use `IntoParams`
53//!   without defining the `parameter_in` attribute. See [axum extras support][axum_path]
54//!   or [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more details.
55//! * **debug** Add extra traits such as debug traits to openapi definitions and elsewhere.
56//! * **chrono** Add support for [chrono](https://crates.io/crates/chrono) `DateTime`, `Date`, `NaiveDate`, `NaiveTime` and `Duration`
57//!   types. By default these types are parsed to `string` types with additional `format` information.
58//!   `format: date-time` for `DateTime` and `format: date` for `Date` and `NaiveDate` according
59//!   [RFC3339](https://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14) as `ISO-8601`. To
60//!   override default `string` representation users have to use `value_type` attribute to override the type.
61//!   See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html) for more details.
62//! * **time** Add support for [time](https://crates.io/crates/time) `OffsetDateTime`, `PrimitiveDateTime`, `Date`, and `Duration` types.
63//!   By default these types are parsed as `string`. `OffsetDateTime` and `PrimitiveDateTime` will use `date-time` format. `Date` will use
64//!   `date` format and `Duration` will not have any format. To override default `string` representation users have to use `value_type` attribute
65//!   to override the type. See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html) for more details.
66//! * **decimal** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default**
67//!   it is interpreted as `String`. If you wish to change the format you need to override the type.
68//!   See the `value_type` in [`ToSchema` derive docs][to_schema_derive].
69//! * **uuid** Add support for [uuid](https://github.com/uuid-rs/uuid). `Uuid` type will be presented as `String` with
70//!   format `uuid` in OpenAPI spec.
71//! * **ulid** Add support for [ulid](https://github.com/dylanhart/ulid-rs). `Ulid` type will be presented as `String` with
72//!   format `ulid` in OpenAPI spec.
73//! * **smallvec** Add support for [smallvec](https://crates.io/crates/smallvec). `SmallVec` will be treated as `Vec`.
74//! * **openapi_extensions** Adds convenience functions for documenting common scenarios, such as JSON request bodies and responses.
75//!   See the [`request_body`](https://docs.rs/utoipa/latest/utoipa/openapi/request_body/index.html) and
76//!   [`response`](https://docs.rs/utoipa/latest/utoipa/openapi/response/index.html) docs for examples.
77//! * **repr** Add support for [repr_serde](https://github.com/dtolnay/serde-repr)'s `repr(u*)` and `repr(i*)` attributes to unit type enums for
78//!   C-like enum representation. See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html) for more details.
79//! * **preserve_order** Preserve order of properties when serializing the schema for a component.
80//!   When enabled, the properties are listed in order of fields in the corresponding struct definition.
81//!   When disabled, the properties are listed in alphabetical order.
82//! * **preserve_path_order** Preserve order of OpenAPI Paths according to order they have been
83//!   introduced to the `#[openapi(paths(...))]` macro attribute. If disabled the paths will be
84//!   ordered in alphabetical order.
85//! * **indexmap** Add support for [indexmap](https://crates.io/crates/indexmap). When enabled `IndexMap` will be rendered as a map similar to
86//!   `BTreeMap` and `HashMap`.
87//! * **non_strict_integers** Add support for non-standard integer formats `int8`, `int16`, `uint8`, `uint16`, `uint32`, and `uint64`.
88//! * **rc_schema** Add `ToSchema` support for `Arc<T>` and `Rc<T>` types. **Note!** serde `rc` feature flag must be enabled separately to allow
89//!   serialization and deserialization of `Arc<T>` and `Rc<T>` types. See more about [serde feature flags](https://serde.rs/feature-flags.html).
90//!
91//! Utoipa implicitly has partial support for `serde` attributes. See [`ToSchema` derive][serde] for more details.
92//!
93//! # Install
94//!
95//! Add minimal dependency declaration to Cargo.toml.
96//! ```toml
97//! [dependencies]
98//! utoipa = "3"
99//! ```
100//!
101//! To enable more features such as use actix framework extras you could define the
102//! dependency as follows.
103//! ```toml
104//! [dependencies]
105//! utoipa = { version = "3", features = ["actix_extras"] }
106//! ```
107//!
108//! **Note!** To use `utoipa` together with Swagger UI you can use the [`utoipa-swagger-ui`][utoipa_swagger] crate.
109//!
110//! [utoipa_swagger]: <https://docs.rs/utoipa-swagger-ui/>
111//!
112//! # Examples
113//!
114//! Create a struct, or it could be an enum also. Add `ToSchema` derive macro to it so it can be registered
115//! as a component in openapi schema.
116//! ```rust
117//! use utoipa::ToSchema;
118//! #[derive(ToSchema)]
119//! struct Pet {
120//!    id: u64,
121//!    name: String,
122//!    age: Option<i32>,
123//! }
124//! ```
125//!
126//! Create an handler that would handle your business logic and add `path` proc attribute macro over it.
127//! ```rust
128//! mod pet_api {
129//! #     use utoipa::ToSchema;
130//! #
131//! #     #[derive(ToSchema)]
132//! #     struct Pet {
133//! #       id: u64,
134//! #       name: String,
135//! #       age: Option<i32>,
136//! #     }
137//!     /// Get pet by id
138//!     ///
139//!     /// Get pet from database by pet id
140//!     #[utoipa::path(
141//!         get,
142//!         path = "/pets/{id}",
143//!         responses(
144//!             (status = 200, description = "Pet found successfully", body = Pet),
145//!             (status = NOT_FOUND, description = "Pet was not found")
146//!         ),
147//!         params(
148//!             ("id" = u64, Path, description = "Pet database id to get Pet for"),
149//!         )
150//!     )]
151//!     async fn get_pet_by_id(pet_id: u64) -> Pet {
152//!         Pet {
153//!             id: pet_id,
154//!             age: None,
155//!             name: "lightning".to_string(),
156//!         }
157//!     }
158//! }
159//! ```
160//!
161//! Utoipa has support for [http](https://crates.io/crates/http) `StatusCode` in responses.
162//!
163//! Tie the above component and api to the openapi schema with following `OpenApi` derive proc macro.
164//! ```rust
165//! # mod pet_api {
166//! #     use utoipa::ToSchema;
167//! #
168//! #     #[derive(ToSchema)]
169//! #     struct Pet {
170//! #       id: u64,
171//! #       name: String,
172//! #       age: Option<i32>,
173//! #     }
174//! #
175//! #     /// Get pet by id
176//! #     ///
177//! #     /// Get pet from database by pet id
178//! #     #[utoipa::path(
179//! #         get,
180//! #         path = "/pets/{id}",
181//! #         responses(
182//! #             (status = 200, description = "Pet found successfully", body = Pet),
183//! #             (status = 404, description = "Pet was not found")
184//! #         ),
185//! #         params(
186//! #             ("id" = u64, Path, description = "Pet database id to get Pet for"),
187//! #         )
188//! #     )]
189//! #     async fn get_pet_by_id(pet_id: u64) -> Pet {
190//! #         Pet {
191//! #             id: pet_id,
192//! #             age: None,
193//! #             name: "lightning".to_string(),
194//! #         }
195//! #     }
196//! # }
197//! # use utoipa::ToSchema;
198//! #
199//! # #[derive(ToSchema)]
200//! # struct Pet {
201//! #   id: u64,
202//! #   name: String,
203//! #   age: Option<i32>,
204//! # }
205//! use utoipa::OpenApi;
206//! #[derive(OpenApi)]
207//! #[openapi(paths(pet_api::get_pet_by_id), components(schemas(Pet)))]
208//! struct ApiDoc;
209//!
210//! println!("{}", ApiDoc::openapi().to_pretty_json().unwrap());
211//! ```
212//!
213//! # Modify OpenAPI at runtime
214//!
215//! You can modify generated OpenAPI at runtime either via generated types directly or using
216//! [`Modify`] trait.
217//!
218//! _**Modify generated OpenAPI via types directly.**_
219//! ```rust
220//! # use utoipa::OpenApi;
221//! #[derive(OpenApi)]
222//! #[openapi(
223//!     info(description = "My Api description"),
224//! )]
225//! struct ApiDoc;
226//!
227//! let mut doc = ApiDoc::openapi();
228//! doc.info.title = String::from("My Api");
229//! ```
230//!
231//! _**You can even convert the generated [`OpenApi`] to [`openapi::OpenApiBuilder`].**_
232//! ```rust
233//! # use utoipa::openapi::OpenApiBuilder;
234//! # use utoipa::OpenApi;
235//! #[derive(OpenApi)]
236//! #[openapi(
237//!     info(description = "My Api description"),
238//! )]
239//! struct ApiDoc;
240//!
241//! let builder: OpenApiBuilder = ApiDoc::openapi().into();
242//! ```
243//!
244//! See [`Modify`] trait for examples on how to modify generated OpenAPI via it.
245//!
246//! # Go beyond the surface
247//!
248//! * See how to serve OpenAPI doc via Swagger UI check [`utoipa-swagger-ui`][utoipa_swagger] crate for more details.
249//! * Browse to [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more comprehensive examples.
250//! * Check [`derive@IntoResponses`] and [`derive@ToResponse`] for examples on deriving responses.
251//! * More about OpenAPI security in [security documentation][security].
252//! * Dump generated API doc to file at build time. See [issue 214 comment](https://github.com/juhaku/utoipa/issues/214#issuecomment-1179589373).
253//!
254//! [path]: attr.path.html
255//! [rocket_path]: attr.path.html#rocket_extras-support-for-rocket
256//! [actix_path]: attr.path.html#actix_extras-support-for-actix-web
257//! [axum_path]: attr.path.html#axum_extras-support-for-axum
258//! [serde]: derive.ToSchema.html#partial-serde-attributes-support
259//!
260//! [security]: openapi/security/index.html
261//! [to_schema_derive]: derive.ToSchema.html
262
263pub mod openapi;
264
265use std::collections::{BTreeMap, HashMap};
266
267pub use utoipa_gen::*;
268
269/// Trait for implementing OpenAPI specification in Rust.
270///
271/// This trait is derivable and can be used with `#[derive]` attribute. The derived implementation
272/// will use Cargo provided environment variables to implement the default information. For a details of
273/// `#[derive(ToSchema)]` refer to [derive documentation][derive].
274///
275/// # Examples
276///
277/// Below is derived example of `OpenApi`.
278/// ```rust
279/// use utoipa::OpenApi;
280/// #[derive(OpenApi)]
281/// #[openapi()]
282/// struct OpenApiDoc;
283/// ```
284///
285/// This manual `OpenApi` trait implementation is approximately equal to the above derived one except the derive
286/// implementation will by default use the Cargo environment variables to set defaults for *application name,
287/// version, application description, license, author name & email*.
288///
289///```rust
290/// struct OpenApiDoc;
291///
292/// impl utoipa::OpenApi for OpenApiDoc {
293///     fn openapi() -> utoipa::openapi::OpenApi {
294///         use utoipa::{ToSchema, Path};
295///         utoipa::openapi::OpenApiBuilder::new()
296///             .info(utoipa::openapi::InfoBuilder::new()
297///                 .title("application name")
298///                 .version("version")
299///                 .description(Some("application description"))
300///                 .license(Some(utoipa::openapi::License::new("MIT")))
301///                 .contact(
302///                     Some(utoipa::openapi::ContactBuilder::new()
303///                         .name(Some("author name"))
304///                         .email(Some("author email")).build()),
305///             ).build())
306///             .paths(utoipa::openapi::path::Paths::new())
307///             .components(Some(utoipa::openapi::Components::new()))
308///             .build()
309///     }
310/// }
311/// ```
312/// [derive]: derive.OpenApi.html
313pub trait OpenApi {
314    fn openapi() -> openapi::OpenApi;
315}
316
317/// Trait for implementing OpenAPI Schema object.
318///
319/// Generated schemas can be referenced or reused in path operations.
320///
321/// This trait is derivable and can be used with `[#derive]` attribute. For a details of
322/// `#[derive(ToSchema)]` refer to [derive documentation][derive].
323///
324/// [derive]: derive.ToSchema.html
325///
326/// # Examples
327///
328/// Use `#[derive]` to implement `ToSchema` trait.
329/// ```rust
330/// # use utoipa::ToSchema;
331/// #[derive(ToSchema)]
332/// #[schema(example = json!({"name": "bob the cat", "id": 1}))]
333/// struct Pet {
334///     id: u64,
335///     name: String,
336///     age: Option<i32>,
337/// }
338/// ```
339///
340/// Following manual implementation is equal to above derive one.
341/// ```rust
342/// # struct Pet {
343/// #     id: u64,
344/// #     name: String,
345/// #     age: Option<i32>,
346/// # }
347/// #
348/// impl<'__s> utoipa::ToSchema<'__s> for Pet {
349///     fn schema() -> (&'__s str, utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>) {
350///          (
351///             "Pet",
352///             utoipa::openapi::ObjectBuilder::new()
353///                 .property(
354///                     "id",
355///                     utoipa::openapi::ObjectBuilder::new()
356///                         .schema_type(utoipa::openapi::SchemaType::Integer)
357///                         .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(
358///                             utoipa::openapi::KnownFormat::Int64,
359///                         ))),
360///                 )
361///                 .required("id")
362///                 .property(
363///                     "name",
364///                     utoipa::openapi::ObjectBuilder::new()
365///                         .schema_type(utoipa::openapi::SchemaType::String),
366///                 )
367///                 .required("name")
368///                 .property(
369///                     "age",
370///                     utoipa::openapi::ObjectBuilder::new()
371///                         .schema_type(utoipa::openapi::SchemaType::Integer)
372///                         .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(
373///                             utoipa::openapi::KnownFormat::Int32,
374///                         ))),
375///                 )
376///                 .example(Some(serde_json::json!({
377///                   "name":"bob the cat","id":1
378///                 })))
379///                 .into(),
380///         ) }
381/// }
382/// ```
383pub trait ToSchema<'__s> {
384    /// Return a tuple of name and schema or reference to a schema that can be referenced by the
385    /// name or inlined directly to responses, request bodies or parameters.
386    fn schema() -> (&'__s str, openapi::RefOr<openapi::schema::Schema>);
387
388    /// Optional set of alias schemas for the [`ToSchema::schema`].
389    ///
390    /// Typically there is no need to manually implement this method but it is instead implemented
391    /// by derive [`macro@ToSchema`] when `#[aliases(...)]` attribute is defined.
392    fn aliases() -> Vec<(&'__s str, openapi::schema::Schema)> {
393        Vec::new()
394    }
395}
396
397impl<'__s, T: ToSchema<'__s>> From<T> for openapi::RefOr<openapi::schema::Schema> {
398    fn from(_: T) -> Self {
399        T::schema().1
400    }
401}
402
403/// Represents _`nullable`_ type. This can be used anywhere where "nothing" needs to be evaluated.
404/// This will serialize to _`null`_ in JSON and [`openapi::schema::empty`] is used to create the
405/// [`openapi::schema::Schema`] for the type.
406pub type TupleUnit = ();
407
408impl<'__s> ToSchema<'__s> for TupleUnit {
409    fn schema() -> (&'__s str, openapi::RefOr<openapi::schema::Schema>) {
410        ("TupleUnit", openapi::schema::empty().into())
411    }
412}
413
414macro_rules! impl_partial_schema {
415    ( $ty:path ) => {
416        impl_partial_schema!( @impl_schema $ty );
417    };
418    ( & $ty:path ) => {
419        impl_partial_schema!( @impl_schema &$ty );
420    };
421    ( @impl_schema $( $tt:tt )* ) => {
422        impl PartialSchema for $($tt)* {
423            fn schema() -> openapi::RefOr<openapi::schema::Schema> {
424                schema!( $($tt)* ).into()
425            }
426        }
427    };
428}
429
430macro_rules! impl_partial_schema_primitive {
431    ( $( $tt:path  ),* ) => {
432        $( impl_partial_schema!( $tt ); )*
433    };
434}
435
436// Create `utoipa` module so we can use `utoipa-gen` directly from `utoipa` crate.
437// ONLY for internal use!
438#[doc(hidden)]
439mod utoipa {
440    pub use super::*;
441}
442
443/// Trait used to implement only _`Schema`_ part of the OpenAPI doc.
444///
445/// This trait is by default implemented for Rust [`primitive`][primitive] types and some well known types like
446/// [`Vec`], [`Option`], [`HashMap`] and [`BTreeMap`]. The default implementation adds `schema()`
447/// method to the implementing type allowing simple conversion of the type to the OpenAPI Schema
448/// object. Moreover this allows handy way of constructing schema objects manually if ever so
449/// wished.
450///
451/// The trait can be implemented manually easily on any type. This trait comes especially handy
452/// with [`macro@schema`] macro that can be used to generate schema for arbitrary types.
453/// ```rust
454/// # use utoipa::PartialSchema;
455/// # use utoipa::openapi::schema::{SchemaType, KnownFormat, SchemaFormat, ObjectBuilder, Schema};
456/// # use utoipa::openapi::RefOr;
457/// #
458/// struct MyType;
459///
460/// impl PartialSchema for MyType {
461///     fn schema() -> RefOr<Schema> {
462///         // ... impl schema generation here
463///         RefOr::T(Schema::Object(ObjectBuilder::new().build()))
464///     }
465/// }
466/// ```
467///
468/// # Examples
469///
470/// _**Create number schema from u64.**_
471/// ```rust
472/// # use utoipa::PartialSchema;
473/// # use utoipa::openapi::schema::{SchemaType, KnownFormat, SchemaFormat, ObjectBuilder, Schema};
474/// # use utoipa::openapi::RefOr;
475/// #
476/// let number: RefOr<Schema> = i64::schema().into();
477///
478/// // would be equal to manual implementation
479/// let number2 = RefOr::T(
480///     Schema::Object(
481///         ObjectBuilder::new()
482///             .schema_type(SchemaType::Integer)
483///             .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int64)))
484///             .build()
485///         )
486///     );
487/// # assert_json_diff::assert_json_eq!(serde_json::to_value(&number).unwrap(), serde_json::to_value(&number2).unwrap());
488/// ```
489///
490/// _**Construct a Pet object schema manually.**_
491/// ```rust
492/// # use utoipa::PartialSchema;
493/// # use utoipa::openapi::schema::ObjectBuilder;
494/// struct Pet {
495///     id: i32,
496///     name: String,
497/// }
498///
499/// let pet_schema = ObjectBuilder::new()
500///     .property("id", i32::schema())
501///     .property("name", String::schema())
502///     .required("id").required("name")
503///     .build();
504/// ```
505///
506/// [primitive]: https://doc.rust-lang.org/std/primitive/index.html
507pub trait PartialSchema {
508    /// Return ref or schema of implementing type that can then be used to
509    /// construct combined schemas.
510    fn schema() -> openapi::RefOr<openapi::schema::Schema>;
511}
512
513#[rustfmt::skip]
514impl_partial_schema_primitive!(
515    i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, bool, f32, f64, String, str, char,
516    Option<i8>, Option<i16>, Option<i32>, Option<i64>, Option<i128>, Option<isize>, Option<u8>, Option<u16>,
517    Option<u32>, Option<u64>, Option<u128>, Option<usize>, Option<bool>, Option<f32>, Option<f64>,
518    Option<String>, Option<&str>, Option<char>
519);
520
521impl_partial_schema!(&str);
522
523impl<'__s, T: ToSchema<'__s>> PartialSchema for Vec<T> {
524    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
525        schema!(#[inline] Vec<T>).into()
526    }
527}
528
529impl<'__s, T: ToSchema<'__s>> PartialSchema for Option<Vec<T>> {
530    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
531        schema!(#[inline] Option<Vec<T>>).into()
532    }
533}
534
535impl<'__s, T: ToSchema<'__s>> PartialSchema for [T] {
536    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
537        schema!(
538            #[inline]
539            [T]
540        )
541        .into()
542    }
543}
544
545impl<'__s, T: ToSchema<'__s>> PartialSchema for &[T] {
546    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
547        schema!(
548            #[inline]
549            &[T]
550        )
551        .into()
552    }
553}
554
555impl<'__s, T: ToSchema<'__s>> PartialSchema for &mut [T] {
556    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
557        schema!(
558            #[inline]
559            &mut [T]
560        )
561        .into()
562    }
563}
564
565impl<'__s, T: ToSchema<'__s>> PartialSchema for Option<&[T]> {
566    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
567        schema!(
568            #[inline]
569            Option<&[T]>
570        )
571        .into()
572    }
573}
574
575impl<'__s, T: ToSchema<'__s>> PartialSchema for Option<&mut [T]> {
576    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
577        schema!(
578            #[inline]
579            Option<&mut [T]>
580        )
581        .into()
582    }
583}
584
585impl<'__s, T: ToSchema<'__s>> PartialSchema for Option<T> {
586    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
587        schema!(#[inline] Option<T>).into()
588    }
589}
590
591impl<'__s, K: PartialSchema, V: ToSchema<'__s>> PartialSchema for BTreeMap<K, V> {
592    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
593        schema!(
594            #[inline]
595            BTreeMap<K, V>
596        )
597        .into()
598    }
599}
600
601impl<'__s, K: PartialSchema, V: ToSchema<'__s>> PartialSchema for Option<BTreeMap<K, V>> {
602    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
603        schema!(
604            #[inline]
605            Option<BTreeMap<K, V>>
606        )
607        .into()
608    }
609}
610
611impl<'__s, K: PartialSchema, V: ToSchema<'__s>> PartialSchema for HashMap<K, V> {
612    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
613        schema!(
614            #[inline]
615            HashMap<K, V>
616        )
617        .into()
618    }
619}
620
621impl<'__s, K: PartialSchema, V: ToSchema<'__s>> PartialSchema for Option<HashMap<K, V>> {
622    fn schema() -> openapi::RefOr<openapi::schema::Schema> {
623        schema!(
624            #[inline]
625            Option<HashMap<K, V>>
626        )
627        .into()
628    }
629}
630
631/// Trait for implementing OpenAPI PathItem object with path.
632///
633/// This trait is implemented via [`#[utoipa::path(...)]`][derive] attribute macro and there
634/// is no need to implement this trait manually.
635///
636/// # Examples
637///
638/// Use `#[utoipa::path(..)]` to implement Path trait
639/// ```rust
640/// # struct Pet {
641/// #   id: u64,
642/// #   name: String,
643/// # }
644/// #
645/// #
646/// /// Get pet by id
647/// ///
648/// /// Get pet from database by pet database id
649/// #[utoipa::path(
650///     get,
651///     path = "/pets/{id}",
652///     responses(
653///         (status = 200, description = "Pet found successfully", body = Pet),
654///         (status = 404, description = "Pet was not found")
655///     ),
656///     params(
657///         ("id" = u64, Path, description = "Pet database id to get Pet for"),
658///     )
659/// )]
660/// async fn get_pet_by_id(pet_id: u64) -> Pet {
661///     Pet {
662///         id: pet_id,
663///         name: "lightning".to_string(),
664///     }
665/// }
666/// ```
667///
668/// Example of what would manual implementation roughly look like of above `#[utoipa::path(...)]` macro.
669/// ```rust
670/// utoipa::openapi::PathsBuilder::new().path(
671///         "/pets/{id}",
672///         utoipa::openapi::PathItem::new(
673///             utoipa::openapi::PathItemType::Get,
674///             utoipa::openapi::path::OperationBuilder::new()
675///                 .responses(
676///                     utoipa::openapi::ResponsesBuilder::new()
677///                         .response(
678///                             "200",
679///                             utoipa::openapi::ResponseBuilder::new()
680///                                 .description("Pet found successfully")
681///                                 .content("application/json",
682///                                     utoipa::openapi::Content::new(
683///                                         utoipa::openapi::Ref::from_schema_name("Pet"),
684///                                     ),
685///                             ),
686///                         )
687///                         .response("404", utoipa::openapi::Response::new("Pet was not found")),
688///                 )
689///                 .operation_id(Some("get_pet_by_id"))
690///                 .deprecated(Some(utoipa::openapi::Deprecated::False))
691///                 .summary(Some("Get pet by id"))
692///                 .description(Some("Get pet by id\n\nGet pet from database by pet database id\n"))
693///                 .parameter(
694///                     utoipa::openapi::path::ParameterBuilder::new()
695///                         .name("id")
696///                         .parameter_in(utoipa::openapi::path::ParameterIn::Path)
697///                         .required(utoipa::openapi::Required::True)
698///                         .deprecated(Some(utoipa::openapi::Deprecated::False))
699///                         .description(Some("Pet database id to get Pet for"))
700///                         .schema(
701///                             Some(utoipa::openapi::ObjectBuilder::new()
702///                                 .schema_type(utoipa::openapi::SchemaType::Integer)
703///                                 .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int64)))),
704///                         ),
705///                 )
706///                 .tag("pet_api"),
707///         ),
708///     );
709/// ```
710///
711/// [derive]: attr.path.html
712pub trait Path {
713    fn path() -> &'static str;
714
715    fn path_item(default_tag: Option<&str>) -> openapi::path::PathItem;
716}
717
718/// Trait that allows OpenApi modification at runtime.
719///
720/// Implement this trait if you wish to modify the OpenApi at runtime before it is being consumed
721/// *(Before `utoipa::OpenApi::openapi()` function returns)*.
722/// This is trait can be used to add or change already generated OpenApi spec to alter the generated
723/// specification by user defined condition. For example you can add definitions that should be loaded
724/// from some configuration at runtime what may not be available during compile time.
725///
726/// See more about [`OpenApi`][derive] derive at [derive documentation][derive].
727///
728/// [derive]: derive.OpenApi.html
729/// [security_schema]: openapi/security/enum.SecuritySchema.html
730///
731/// # Examples
732///
733/// Add custom JWT [`SecuritySchema`][security_schema] to [`OpenApi`][`openapi::OpenApi`].
734/// ```rust
735/// # use utoipa::{OpenApi, Modify};
736/// # use utoipa::openapi::security::{SecurityScheme, HttpBuilder, HttpAuthScheme};
737/// #[derive(OpenApi)]
738/// #[openapi(modifiers(&SecurityAddon))]
739/// struct ApiDoc;
740///
741/// struct SecurityAddon;
742///
743/// impl Modify for SecurityAddon {
744///     fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
745///          openapi.components = Some(
746///              utoipa::openapi::ComponentsBuilder::new()
747///                  .security_scheme(
748///                      "api_jwt_token",
749///                      SecurityScheme::Http(
750///                          HttpBuilder::new()
751///                              .scheme(HttpAuthScheme::Bearer)
752///                              .bearer_format("JWT")
753///                              .build(),
754///                      ),
755///                  )
756///                  .build(),
757///          )
758///      }
759/// }
760/// ```
761///
762/// Add [OpenAPI Server Object][server] to alter the target server url. This can be used to give context
763/// path for api operations.
764/// ```rust
765/// # use utoipa::{OpenApi, Modify};
766/// # use utoipa::openapi::Server;
767/// #[derive(OpenApi)]
768/// #[openapi(modifiers(&ServerAddon))]
769/// struct ApiDoc;
770///
771/// struct ServerAddon;
772///
773/// impl Modify for ServerAddon {
774///     fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
775///         openapi.servers = Some(vec![Server::new("/api")])
776///     }
777/// }
778/// ```
779///
780/// [server]: https://spec.openapis.org/oas/latest.html#server-object
781pub trait Modify {
782    fn modify(&self, openapi: &mut openapi::OpenApi);
783}
784
785/// Trait used to convert implementing type to OpenAPI parameters.
786///
787/// This trait is [derivable][derive] for structs which are used to describe `path` or `query` parameters.
788/// For more details of `#[derive(IntoParams)]` refer to [derive documentation][derive].
789///
790/// # Examples
791///
792/// Derive [`IntoParams`] implementation. This example will fail to compile because [`IntoParams`] cannot
793/// be used alone and it need to be used together with endpoint using the params as well. See
794/// [derive documentation][derive] for more details.
795/// ```
796/// use utoipa::{IntoParams};
797///
798/// #[derive(IntoParams)]
799/// struct PetParams {
800///     /// Id of pet
801///     id: i64,
802///     /// Name of pet
803///     name: String,
804/// }
805/// ```
806///
807/// Roughly equal manual implementation of [`IntoParams`] trait.
808/// ```rust
809/// # struct PetParams {
810/// #    /// Id of pet
811/// #    id: i64,
812/// #    /// Name of pet
813/// #    name: String,
814/// # }
815/// impl utoipa::IntoParams for PetParams {
816///     fn into_params(
817///         parameter_in_provider: impl Fn() -> Option<utoipa::openapi::path::ParameterIn>
818///     ) -> Vec<utoipa::openapi::path::Parameter> {
819///         vec![
820///             utoipa::openapi::path::ParameterBuilder::new()
821///                 .name("id")
822///                 .required(utoipa::openapi::Required::True)
823///                 .parameter_in(parameter_in_provider().unwrap_or_default())
824///                 .description(Some("Id of pet"))
825///                 .schema(Some(
826///                     utoipa::openapi::ObjectBuilder::new()
827///                         .schema_type(utoipa::openapi::SchemaType::Integer)
828///                         .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int64))),
829///                 ))
830///                 .build(),
831///             utoipa::openapi::path::ParameterBuilder::new()
832///                 .name("name")
833///                 .required(utoipa::openapi::Required::True)
834///                 .parameter_in(parameter_in_provider().unwrap_or_default())
835///                 .description(Some("Name of pet"))
836///                 .schema(Some(
837///                     utoipa::openapi::ObjectBuilder::new()
838///                         .schema_type(utoipa::openapi::SchemaType::String),
839///                 ))
840///                 .build(),
841///         ]
842///     }
843/// }
844/// ```
845/// [derive]: derive.IntoParams.html
846pub trait IntoParams {
847    /// Provide [`Vec`] of [`openapi::path::Parameter`]s to caller. The result is used in `utoipa-gen` library to
848    /// provide OpenAPI parameter information for the endpoint using the parameters.
849    fn into_params(
850        parameter_in_provider: impl Fn() -> Option<openapi::path::ParameterIn>,
851    ) -> Vec<openapi::path::Parameter>;
852}
853
854/// This trait is implemented to document a type (like an enum) which can represent multiple
855/// responses, to be used in operation.
856///
857/// # Examples
858///
859/// ```
860/// use std::collections::BTreeMap;
861/// use utoipa::{
862///     openapi::{Response, ResponseBuilder, ResponsesBuilder, RefOr},
863///     IntoResponses,
864/// };
865///
866/// enum MyResponse {
867///     Ok,
868///     NotFound,
869/// }
870///
871/// impl IntoResponses for MyResponse {
872///     fn responses() -> BTreeMap<String, RefOr<Response>> {
873///         ResponsesBuilder::new()
874///             .response("200", ResponseBuilder::new().description("Ok"))
875///             .response("404", ResponseBuilder::new().description("Not Found"))
876///             .build()
877///             .into()
878///     }
879/// }
880/// ```
881pub trait IntoResponses {
882    /// Returns an ordered map of response codes to responses.
883    fn responses() -> BTreeMap<String, openapi::RefOr<openapi::response::Response>>;
884}
885
886#[cfg(feature = "auto_into_responses")]
887impl<T: IntoResponses, E: IntoResponses> IntoResponses for Result<T, E> {
888    fn responses() -> BTreeMap<String, openapi::RefOr<openapi::response::Response>> {
889        let mut responses = T::responses();
890        responses.append(&mut E::responses());
891
892        responses
893    }
894}
895
896#[cfg(feature = "auto_into_responses")]
897impl IntoResponses for () {
898    fn responses() -> BTreeMap<String, openapi::RefOr<openapi::response::Response>> {
899        BTreeMap::new()
900    }
901}
902
903/// This trait is implemented to document a type which represents a single response which can be
904/// referenced or reused as a component in multiple operations.
905///
906/// _`ToResponse`_ trait can also be derived with [`#[derive(ToResponse)]`][derive].
907///
908/// # Examples
909///
910/// ```
911/// use utoipa::{
912///     openapi::{RefOr, Response, ResponseBuilder},
913///     ToResponse,
914/// };
915///
916/// struct MyResponse;
917///
918/// impl<'__r> ToResponse<'__r> for MyResponse {
919///     fn response() -> (&'__r str, RefOr<Response>) {
920///         (
921///             "MyResponse",
922///             ResponseBuilder::new().description("My Response").build().into(),
923///         )
924///     }
925/// }
926/// ```
927///
928/// [derive]: derive.ToResponse.html
929pub trait ToResponse<'__r> {
930    /// Returns a tuple of response component name (to be referenced) to a response.
931    fn response() -> (&'__r str, openapi::RefOr<openapi::response::Response>);
932}
933
934#[cfg(test)]
935mod tests {
936    use assert_json_diff::assert_json_eq;
937    use serde_json::json;
938
939    use super::*;
940
941    #[cfg(not(feature = "non_strict_integers"))]
942    #[test]
943    fn test_partial_schema_strict_integers() {
944        use assert_json_diff::{assert_json_matches, CompareMode, Config, NumericMode};
945
946        for (name, schema, value) in [
947            (
948                "i8",
949                i8::schema(),
950                json!({"type": "integer", "format": "int32"}),
951            ),
952            (
953                "i16",
954                i16::schema(),
955                json!({"type": "integer", "format": "int32"}),
956            ),
957            (
958                "i32",
959                i32::schema(),
960                json!({"type": "integer", "format": "int32"}),
961            ),
962            (
963                "i64",
964                i64::schema(),
965                json!({"type": "integer", "format": "int64"}),
966            ),
967            ("i128", i128::schema(), json!({"type": "integer"})),
968            ("isize", isize::schema(), json!({"type": "integer"})),
969            (
970                "u8",
971                u8::schema(),
972                json!({"type": "integer", "format": "int32", "minimum": 0.0}),
973            ),
974            (
975                "u16",
976                u16::schema(),
977                json!({"type": "integer", "format": "int32", "minimum": 0.0}),
978            ),
979            (
980                "u32",
981                u32::schema(),
982                json!({"type": "integer", "format": "int32", "minimum": 0.0}),
983            ),
984            (
985                "u64",
986                u64::schema(),
987                json!({"type": "integer", "format": "int64", "minimum": 0.0}),
988            ),
989        ] {
990            println!(
991                "{name}: {json}",
992                json = serde_json::to_string(&schema).unwrap()
993            );
994            let schema = serde_json::to_value(schema).unwrap();
995
996            let config = Config::new(CompareMode::Strict).numeric_mode(NumericMode::AssumeFloat);
997            assert_json_matches!(schema, value, config);
998        }
999    }
1000
1001    #[cfg(feature = "non_strict_integers")]
1002    #[test]
1003    fn test_partial_schema_non_strict_integers() {
1004        for (name, schema, value) in [
1005            (
1006                "i8",
1007                i8::schema(),
1008                json!({"type": "integer", "format": "int8"}),
1009            ),
1010            (
1011                "i16",
1012                i16::schema(),
1013                json!({"type": "integer", "format": "int16"}),
1014            ),
1015            (
1016                "i32",
1017                i32::schema(),
1018                json!({"type": "integer", "format": "int32"}),
1019            ),
1020            (
1021                "i64",
1022                i64::schema(),
1023                json!({"type": "integer", "format": "int64"}),
1024            ),
1025            ("i128", i128::schema(), json!({"type": "integer"})),
1026            ("isize", isize::schema(), json!({"type": "integer"})),
1027            (
1028                "u8",
1029                u8::schema(),
1030                json!({"type": "integer", "format": "uint8", "minimum": 0}),
1031            ),
1032            (
1033                "u16",
1034                u16::schema(),
1035                json!({"type": "integer", "format": "uint16", "minimum": 0}),
1036            ),
1037            (
1038                "u32",
1039                u32::schema(),
1040                json!({"type": "integer", "format": "uint32", "minimum": 0}),
1041            ),
1042            (
1043                "u64",
1044                u64::schema(),
1045                json!({"type": "integer", "format": "uint64", "minimum": 0}),
1046            ),
1047        ] {
1048            println!(
1049                "{name}: {json}",
1050                json = serde_json::to_string(&schema).unwrap()
1051            );
1052            let schema = serde_json::to_value(schema).unwrap();
1053            assert_json_eq!(schema, value);
1054        }
1055    }
1056
1057    #[test]
1058    fn test_partial_schema() {
1059        for (name, schema, value) in [
1060            ("bool", bool::schema(), json!({"type": "boolean"})),
1061            ("str", str::schema(), json!({"type": "string"})),
1062            ("String", String::schema(), json!({"type": "string"})),
1063            ("char", char::schema(), json!({"type": "string"})),
1064            (
1065                "f32",
1066                f32::schema(),
1067                json!({"type": "number", "format": "float"}),
1068            ),
1069            (
1070                "f64",
1071                f64::schema(),
1072                json!({"type": "number", "format": "double"}),
1073            ),
1074        ] {
1075            println!(
1076                "{name}: {json}",
1077                json = serde_json::to_string(&schema).unwrap()
1078            );
1079            let schema = serde_json::to_value(schema).unwrap();
1080            assert_json_eq!(schema, value);
1081        }
1082    }
1083}