1use serde::{Deserialize, Serialize};
4
5use std::{collections::HashMap, str::Split};
6
7use crate::{validation::NumberCriteria, Schema};
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11#[serde(untagged)]
12pub enum Property {
13 Value(PropertyInstance),
14 Ref(RefProperty),
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
18pub struct RefProperty {
19 #[serde(rename = "$ref")]
20 pub reference: String,
21}
22
23#[derive(Debug)]
24enum Data<'a> {
25 Map(&'a HashMap<String, Property>),
26 Prop(&'a Property),
27 Instance(&'a PropertyInstance),
28 Schema(&'a Schema),
29}
30
31fn get_items(p: &Property) -> Option<&PropertyInstance> {
32 match p {
33 Property::Value(PropertyInstance::Array { items }) => Some(&**items),
34 _ => None,
35 }
36}
37
38fn get_properties_instance(p: &PropertyInstance) -> Option<&HashMap<String, Property>> {
39 match p {
40 PropertyInstance::Object { properties, .. } => Some(properties),
41 _ => None,
42 }
43}
44
45fn get_properties(p: &Property) -> Option<&HashMap<String, Property>> {
46 match p {
47 Property::Value(v) => get_properties_instance(v),
48 _ => None,
49 }
50}
51
52fn find_ref<'a>(mut path: Split<'a, char>, mut data: Data<'a>) -> Option<Data<'a>> {
53 loop {
54 let Some(branch) = path.next() else {
55 return Some(data);
56 };
57 data = match (branch, data) {
58 ("properties", Data::Instance(v)) => Data::Map(get_properties_instance(v)?),
59 ("properties", Data::Map(v)) => Data::Map(get_properties(v.get(branch)?)?),
60 ("properties", Data::Prop(v)) => Data::Map(get_properties(v)?),
61 ("properties", Data::Schema(v)) => Data::Map(v.properties()?),
62 ("items", Data::Prop(v)) => Data::Instance(get_items(v)?),
63 (_, Data::Map(v)) => Data::Prop(v.get(branch)?),
64 _ => return None,
65 };
66 }
67}
68
69impl RefProperty {
70 pub fn deref<'a>(&'a self, schema: &'a Schema) -> Option<&PropertyInstance> {
71 let reference = self.reference.strip_prefix("#/")?;
72 let path = reference.split('/');
73 match find_ref(path, Data::Schema(schema))? {
74 Data::Prop(v) => match v {
75 Property::Ref(v) => Some(v.deref(schema)?),
76 Property::Value(v) => Some(v),
77 },
78 Data::Instance(v) => Some(v),
79 _ => None,
80 }
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
86#[serde(tag = "type", rename_all = "camelCase")]
87pub enum PropertyInstance {
88 Null,
89
90 Boolean,
91
92 Integer {
93 #[serde(flatten)]
94 criteria: NumberCriteria,
95 },
96 Object {
97 properties: HashMap<String, Property>,
98 required: Option<Vec<String>>,
99 },
100
101 Array {
102 items: Box<PropertyInstance>,
103 },
104
105 Number {
106 #[serde(flatten)]
107 criteria: NumberCriteria,
108 },
109
110 String,
111}
112
113impl PropertyInstance {
114 pub fn validate(&self, json: &serde_json::Value) -> Result<(), Vec<String>> {
116 use serde_json::Value;
117 use PropertyInstance::*;
118
119 match (&self, json) {
120 (Null, Value::Null) => Ok(()),
121 (Null, unexpected_value) => {
122 Err(vec![format!("expected null found {:?}", unexpected_value)])
123 }
124
125 (Boolean, Value::Bool(_)) => Ok(()),
126 (Boolean, unexpected_value) => Err(vec![format!(
127 "expected boolean found {:?}",
128 unexpected_value
129 )]),
130
131 (String, Value::String(_)) => Ok(()),
132 (String, unexpected_value) => Err(vec![format!(
133 "expected string found {:?}",
134 unexpected_value
135 )]),
136
137 (Number { .. }, Value::Number(_)) => Ok(()),
138 (Number { .. }, unexpected_value) => Err(vec![format!(
139 "expected number found {:?}",
140 unexpected_value
141 )]),
142
143 (Integer { .. }, Value::Number(i)) if i.is_i64() => Ok(()),
144 (Integer { .. }, unexpected_value) => Err(vec![format!(
145 "expected integer found {:?}",
146 unexpected_value
147 )]),
148
149 (Array { items }, Value::Array(elems)) => {
150 let errors: Vec<std::string::String> = elems
151 .iter()
152 .map(|value| items.validate(value))
153 .filter_map(Result::err)
154 .flat_map(|errors| errors.into_iter())
155 .collect();
156 if errors.is_empty() {
157 Ok(())
158 } else {
159 Err(errors)
160 }
161 }
162 (Array { .. }, unexpected_value) => {
163 Err(vec![format!("expected array found {:?}", unexpected_value)])
164 }
165
166 (
167 Object {
168 properties,
169 required,
170 },
171 Value::Object(object),
172 ) => {
173 let errors: Vec<std::string::String> = properties
174 .iter()
175 .filter_map(|(k, schema)| {
176 object
177 .get(k)
178 .map(|v| match schema {
179 Property::Value(schema) => schema.validate(v).err(),
180 Property::Ref(_schema) => unimplemented!(),
181 })
182 .unwrap_or_else(|| {
183 if required.iter().flat_map(|v| v.iter()).any(|x| x == k) {
184 Some(vec![format!(
185 "object doesn't contain the required property {:?}",
186 k
187 )])
188 } else {
189 None
190 }
191 })
192 })
193 .flat_map(|errors| errors.into_iter())
194 .collect();
195 if errors.is_empty() {
196 Ok(())
197 } else {
198 Err(errors)
199 }
200 }
201
202 (Object { .. }, _) => Err(vec![format!("invalid object")]),
203 }
204 }
205}