oxihuman_core/
json_schema_validator.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, PartialEq)]
9pub enum SchemaType {
10 Null,
11 Boolean,
12 Integer,
13 Number,
14 String,
15 Array,
16 Object,
17}
18
19#[derive(Debug, Clone)]
21pub struct SchemaNode {
22 pub schema_type: Option<SchemaType>,
23 pub required: Vec<String>,
24 pub min_length: Option<usize>,
25 pub max_length: Option<usize>,
26 pub minimum: Option<f64>,
27 pub maximum: Option<f64>,
28 pub description: Option<String>,
29}
30
31#[allow(clippy::derivable_impls)]
32impl Default for SchemaNode {
33 fn default() -> Self {
34 SchemaNode {
35 schema_type: None,
36 required: vec![],
37 min_length: None,
38 max_length: None,
39 minimum: None,
40 maximum: None,
41 description: None,
42 }
43 }
44}
45
46impl SchemaNode {
47 pub fn new() -> Self {
49 Self::default()
50 }
51
52 pub fn with_type(mut self, t: SchemaType) -> Self {
54 self.schema_type = Some(t);
55 self
56 }
57
58 pub fn require(mut self, field: &str) -> Self {
60 self.required.push(field.to_string());
61 self
62 }
63
64 pub fn with_minimum(mut self, v: f64) -> Self {
66 self.minimum = Some(v);
67 self
68 }
69
70 pub fn with_maximum(mut self, v: f64) -> Self {
72 self.maximum = Some(v);
73 self
74 }
75}
76
77#[derive(Debug, Clone, PartialEq)]
79pub struct ValidationError {
80 pub path: String,
81 pub message: String,
82}
83
84pub fn validate_number(schema: &SchemaNode, value: f64, path: &str) -> Vec<ValidationError> {
86 let mut errs = vec![];
87 if let Some(min) = schema.minimum {
88 if value < min {
89 errs.push(ValidationError {
90 path: path.to_string(),
91 message: format!("{value} < minimum {min}"),
92 });
93 }
94 }
95 if let Some(max) = schema.maximum {
96 if value > max {
97 errs.push(ValidationError {
98 path: path.to_string(),
99 message: format!("{value} > maximum {max}"),
100 });
101 }
102 }
103 errs
104}
105
106pub fn validate_string(schema: &SchemaNode, value: &str, path: &str) -> Vec<ValidationError> {
108 let mut errs = vec![];
109 let len = value.len();
110 if let Some(min) = schema.min_length {
111 if len < min {
112 errs.push(ValidationError {
113 path: path.to_string(),
114 message: format!("string length {len} < minLength {min}"),
115 });
116 }
117 }
118 if let Some(max) = schema.max_length {
119 if len > max {
120 errs.push(ValidationError {
121 path: path.to_string(),
122 message: format!("string length {len} > maxLength {max}"),
123 });
124 }
125 }
126 errs
127}
128
129pub fn is_required(schema: &SchemaNode, field: &str) -> bool {
131 schema.required.iter().any(|r| r == field)
132}
133
134pub fn required_count(schema: &SchemaNode) -> usize {
136 schema.required.len()
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_schema_default() {
145 let s = SchemaNode::new();
147 assert!(s.schema_type.is_none());
148 }
149
150 #[test]
151 fn test_with_type() {
152 let s = SchemaNode::new().with_type(SchemaType::String);
154 assert_eq!(s.schema_type, Some(SchemaType::String));
155 }
156
157 #[test]
158 fn test_require_adds_field() {
159 let s = SchemaNode::new().require("name").require("age");
161 assert_eq!(s.required.len(), 2);
162 }
163
164 #[test]
165 fn test_is_required_true() {
166 let s = SchemaNode::new().require("email");
168 assert!(is_required(&s, "email"));
169 }
170
171 #[test]
172 fn test_is_required_false() {
173 let s = SchemaNode::new().require("email");
175 assert!(!is_required(&s, "phone"));
176 }
177
178 #[test]
179 fn test_validate_number_ok() {
180 let s = SchemaNode::new().with_minimum(0.0).with_maximum(100.0);
182 assert!(validate_number(&s, 50.0, "/x").is_empty());
183 }
184
185 #[test]
186 fn test_validate_number_below_min() {
187 let s = SchemaNode::new().with_minimum(10.0);
189 assert!(!validate_number(&s, 5.0, "/x").is_empty());
190 }
191
192 #[test]
193 fn test_validate_string_ok() {
194 let mut s = SchemaNode::new();
196 s.min_length = Some(2);
197 s.max_length = Some(10);
198 assert!(validate_string(&s, "hello", "/s").is_empty());
199 }
200
201 #[test]
202 fn test_validate_string_too_short() {
203 let mut s = SchemaNode::new();
205 s.min_length = Some(5);
206 assert!(!validate_string(&s, "hi", "/s").is_empty());
207 }
208
209 #[test]
210 fn test_required_count() {
211 let s = SchemaNode::new().require("a").require("b").require("c");
213 assert_eq!(required_count(&s), 3);
214 }
215}