1use indexmap::map::IndexMap;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5#[derive(Clone, Debug, Deserialize, Serialize)]
7pub struct SchemaFile {
8 #[serde(flatten)]
9 pub metadata: Metadata,
10 #[serde(flatten)]
11 pub schema: Option<SchemaDef>,
12 #[serde(rename = "$defs")]
13 pub definitions: Option<IndexMap<String, Schema>>,
14}
15
16impl SchemaFile {
17 pub fn objects(&self) -> Vec<(Vec<String>, SchemaObject)> {
18 let mut result = vec![];
19
20 if let Some(schema) = &self.schema {
21 Self::objects_rec(&Schema::new(schema), &[], &mut result);
22 }
23
24 if let Some(definitions) = &self.definitions {
25 for (key, value) in definitions {
26 Self::objects_rec(value, &[key.clone()], &mut result);
27 }
28 }
29
30 result
31 }
32
33 fn objects_rec(schema: &Schema, path: &[String], acc: &mut Vec<(Vec<String>, SchemaObject)>) {
34 match &schema.schema {
35 SchemaDef::Type(SchemaType::Array { items, .. }) => {
36 let mut new_path = path.to_vec();
37 new_path.push("array".to_string());
38
39 Self::objects_rec(items, &new_path, acc);
40 }
41 SchemaDef::Type(other) => {
42 if let Some(object) = other.as_object() {
43 acc.push((path.to_vec(), object.clone()));
44
45 for (key, value) in &object.properties {
46 let mut new_path = path.to_vec();
47 new_path.push(key.clone());
48
49 Self::objects_rec(value, &new_path, acc);
50 }
51 }
52 }
53 SchemaDef::OneOf { value } => {
54 for (i, schema) in value.iter().enumerate() {
55 let mut new_path = path.to_vec();
56 new_path.push(format!("oneOf[{}]", i));
57
58 Self::objects_rec(schema, &new_path, acc);
59 }
60 }
61 SchemaDef::AnyOf { value } => {
62 for (i, schema) in value.iter().enumerate() {
63 let mut new_path = path.to_vec();
64 new_path.push(format!("anyOf[{}]", i));
65
66 Self::objects_rec(schema, &new_path, acc);
67 }
68 }
69 SchemaDef::AllOf { value } => {
70 for (i, schema) in value.iter().enumerate() {
71 let mut new_path = path.to_vec();
72 new_path.push(format!("allOf[{}]", i));
73
74 Self::objects_rec(schema, &new_path, acc);
75 }
76 }
77 _ => {}
78 }
79 }
80}
81
82#[derive(Clone, Debug, Deserialize, Serialize)]
83pub struct Schema {
84 #[serde(flatten)]
85 pub metadata: Metadata,
86 #[serde(flatten)]
87 pub schema: SchemaDef,
88}
89
90impl Schema {
91 fn new(schema: &SchemaDef) -> Self {
92 Self {
93 metadata: Metadata::default(),
94 schema: schema.clone(),
95 }
96 }
97}
98
99#[derive(Clone, Default, Debug, Deserialize, Serialize)]
100#[serde(deny_unknown_fields)]
101pub struct Metadata {
102 #[serde(rename = "$id")]
103 pub id: Option<String>,
104 pub title: Option<String>,
105 pub description: Option<String>,
106 #[serde(rename = "$comment")]
107 pub comment: Option<String>,
108 pub examples: Option<Vec<Value>>,
109}
110
111#[derive(Clone, Debug, Deserialize, Serialize)]
112#[serde(untagged)]
113#[serde(deny_unknown_fields)]
114pub enum SchemaDef {
115 Type(SchemaType),
116 Ref {
117 #[serde(rename = "$ref")]
118 value: String,
119 },
120 Enum {
121 #[serde(rename = "enum")]
122 value: Vec<String>,
123 },
124 Const {
125 #[serde(rename = "const")]
126 value: Value,
127 },
128 OneOf {
129 #[serde(rename = "oneOf")]
130 value: Vec<Schema>,
131 },
132 AnyOf {
133 #[serde(rename = "anyOf")]
134 value: Vec<Schema>,
135 },
136 AllOf {
137 #[serde(rename = "allOf")]
138 value: Vec<Schema>,
139 },
140 Empty {},
141}
142
143#[derive(Clone, Debug, Deserialize, Serialize)]
144#[serde(tag = "type")]
145#[serde(deny_unknown_fields)]
146pub enum SchemaType {
147 #[serde(rename = "null")]
148 Null {},
149 #[serde(rename = "boolean")]
150 Boolean {},
151 #[serde(rename = "string")]
152 String { pattern: Option<String> },
153 #[serde(rename = "integer")]
154 Integer {
155 minimum: Option<i64>,
156 maximum: Option<i64>,
157 },
158 #[serde(rename = "number")]
159 Number {
160 minimum: Option<f64>,
161 maximum: Option<f64>,
162 },
163 #[serde(rename = "array")]
164 Array {
165 items: Box<Schema>,
166 #[serde(rename = "minItems")]
167 min_items: Option<usize>,
168 #[serde(rename = "maxItems")]
169 max_items: Option<usize>,
170 },
171 #[serde(rename = "object")]
172 Object(SchemaObject),
173}
174
175impl SchemaType {
176 fn as_object(&self) -> Option<SchemaObject> {
177 match self {
178 Self::Object(object) => Some(object.clone()),
179 _ => None,
180 }
181 }
182}
183
184#[derive(Clone, Debug, Deserialize, Serialize)]
185#[serde(deny_unknown_fields)]
186pub struct SchemaObject {
187 #[serde(rename = "additionalProperties", default)]
188 pub additional_properties: AdditionalProperties,
189 #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
190 pub properties: IndexMap<String, Schema>,
191 #[serde(default, skip_serializing_if = "Vec::is_empty")]
192 pub required: Vec<String>,
193}
194
195impl SchemaObject {
196 pub fn no_additional_properties(&self) -> bool {
197 matches!(
198 self.additional_properties,
199 AdditionalProperties::Boolean(false)
200 )
201 }
202}
203
204#[derive(Clone, Debug, Deserialize, Serialize)]
205#[serde(untagged)]
206#[serde(deny_unknown_fields)]
207pub enum AdditionalProperties {
208 Boolean(bool),
209 Schema(Box<Schema>),
210}
211
212impl Default for AdditionalProperties {
213 fn default() -> Self {
214 Self::Boolean(true)
215 }
216}