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 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}