jsona_schema_validator/
lib.rs1mod validates;
2
3use std::convert::TryFrom;
4
5use indexmap::IndexMap;
6use jsona::dom::{visit_annotations, Key, KeyOrIndex, Keys, Node};
7use jsona_schema::{SchemaError, SchemaResult, SchemaType};
8pub use validates::Error as JSONASchemaValidationError;
9
10pub use jsona_schema::Schema;
11
12pub const VALUE_KEY: &str = "_";
13
14#[derive(Debug)]
15pub struct JSONASchemaValidator {
16 schema: Schema,
17 annotations: Schema,
18}
19
20impl JSONASchemaValidator {
21 pub fn validate(&self, node: &Node) -> Vec<JSONASchemaValidationError> {
22 let mut collect_errors = vec![];
23 let default_defs = Default::default();
24 let defs = self.schema.defs.as_ref().unwrap_or(&default_defs);
25 if let Some(value_schema) = self.get_entry_schema(VALUE_KEY) {
26 collect_errors.extend(validates::validate(
27 defs,
28 value_schema,
29 &Keys::default(),
30 node,
31 ));
32 }
33 for (keys, value) in visit_annotations(node).into_iter() {
34 if let Some(key) = keys.last_annotation_key() {
35 if let Some(schema) = self.get_entry_schema(key.value()) {
36 collect_errors.extend(validates::validate(defs, schema, &keys, &value));
37 }
38 }
39 }
40 collect_errors
41 }
42
43 pub fn pointer(&self, keys: &Keys) -> Vec<&Schema> {
44 let (annotation_key, keys) = keys.shift_annotation();
45 let new_keys = match annotation_key {
46 Some(key) => {
47 if !self.contains_annotation_key(key.value()) {
48 return vec![&self.annotations];
49 }
50 let new_keys = Keys::new(
51 [
52 KeyOrIndex::property(key.annotation_name().unwrap()),
53 KeyOrIndex::property("value"),
54 ]
55 .into_iter(),
56 );
57 new_keys.extend(keys)
58 }
59 None => {
60 let new_keys = Keys::new(
61 [
62 KeyOrIndex::property(VALUE_KEY),
63 KeyOrIndex::property("value"),
64 ]
65 .into_iter(),
66 );
67 new_keys.extend(keys)
68 }
69 };
70 self.schema.pointer(&new_keys)
71 }
72
73 pub fn get_entry_schema(&self, key: &str) -> Option<&Schema> {
74 let properties = self.schema.properties.as_ref()?;
75 let schema = properties.get(key)?;
76 let properties = schema.properties.as_ref()?;
77 properties.get("value")
78 }
79
80 pub fn contains_annotation_key(&self, key: &str) -> bool {
81 match self.annotations.properties.as_ref() {
82 Some(properties) => properties.contains_key(key),
83 None => false,
84 }
85 }
86}
87
88impl TryFrom<&Node> for JSONASchemaValidator {
89 type Error = Vec<SchemaError>;
90 fn try_from(value: &Node) -> Result<Self, Self::Error> {
91 let object = match value.as_object() {
92 Some(v) => v,
93 None => {
94 return Err(vec![SchemaError::InvalidSchemaValue {
95 keys: Keys::default(),
96 error: "must be object".into(),
97 }])
98 }
99 };
100 let mut annotation_schemas = IndexMap::default();
101 let mut errors = vec![];
102 for (key, value) in object.value().read().iter() {
103 let keys = Keys::single(key.clone());
104 if key.is_quote() {
105 errors.push(SchemaError::InvalidSchemaValue {
106 keys,
107 error: "invalid name".into(),
108 });
109 continue;
110 }
111 let object = match value.as_object() {
112 Some(v) => v,
113 None => {
114 errors.push(SchemaError::InvalidSchemaValue {
115 keys,
116 error: "must be object".into(),
117 });
118 continue;
119 }
120 };
121 let value_key = Key::property("value");
122 let keys = keys.join(value_key.clone());
123 match object.get(&value_key) {
124 Some(value) => {
125 if key.value() != VALUE_KEY {
126 let schema = Schema {
127 description: parse_string_annotation(&keys, &value, "@describe")
128 .ok()
129 .flatten(),
130 schema_type: SchemaType::from_node(&value).map(|v| v.into()),
131 ..Default::default()
132 };
133 annotation_schemas.insert(format!("@{}", key.value()), schema);
134 }
135 }
136 None => {
137 errors.push(SchemaError::InvalidSchemaValue {
138 keys,
139 error: "must be object".into(),
140 });
141 continue;
142 }
143 }
144 }
145 if !errors.is_empty() {
146 return Err(errors);
147 }
148 let schema = Schema::try_from(value)?;
149 let annotations = Schema {
150 schema_type: Some(SchemaType::Object.into()),
151 properties: if annotation_schemas.is_empty() {
152 None
153 } else {
154 Some(annotation_schemas)
155 },
156 ..Default::default()
157 };
158 Ok(JSONASchemaValidator {
159 schema,
160 annotations,
161 })
162 }
163}
164
165fn parse_string_annotation(keys: &Keys, node: &Node, name: &str) -> SchemaResult<Option<String>> {
166 match node.get_as_string(name) {
167 Some((_, Some(value))) => Ok(Some(value.value().to_string())),
168 Some((key, None)) => Err(vec![SchemaError::UnexpectedType {
169 keys: keys.clone().join(key),
170 }]),
171 None => Ok(None),
172 }
173}