json_schema_tools/
schema.rs

1use indexmap::map::IndexMap;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5/// A schema file that may contain a top-level schema and related definitions
6#[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}