openapi_type/lib.rs
1#![cfg_attr(not(feature = "_all_features"), allow(rustdoc::broken_intra_doc_links))]
2//! This crate gives static type information for primitives and commonly used types from
3//! the standard library and other commonly used libraries (see [features](#features))
4//! when the according feature is enabled. Please refer to the [`Cargo.toml`] for a list
5//! of all available feature flags and optional dependencies. Also, it provides a derive
6//! macro for structs and enums to gain access to their static type information at
7//! runtime.
8//!
9//! The core of this crate is the [`OpenapiType`] trait. It has three static function,
10//! where [`schema`](OpenapiType::schema), which returns an [`OpenapiSchema`], is the one
11//! you are probably looking for. It assembles the static type information in a way that
12//! is convenient to use for a generated OpenAPI specification, but can also be utilized
13//! in other use cases as well.
14//!
15//! # Custom Types
16//!
17//! To gain access to the static type information of your custom types at runtime, the
18//! easiest way is to use the derive macro:
19//!
20//! ```rust
21//! # use openapi_type::OpenapiType;
22//! #[derive(OpenapiType)]
23//! struct FooBar {
24//! foo: Option<String>,
25//! bar: u64
26//! }
27//! #
28//! # // Test that the OpenAPI schema is the one we display below
29//! # let schema = FooBar::schema();
30//! # let schema_yaml = serde_saphyr::to_string_with_options(
31//! # &schema.schema,
32//! # serde_saphyr::ser::options::SerializerOptions {
33//! # compact_list_indent: false,
34//! # ..Default::default()
35//! # }).unwrap();
36//! # pretty_assertions::assert_eq!(include_str!("lib.rs_schema.yml"), schema_yaml);
37//! #
38//! # // Test that the Path Params is the one we display below
39//! # let path_params = FooBar::path_params();
40//! # let path_params_yaml = serde_saphyr::to_string_with_options(
41//! # &path_params.params,
42//! # serde_saphyr::ser::options::SerializerOptions {
43//! # compact_list_indent: false,
44//! # ..Default::default()
45//! # }).unwrap();
46//! # pretty_assertions::assert_eq!(include_str!("lib.rs_path_params.yml"), path_params_yaml);
47//! #
48//! # // Test that the Query Params is the one we display below
49//! # let query_params = FooBar::query_params();
50//! # let query_params_yaml = serde_saphyr::to_string_with_options(
51//! # &query_params.params,
52//! # serde_saphyr::ser::options::SerializerOptions {
53//! # compact_list_indent: false,
54//! # ..Default::default()
55//! # }).unwrap();
56//! # pretty_assertions::assert_eq!(include_str!("lib.rs_query_params.yml"), query_params_yaml);
57//! ```
58//!
59//! # OpenAPI specification
60//!
61//! Using above type, running `FooBar::schema().into_schema()` yields
62//!
63//! ```yaml
64#![doc = include_str!("lib.rs_schema.yml")]
65//! ```
66//!
67//! Note, however, that this is not sufficient for more complex types. If one of your
68//! structs fields is a type that has a name (that is, `Type::schema().name` is not
69//! `None`), above schema will contain a reference to that schema. Therefore, always
70//! remember to put the [`dependencies`](OpenapiSchema::dependencies) into the
71//! specification alongside the type you are interested in.
72//!
73//! ## Path Parameters
74//!
75//! We can also treat the `FooBar` struct from above as a struct containing path
76//! parameters, if we construct an [`Operation`](oas3::spec::Operation) for a path like
77//! `/foo/{foo}/bar/{bar}`. Running `FooBar::path_params()` yields
78//! ```yaml
79#![doc = include_str!("lib.rs_path_params.yml")]
80//! ```
81//!
82//! ## Query Parameters
83//!
84//! Likewise, we can treat the `FooBar` struct from above as a struct containing query
85//! parameters. Running `FooBar::query_params()` yields
86//! ```yaml
87#![doc = include_str!("lib.rs_query_params.yml")]
88//! ```
89//!
90//! # Features
91//!
92//! The following cargo features may be enabled for this crate to unlock additional
93//! implementations for larger tuples. For example, the `tuples32` feature would unlock
94//! the implementation for tuples with sizes up to 32.
95//!
96//! - `tuples16` (enabled by default)
97//! - `tuples32`
98//! - `tuples48`
99//! - `tuples64`
100//!
101//! The following cargo features may be enabled for this crate to unlock additional
102//! implementations for external crates:
103//!
104//! - `chrono`: Enable all of the below:
105//! - `chrono04`: Enable implementations for [`chrono` 0.4](chrono04)
106//! - `hashbrown`: Enable all of the below:
107//! - `hashbrown016`: Enable implementations for [`hashbrown` 0.16](hashbrown016)
108//! - `hashbrown017`: Enable implementations for [`hashbrown` 0.17](hashbrown017)
109//! - `jiff`: Enable all of the below:
110//! - `jiff02`: Enable implementations for [`jiff` 0.2](jiff02)
111//! - `linked-hash-map`: Enable all of the below:
112//! - `linked-hash-map05`: Enable implementations for [`linked-hash-map` 0.5](linked_hash_map05)
113//! - `time`: Enable all of the below:
114//! - `time03`: Enable implementations for [`time` 0.3](time03)
115//! - `url`: Enable all of the below:
116//! - `url2`: Enable implementations for [`url` 2.x](url2)
117//! - `uuid`: Enable all of the below:
118//! - `uuid1`: Enable implementations for [`uuid` 1.x](uuid1)
119//!
120//! Note that support for [`indexmap` 2.x](indexmap) is enabled by default.
121//!
122//! [`Cargo.toml`]: https://docs.rs/crate/openapi_type/latest/source/Cargo.toml.orig
123
124mod impls;
125mod visitor;
126
127#[doc(inline)]
128pub use self::visitor::{
129 AlternativesVisitor, ObjectVisitor, OpenapiParams, OpenapiParamsVisitor,
130 OpenapiSchema, OpenapiVisitor, PathParamsVisitor, QueryParamsVisitor, TupleVisitor,
131 Visitor
132};
133#[doc(no_inline)]
134pub use indexmap::{IndexMap, IndexSet};
135#[doc(no_inline)]
136pub use oas3::{Map, spec};
137#[doc(inline)]
138pub use openapi_type_derive::OpenapiType;
139
140/// This trait needs to be implemented by every type that is being used in the OpenAPI Spec. It gives
141/// access to the [OpenapiSchema] of this type. It is provided for primitive types, String and the
142/// like. For use on your own types, there is a derive macro:
143///
144/// ```
145/// # #[macro_use] extern crate openapi_type_derive;
146/// #
147/// #[derive(OpenapiType)]
148/// struct MyResponse {
149/// message: String
150/// }
151/// ```
152pub trait OpenapiType {
153 /// Visit the type description of this type.
154 ///
155 /// You are unlikely to ever need this method directly. Instead, you are probably
156 /// looking for the [`schema()`](Self::schema) method, or one of the other methods
157 /// that directly return the desired OpenAPI schema type.
158 ///
159 /// When implementing this method by hand, you must make sure to always call exactly
160 /// one visit method.
161 fn visit_type<V: Visitor>(visitor: &mut V);
162
163 /// Return the [`OpenapiSchema`] for this type.
164 ///
165 /// This schema is designed to be compatible with the way [`serde_json`] would
166 /// (de)serialize this type. If you find a discrepancy between the OpenAPI schema
167 /// and the way [`serde_json`] serializes this type, please [open an issue].
168 ///
169 /// [`serde_json`]: https://crates.io/crates/serde_json
170 /// [open an issue]: https://codeberg.org/msrd0/openapi_type/issues/new
171 fn schema() -> OpenapiSchema {
172 let mut visitor = OpenapiVisitor::new();
173 Self::visit_type(&mut visitor);
174 visitor
175 .into_schema()
176 .expect("The OpenapiType implementation failed to call the visitor")
177 }
178
179 /// Interpret this type as a collection of path parameters, and return the
180 /// path params as [`OpenapiParams`] for this type.
181 ///
182 /// # Panics
183 ///
184 /// This will panic if this type is anything but an object, or if the fields of this
185 /// object are not either “primitive” types or flattened objects.
186 fn path_params() -> OpenapiParams {
187 let mut visitor = OpenapiParamsVisitor::new(PathParamsVisitor::new());
188 Self::visit_type(&mut visitor);
189 visitor.into_inner().into_params()
190 }
191
192 /// Interpret this type as a collection of query parameters, and return the
193 /// query params as [`OpenapiParams`] for this type.
194 ///
195 /// This schema is desigend to be compatible with the way [`serde_html_form`] would
196 /// deserialize this type. If you find a discrepancy between the OpenAPI query params
197 /// and the way [`serde_html_form`] deserializes this type, please [open an issue].
198 ///
199 /// # Panics
200 ///
201 /// This will panic if this type is anything but an object, or if the fields of this
202 /// object are not either “primitive” types, arrays of “primitive” types, or flattened
203 /// objects.
204 ///
205 /// [`serde_html_form`]: https://codeberg.org/jplatte/serde_html_form
206 /// [open an issue]: https://codeberg.org/msrd0/openapi_type/issues/new
207 fn query_params() -> OpenapiParams {
208 let mut visitor = OpenapiParamsVisitor::new(QueryParamsVisitor::new());
209 Self::visit_type(&mut visitor);
210 visitor.into_inner().into_params()
211 }
212}
213
214impl<T: ?Sized + OpenapiType> OpenapiType for &T {
215 fn visit_type<V: Visitor>(visitor: &mut V) {
216 T::visit_type(visitor)
217 }
218}