schemars/json_schema_impls/
core.rs

1use crate::gen::SchemaGenerator;
2use crate::schema::*;
3use crate::JsonSchema;
4use serde_json::json;
5use std::borrow::Cow;
6use std::ops::{Bound, Range, RangeInclusive};
7
8impl<T: JsonSchema> JsonSchema for Option<T> {
9    no_ref_schema!();
10
11    fn schema_name() -> String {
12        format!("Nullable_{}", T::schema_name())
13    }
14
15    fn schema_id() -> Cow<'static, str> {
16        Cow::Owned(format!("Option<{}>", T::schema_id()))
17    }
18
19    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
20        let mut schema = gen.subschema_for::<T>();
21        if gen.settings().option_add_null_type {
22            schema = match schema {
23                Schema::Bool(true) => Schema::Bool(true),
24                Schema::Bool(false) => <()>::json_schema(gen),
25                Schema::Object(SchemaObject {
26                    instance_type: Some(ref mut instance_type),
27                    ..
28                }) => {
29                    add_null_type(instance_type);
30                    schema
31                }
32                schema => SchemaObject {
33                    // TODO technically the schema already accepts null, so this may be unnecessary
34                    subschemas: Some(Box::new(SubschemaValidation {
35                        any_of: Some(vec![schema, <()>::json_schema(gen)]),
36                        ..Default::default()
37                    })),
38                    ..Default::default()
39                }
40                .into(),
41            }
42        }
43        if gen.settings().option_nullable {
44            let mut schema_obj = schema.into_object();
45            schema_obj
46                .extensions
47                .insert("nullable".to_owned(), json!(true));
48            schema = Schema::Object(schema_obj);
49        };
50        schema
51    }
52
53    fn _schemars_private_non_optional_json_schema(gen: &mut SchemaGenerator) -> Schema {
54        T::_schemars_private_non_optional_json_schema(gen)
55    }
56
57    fn _schemars_private_is_option() -> bool {
58        true
59    }
60}
61
62fn add_null_type(instance_type: &mut SingleOrVec<InstanceType>) {
63    match instance_type {
64        SingleOrVec::Single(ty) if **ty != InstanceType::Null => {
65            *instance_type = vec![**ty, InstanceType::Null].into()
66        }
67        SingleOrVec::Vec(ty) if !ty.contains(&InstanceType::Null) => ty.push(InstanceType::Null),
68        _ => {}
69    };
70}
71
72impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
73    fn schema_name() -> String {
74        format!("Result_of_{}_or_{}", T::schema_name(), E::schema_name())
75    }
76
77    fn schema_id() -> Cow<'static, str> {
78        Cow::Owned(format!("Result<{}, {}>", T::schema_id(), E::schema_id()))
79    }
80
81    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
82        let mut ok_schema = SchemaObject {
83            instance_type: Some(InstanceType::Object.into()),
84            ..Default::default()
85        };
86        let obj = ok_schema.object();
87        obj.required.insert("Ok".to_owned());
88        obj.properties
89            .insert("Ok".to_owned(), gen.subschema_for::<T>());
90
91        let mut err_schema = SchemaObject {
92            instance_type: Some(InstanceType::Object.into()),
93            ..Default::default()
94        };
95        let obj = err_schema.object();
96        obj.required.insert("Err".to_owned());
97        obj.properties
98            .insert("Err".to_owned(), gen.subschema_for::<E>());
99
100        let mut schema = SchemaObject::default();
101        schema.subschemas().one_of = Some(vec![ok_schema.into(), err_schema.into()]);
102        schema.into()
103    }
104}
105
106impl<T: JsonSchema> JsonSchema for Bound<T> {
107    fn schema_name() -> String {
108        format!("Bound_of_{}", T::schema_name())
109    }
110
111    fn schema_id() -> Cow<'static, str> {
112        Cow::Owned(format!("Bound<{}>", T::schema_id()))
113    }
114
115    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
116        let mut included_schema = SchemaObject {
117            instance_type: Some(InstanceType::Object.into()),
118            ..Default::default()
119        };
120        let obj = included_schema.object();
121        obj.required.insert("Included".to_owned());
122        obj.properties
123            .insert("Included".to_owned(), gen.subschema_for::<T>());
124
125        let mut excluded_schema = SchemaObject {
126            instance_type: Some(InstanceType::Object.into()),
127            ..Default::default()
128        };
129        let obj = excluded_schema.object();
130        obj.required.insert("Excluded".to_owned());
131        obj.properties
132            .insert("Excluded".to_owned(), gen.subschema_for::<T>());
133
134        let unbounded_schema = SchemaObject {
135            instance_type: Some(InstanceType::String.into()),
136            const_value: Some(json!("Unbounded")),
137            ..Default::default()
138        };
139
140        let mut schema = SchemaObject::default();
141        schema.subschemas().one_of = Some(vec![
142            included_schema.into(),
143            excluded_schema.into(),
144            unbounded_schema.into(),
145        ]);
146        schema.into()
147    }
148}
149
150impl<T: JsonSchema> JsonSchema for Range<T> {
151    fn schema_name() -> String {
152        format!("Range_of_{}", T::schema_name())
153    }
154
155    fn schema_id() -> Cow<'static, str> {
156        Cow::Owned(format!("Range<{}>", T::schema_id()))
157    }
158
159    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
160        let mut schema = SchemaObject {
161            instance_type: Some(InstanceType::Object.into()),
162            ..Default::default()
163        };
164        let obj = schema.object();
165        obj.required.insert("start".to_owned());
166        obj.required.insert("end".to_owned());
167        obj.properties
168            .insert("start".to_owned(), gen.subschema_for::<T>());
169        obj.properties
170            .insert("end".to_owned(), gen.subschema_for::<T>());
171        schema.into()
172    }
173}
174
175forward_impl!((<T: JsonSchema> JsonSchema for RangeInclusive<T>) => Range<T>);
176
177forward_impl!((<T: ?Sized> JsonSchema for std::marker::PhantomData<T>) => ());
178
179forward_impl!((<'a> JsonSchema for std::fmt::Arguments<'a>) => String);
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184    use crate::tests::{schema_for, schema_object_for};
185    use pretty_assertions::assert_eq;
186
187    #[test]
188    fn schema_for_option() {
189        let schema = schema_object_for::<Option<i32>>();
190        assert_eq!(
191            schema.instance_type,
192            Some(vec![InstanceType::Integer, InstanceType::Null].into())
193        );
194        assert_eq!(schema.extensions.get("nullable"), None);
195        assert_eq!(schema.subschemas.is_none(), true);
196    }
197
198    #[test]
199    fn schema_for_option_with_ref() {
200        use crate as schemars;
201        #[derive(JsonSchema)]
202        struct Foo;
203
204        let schema = schema_object_for::<Option<Foo>>();
205        assert_eq!(schema.instance_type, None);
206        assert_eq!(schema.extensions.get("nullable"), None);
207        assert_eq!(schema.subschemas.is_some(), true);
208        let any_of = schema.subschemas.unwrap().any_of.unwrap();
209        assert_eq!(any_of.len(), 2);
210        assert_eq!(any_of[0], Schema::new_ref("#/definitions/Foo".to_string()));
211        assert_eq!(any_of[1], schema_for::<()>());
212    }
213
214    #[test]
215    fn schema_for_result() {
216        let schema = schema_object_for::<Result<bool, String>>();
217        let one_of = schema.subschemas.unwrap().one_of.unwrap();
218        assert_eq!(one_of.len(), 2);
219
220        let ok_schema: SchemaObject = one_of[0].clone().into();
221        let obj = ok_schema.object.unwrap();
222        assert!(obj.required.contains("Ok"));
223        assert_eq!(obj.properties["Ok"], schema_for::<bool>());
224
225        let err_schema: SchemaObject = one_of[1].clone().into();
226        let obj = err_schema.object.unwrap();
227        assert!(obj.required.contains("Err"));
228        assert_eq!(obj.properties["Err"], schema_for::<String>());
229    }
230}