Skip to main content

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}