apistos_schemars/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4/// The map type used by schemars types.
5///
6/// Currently a `BTreeMap` or `IndexMap` can be used, but this may change to a different implementation
7/// with a similar interface in a future version of schemars.
8/// The `IndexMap` will be used when the `preserve_order` feature flag is set.
9#[cfg(not(feature = "preserve_order"))]
10pub type Map<K, V> = std::collections::BTreeMap<K, V>;
11#[cfg(feature = "preserve_order")]
12pub type Map<K, V> = indexmap::IndexMap<K, V>;
13/// The set type used by schemars types.
14///
15/// Currently a `BTreeSet`, but this may change to a different implementation
16/// with a similar interface in a future version of schemars.
17pub type Set<T> = std::collections::BTreeSet<T>;
18
19/// A view into a single entry in a map, which may either be vacant or occupied.
20//
21/// This is constructed from the `entry` method on `BTreeMap` or `IndexMap`,
22/// depending on whether the `preserve_order` feature flag is set.
23#[cfg(not(feature = "preserve_order"))]
24pub type MapEntry<'a, K, V> = std::collections::btree_map::Entry<'a, K, V>;
25#[cfg(feature = "preserve_order")]
26pub type MapEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>;
27
28mod flatten;
29mod json_schema_impls;
30mod ser;
31#[macro_use]
32mod macros;
33
34/// This module is only public for use by `schemars_derive`. It should not need to be used by code
35/// outside of `schemars`, and should not be considered part of the public API.
36#[doc(hidden)]
37pub mod _private;
38
39pub mod r#gen;
40pub mod schema;
41pub mod visit;
42
43pub use r#gen::SchemaGenerator;
44
45#[cfg(feature = "schemars_derive")]
46extern crate schemars_derive;
47use std::borrow::Cow;
48
49#[cfg(feature = "schemars_derive")]
50pub use schemars_derive::*;
51
52// Export serde_json so schemars_derive can use it
53#[doc(hidden)]
54pub use serde_json as _serde_json;
55
56use schema::Schema;
57
58/// A type which can be described as a JSON Schema document.
59///
60/// This is implemented for many Rust primitive and standard library types.
61///
62/// This can also be automatically derived on most custom types with `#[derive(JsonSchema)]`.
63///
64/// # Examples
65/// Deriving an implementation:
66/// ```
67///# extern crate apistos_schemars as schemars;
68/// use schemars::{schema_for, JsonSchema};
69///
70/// #[derive(JsonSchema)]
71/// struct MyStruct {
72///     foo: i32,
73/// }
74///
75/// let my_schema = schema_for!(MyStruct);
76/// ```
77///
78/// When manually implementing `JsonSchema`, as well as determining an appropriate schema,
79/// you will need to determine an appropriate name and ID for the type.
80/// For non-generic types, the type name/path are suitable for this:
81/// ```
82///# extern crate apistos_schemars as schemars;
83/// use schemars::{r#gen::SchemaGenerator, schema::Schema, JsonSchema};
84/// use std::borrow::Cow;
85///
86/// struct NonGenericType;
87///
88/// impl JsonSchema for NonGenericType {
89///     fn schema_name() -> String {
90///         // Exclude the module path to make the name in generated schemas clearer.
91///         "NonGenericType".to_owned()
92///     }
93///
94///     fn schema_id() -> Cow<'static, str> {
95///         // Include the module, in case a type with the same name is in another module/crate
96///         Cow::Borrowed(concat!(module_path!(), "::NonGenericType"))
97///     }
98///
99///     fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
100///         todo!()
101///     }
102/// }
103///
104/// assert_eq!(NonGenericType::schema_id(), <&mut NonGenericType>::schema_id());
105/// ```
106///
107/// But generic type parameters which may affect the generated schema should typically be included in the name/ID:
108/// ```
109///# extern crate apistos_schemars as schemars;
110/// use schemars::{r#gen::SchemaGenerator, schema::Schema, JsonSchema};
111/// use std::{borrow::Cow, marker::PhantomData};
112///
113/// struct GenericType<T>(PhantomData<T>);
114///
115/// impl<T: JsonSchema> JsonSchema for GenericType<T> {
116///     fn schema_name() -> String {
117///         format!("GenericType_{}", T::schema_name())
118///     }
119///
120///     fn schema_id() -> Cow<'static, str> {
121///         Cow::Owned(format!(
122///             "{}::GenericType<{}>",
123///             module_path!(),
124///             T::schema_id()
125///         ))
126///     }
127///
128///     fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
129///         todo!()
130///     }
131/// }
132///
133/// assert_eq!(<GenericType<i32>>::schema_id(), <&mut GenericType<&i32>>::schema_id());
134/// ```
135///
136pub trait JsonSchema {
137    /// Whether JSON Schemas generated for this type should be re-used where possible using the `$ref` keyword.
138    ///
139    /// For trivial types (such as primitives), this should return `false`. For more complex types, it should return `true`.
140    /// For recursive types, this **must** return `true` to prevent infinite cycles when generating schemas.
141    ///
142    /// By default, this returns `true`.
143    fn is_referenceable() -> bool {
144        true
145    }
146
147    /// The name of the generated JSON Schema.
148    ///
149    /// This is used as the title for root schemas, and the key within the root's `definitions` property for subschemas.
150    fn schema_name() -> String;
151
152    /// Returns a string that uniquely identifies the schema produced by this type.
153    ///
154    /// This does not have to be a human-readable string, and the value will not itself be included in generated schemas.
155    /// If two types produce different schemas, then they **must** have different `schema_id()`s,
156    /// but two types that produce identical schemas should *ideally* have the same `schema_id()`.
157    ///
158    /// The default implementation returns the same value as `schema_name()`.
159    fn schema_id() -> Cow<'static, str> {
160        Cow::Owned(Self::schema_name())
161    }
162
163    /// Generates a JSON Schema for this type.
164    ///
165    /// If the returned schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
166    /// add them to the [`SchemaGenerator`](r#gen::SchemaGenerator)'s schema definitions.
167    ///
168    /// This should not return a `$ref` schema.
169    fn json_schema(generator: &mut r#gen::SchemaGenerator) -> Schema;
170
171    // TODO document and bring into public API?
172    #[doc(hidden)]
173    fn _schemars_private_non_optional_json_schema(
174        generator: &mut r#gen::SchemaGenerator,
175    ) -> Schema {
176        Self::json_schema(generator)
177    }
178
179    // TODO document and bring into public API?
180    #[doc(hidden)]
181    fn _schemars_private_is_option() -> bool {
182        false
183    }
184}
185
186#[cfg(test)]
187pub mod tests {
188    use super::*;
189
190    pub fn schema_object_for<T: JsonSchema>() -> schema::SchemaObject {
191        schema_object(schema_for::<T>())
192    }
193
194    pub fn schema_for<T: JsonSchema>() -> schema::Schema {
195        let mut generator = r#gen::SchemaGenerator::default();
196        T::json_schema(&mut generator)
197    }
198
199    pub fn schema_object(schema: schema::Schema) -> schema::SchemaObject {
200        match schema {
201            schema::Schema::Object(o) => o,
202            s => panic!("Schema was not an object: {:?}", s),
203        }
204    }
205}