schemars/json_schema_impls/
core.rs

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