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}