naru_core/core/
schema_model.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Serialize, Deserialize, Clone)]
4pub struct SchemaFile {
5 pub version: String,
6 pub fields: Vec<FieldDefinition>,
7}
8
9#[derive(Debug, Serialize, Deserialize, Clone)]
10pub struct FieldDefinition {
11 pub key: String,
12 pub r#type: String,
13 pub description: Option<String>,
14 pub validation: Option<ValidationRules>,
15 pub is_secret: bool,
16}
17
18#[derive(Debug, Serialize, Deserialize, Clone)]
19pub struct ValidationRules {
20 pub min_length: Option<usize>,
21 pub max_length: Option<usize>,
22 pub min_value: Option<i64>,
23 pub max_value: Option<i64>,
24 pub pattern: Option<String>,
25}
26
27impl SchemaFile {
28 pub fn new(version: &str) -> Self {
29 SchemaFile {
30 version: version.to_string(),
31 fields: Vec::new(),
32 }
33 }
34
35 pub fn add_field(&mut self, field: FieldDefinition) {
36 self.fields.push(field);
37 }
38
39 pub fn get_field(&self, key: &str) -> Option<&FieldDefinition> {
40 self.fields.iter().find(|f| f.key == key)
41 }
42
43 pub fn get_field_mut(&mut self, key: &str) -> Option<&mut FieldDefinition> {
44 self.fields.iter_mut().find(|f| f.key == key)
45 }
46
47 pub fn remove_field(&mut self, key: &str) -> Option<FieldDefinition> {
48 if let Some(pos) = self.fields.iter().position(|f| f.key == key) {
49 Some(self.fields.remove(pos))
50 } else {
51 None
52 }
53 }
54
55 pub fn is_empty(&self) -> bool {
56 self.fields.is_empty()
57 }
58
59 pub fn len(&self) -> usize {
60 self.fields.len()
61 }
62
63 pub fn iter(&self) -> impl Iterator<Item = &FieldDefinition> {
64 self.fields.iter()
65 }
66}
67
68impl FieldDefinition {
69 pub fn new(key: &str, r#type: &str) -> Self {
70 FieldDefinition {
71 key: key.to_string(),
72 r#type: r#type.to_string(),
73 description: None,
74 validation: None,
75 is_secret: false,
76 }
77 }
78
79 pub fn with_description(mut self, description: &str) -> Self {
80 self.description = Some(description.to_string());
81 self
82 }
83
84 pub fn with_validation(mut self, validation: ValidationRules) -> Self {
85 self.validation = Some(validation);
86 self
87 }
88
89 pub fn secret(mut self) -> Self {
90 self.is_secret = true;
91 self
92 }
93
94 pub fn is_string(&self) -> bool {
95 self.r#type == "string"
96 }
97
98 pub fn is_integer(&self) -> bool {
99 self.r#type == "integer"
100 }
101
102 pub fn is_boolean(&self) -> bool {
103 self.r#type == "boolean"
104 }
105}
106
107impl ValidationRules {
108 pub fn new() -> Self {
109 ValidationRules {
110 min_length: None,
111 max_length: None,
112 min_value: None,
113 max_value: None,
114 pattern: None,
115 }
116 }
117
118 pub fn with_min_length(mut self, min: usize) -> Self {
119 self.min_length = Some(min);
120 self
121 }
122
123 pub fn with_max_length(mut self, max: usize) -> Self {
124 self.max_length = Some(max);
125 self
126 }
127
128 pub fn with_length_range(mut self, min: usize, max: usize) -> Self {
129 self.min_length = Some(min);
130 self.max_length = Some(max);
131 self
132 }
133
134 pub fn with_min_value(mut self, min: i64) -> Self {
135 self.min_value = Some(min);
136 self
137 }
138
139 pub fn with_max_value(mut self, max: i64) -> Self {
140 self.max_value = Some(max);
141 self
142 }
143
144 pub fn with_value_range(mut self, min: i64, max: i64) -> Self {
145 self.min_value = Some(min);
146 self.max_value = Some(max);
147 self
148 }
149
150 pub fn with_pattern(mut self, pattern: &str) -> Self {
151 self.pattern = Some(pattern.to_string());
152 self
153 }
154}
155
156impl Default for ValidationRules {
157 fn default() -> Self {
158 Self::new()
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_schema_file_new() {
168 let schema = SchemaFile::new("1.0.0");
169 assert_eq!(schema.version, "1.0.0");
170 assert!(schema.fields.is_empty());
171 }
172
173 #[test]
174 fn test_schema_file_add_field() {
175 let mut schema = SchemaFile::new("1.0.0");
176 let field = FieldDefinition::new("username", "string");
177 schema.add_field(field);
178 assert_eq!(schema.len(), 1);
179 }
180
181 #[test]
182 fn test_schema_file_get_field() {
183 let mut schema = SchemaFile::new("1.0.0");
184 let field = FieldDefinition::new("username", "string");
185 schema.add_field(field);
186
187 assert!(schema.get_field("username").is_some());
188 assert!(schema.get_field("nonexistent").is_none());
189 }
190
191 #[test]
192 fn test_schema_file_remove_field() {
193 let mut schema = SchemaFile::new("1.0.0");
194 let field = FieldDefinition::new("username", "string");
195 schema.add_field(field);
196
197 let removed = schema.remove_field("username");
198 assert!(removed.is_some());
199 assert!(schema.get_field("username").is_none());
200 }
201
202 #[test]
203 fn test_field_definition_new() {
204 let field = FieldDefinition::new("db_host", "string");
205 assert_eq!(field.key, "db_host");
206 assert_eq!(field.r#type, "string");
207 assert!(!field.is_secret);
208 }
209
210 #[test]
211 fn test_field_definition_builder() {
212 let field = FieldDefinition::new("email", "string")
213 .with_description("User email address")
214 .with_validation(ValidationRules::new().with_pattern(r"^[\w\.-]+@[\w\.-]+\.\w+$"))
215 .secret();
216
217 assert_eq!(field.key, "email");
218 assert!(field.description.is_some());
219 assert!(field.validation.is_some());
220 assert!(field.is_secret);
221 }
222
223 #[test]
224 fn test_field_type_checkers() {
225 let string_field = FieldDefinition::new("name", "string");
226 let int_field = FieldDefinition::new("age", "integer");
227 let bool_field = FieldDefinition::new("active", "boolean");
228
229 assert!(string_field.is_string());
230 assert!(!string_field.is_integer());
231 assert!(!string_field.is_boolean());
232
233 assert!(int_field.is_integer());
234 assert!(bool_field.is_boolean());
235 }
236
237 #[test]
238 fn test_validation_rules_builder() {
239 let rules = ValidationRules::new()
240 .with_min_length(3)
241 .with_max_length(10)
242 .with_min_value(0)
243 .with_max_value(120)
244 .with_pattern(r"^[a-z]+$");
245
246 assert_eq!(rules.min_length, Some(3));
247 assert_eq!(rules.max_length, Some(10));
248 assert_eq!(rules.min_value, Some(0));
249 assert_eq!(rules.max_value, Some(120));
250 assert!(rules.pattern.is_some());
251 }
252
253 #[test]
254 fn test_validation_rules_length_range() {
255 let rules = ValidationRules::new().with_length_range(5, 15);
256 assert_eq!(rules.min_length, Some(5));
257 assert_eq!(rules.max_length, Some(15));
258 }
259
260 #[test]
261 fn test_validation_rules_value_range() {
262 let rules = ValidationRules::new().with_value_range(-100, 100);
263 assert_eq!(rules.min_value, Some(-100));
264 assert_eq!(rules.max_value, Some(100));
265 }
266
267 #[test]
268 fn test_schema_iter() {
269 let mut schema = SchemaFile::new("1.0.0");
270 schema.add_field(FieldDefinition::new("field1", "string"));
271 schema.add_field(FieldDefinition::new("field2", "integer"));
272
273 let keys: Vec<_> = schema.iter().map(|f| f.key.clone()).collect();
274 assert_eq!(keys, vec!["field1", "field2"]);
275 }
276}