schema_analysis/targets/
schemars.rs1use std::error::Error;
4
5use schemars::schema as schemars_types;
6
7use crate::Schema;
8
9impl Schema {
10 pub fn to_json_schema_with_schemars(&self) -> Result<String, impl Error> {
12 self.to_json_schema_with_schemars_version(&Default::default())
13 }
14
15 pub fn to_json_schema_with_schemars_version(
17 &self,
18 version: &JsonSchemaVersion,
19 ) -> Result<String, impl Error> {
20 let settings: schemars::gen::SchemaSettings = version.to_schemars_settings();
21 let mut generator: schemars::gen::SchemaGenerator = settings.into();
22
23 let root = self.to_schemars_schema(&mut generator);
24 serde_json::to_string_pretty(&root)
25 }
26
27 pub fn to_schemars_schema(
29 &self,
30 generator: &mut schemars::gen::SchemaGenerator,
31 ) -> schemars_types::RootSchema {
32 let inner = helpers::inferred_to_schemars(generator, self);
33 helpers::wrap_in_root(inner, generator.settings())
34 }
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Hash)]
39pub enum JsonSchemaVersion {
40 Draft07,
42 Draft2019_09,
44 OpenApi3,
46}
47impl Default for JsonSchemaVersion {
48 fn default() -> Self {
49 Self::Draft2019_09
50 }
51}
52impl JsonSchemaVersion {
53 pub fn to_schemars_settings(&self) -> schemars::gen::SchemaSettings {
55 use schemars::gen::SchemaSettings;
56 match self {
57 JsonSchemaVersion::Draft07 => SchemaSettings::draft07(),
58 JsonSchemaVersion::Draft2019_09 => SchemaSettings::draft2019_09(),
59 JsonSchemaVersion::OpenApi3 => SchemaSettings::openapi3(),
60 }
61 }
62}
63
64mod helpers {
65
66 use std::collections::BTreeSet;
67
68 use schemars::schema as schemars_types;
69
70 use crate::{Field, Schema};
71
72 pub fn wrap_in_root(
74 inner: schemars_types::Schema,
75 settings: &schemars::gen::SchemaSettings,
76 ) -> schemars_types::RootSchema {
77 schemars_types::RootSchema {
78 meta_schema: settings.meta_schema.clone(),
79 definitions: Default::default(),
80 schema: inner.into_object(),
81 }
82 }
83
84 pub fn inferred_to_schemars(
86 generator: &mut schemars::gen::SchemaGenerator,
87 inferred: &Schema,
88 ) -> schemars_types::Schema {
89 match inferred {
93 Schema::Null(_) => generator.subschema_for::<()>(),
94 Schema::Boolean(_) => generator.subschema_for::<bool>(),
95
96 Schema::Integer(_) => schemars_types::SchemaObject {
99 instance_type: Some(schemars_types::InstanceType::Integer.into()),
100 ..Default::default()
101 }
102 .into(),
103 Schema::Float(_) => schemars_types::SchemaObject {
104 instance_type: Some(schemars_types::InstanceType::Number.into()),
105 ..Default::default()
106 }
107 .into(),
108
109 Schema::String(_) => generator.subschema_for::<String>(),
110 Schema::Bytes(_) => generator.subschema_for::<Vec<u8>>(),
111
112 Schema::Sequence { field, .. } => schemars_types::SchemaObject {
113 instance_type: Some(schemars_types::InstanceType::Array.into()),
114 array: Some(Box::new(schemars_types::ArrayValidation {
115 items: Some(internal_field_to_schemars_schema(generator, field).into()),
116 ..Default::default()
117 })),
118 ..Default::default()
119 }
120 .into(),
121
122 Schema::Struct { fields, .. } => {
123 let required: BTreeSet<String> = fields
124 .iter()
125 .filter(|(_, v)| !v.status.may_be_missing)
127 .map(|(k, _)| k.clone())
128 .collect();
129 let properties = fields
130 .iter()
131 .map(|(k, field)| {
132 (
133 k.clone(),
134 internal_field_to_schemars_schema(generator, field),
135 )
136 })
137 .collect();
138 schemars_types::SchemaObject {
139 instance_type: Some(schemars_types::InstanceType::Object.into()),
140 object: Some(Box::new(schemars_types::ObjectValidation {
141 required,
142 properties,
143 ..Default::default()
144 })),
145 ..Default::default()
146 }
147 .into()
148 }
149
150 Schema::Union { variants } => {
151 let json_schemas = variants
152 .iter()
153 .map(|s| inferred_to_schemars(generator, s))
154 .collect();
155 schemars_types::SchemaObject {
156 subschemas: Some(Box::new(schemars_types::SubschemaValidation {
157 any_of: Some(json_schemas),
158 ..Default::default()
159 })),
160 ..Default::default()
161 }
162 .into()
163 }
164 }
165 }
166
167 fn internal_field_to_schemars_schema(
169 generator: &mut schemars::gen::SchemaGenerator,
170 field: &Field,
171 ) -> schemars_types::Schema {
172 let mut schema = match &field.schema {
177 Some(schema) => inferred_to_schemars(generator, schema),
178 None => schemars_types::Schema::Bool(true),
179 };
180
181 if field.status.may_be_null {
182 if generator.settings().option_add_null_type {
185 schema = match schema {
186 schemars_types::Schema::Bool(true) => schemars_types::Schema::Bool(true),
187 schemars_types::Schema::Bool(false) => generator.subschema_for::<()>(),
188 schemars_types::Schema::Object(schemars_types::SchemaObject {
189 instance_type: Some(ref mut instance_type),
190 ..
191 }) => {
192 add_null_type(instance_type);
193 schema
194 }
195 schema => schemars_types::SchemaObject {
196 subschemas: Some(Box::new(schemars_types::SubschemaValidation {
198 any_of: Some(vec![schema, generator.subschema_for::<()>()]),
199 ..Default::default()
200 })),
201 ..Default::default()
202 }
203 .into(),
204 }
205 }
206 if generator.settings().option_nullable {
207 let mut schema_obj = schema.into_object();
208 schema_obj
209 .extensions
210 .insert("nullable".to_owned(), serde_json::json!(true));
211 schema = schemars_types::Schema::Object(schema_obj);
212 };
213 }
214 schema
215 }
216
217 fn add_null_type(
220 instance_type: &mut schemars_types::SingleOrVec<schemars_types::InstanceType>,
221 ) {
222 match instance_type {
223 schemars_types::SingleOrVec::Single(ty)
224 if **ty != schemars_types::InstanceType::Null =>
225 {
226 *instance_type = vec![**ty, schemars_types::InstanceType::Null].into()
227 }
228 schemars_types::SingleOrVec::Vec(ty)
229 if !ty.contains(&schemars_types::InstanceType::Null) =>
230 {
231 ty.push(schemars_types::InstanceType::Null)
232 }
233 _ => {}
234 };
235 }
236}