data_modelling_core/validation/
schema.rs1#[cfg(feature = "schema-validation")]
8fn format_validation_error(error: &jsonschema::ValidationError, schema_type: &str) -> String {
9 let instance_path = error.instance_path();
11
12 let path_str = instance_path.to_string();
14 let path_str = if path_str == "/" || path_str.is_empty() {
15 "root".to_string()
16 } else {
17 path_str
18 };
19
20 let error_message = error.to_string();
22
23 format!(
24 "{} validation failed at path '{}': {}",
25 schema_type, path_str, error_message
26 )
27}
28
29#[cfg(feature = "schema-validation")]
33pub fn validate_odcs_internal(content: &str) -> Result<(), String> {
34 use jsonschema::Validator;
35 use serde_json::Value;
36
37 let data: Value =
39 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
40
41 let is_odcl_format = if let Some(obj) = data.as_object() {
44 obj.contains_key("dataContractSpecification")
46 || (obj.contains_key("name")
48 && obj.contains_key("columns")
49 && !obj.contains_key("apiVersion")
50 && !obj.contains_key("kind")
51 && !obj.contains_key("schema"))
52 } else {
53 false
54 };
55
56 if is_odcl_format {
58 return validate_odcl_internal(content);
59 }
60
61 let schema_content = include_str!("../../../../schemas/odcs-json-schema-v3.1.0.json");
63 let schema: Value = serde_json::from_str(schema_content)
64 .map_err(|e| format!("Failed to load ODCS schema: {}", e))?;
65
66 let validator =
67 Validator::new(&schema).map_err(|e| format!("Failed to compile ODCS schema: {}", e))?;
68
69 if let Err(error) = validator.validate(&data) {
71 let error_msg = format_validation_error(&error, "ODCS");
73 return Err(error_msg);
74 }
75
76 Ok(())
77}
78
79#[cfg(not(feature = "schema-validation"))]
80pub fn validate_odcs_internal(_content: &str) -> Result<(), String> {
81 Ok(())
83}
84
85#[cfg(feature = "schema-validation")]
87pub fn validate_odcl_internal(content: &str) -> Result<(), String> {
88 use jsonschema::Validator;
89 use serde_json::Value;
90
91 let schema_content = include_str!("../../../../schemas/odcl-json-schema-1.2.1.json");
93 let schema: Value = serde_json::from_str(schema_content)
94 .map_err(|e| format!("Failed to load ODCL schema: {}", e))?;
95
96 let validator =
97 Validator::new(&schema).map_err(|e| format!("Failed to compile ODCL schema: {}", e))?;
98
99 let data: Value =
101 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
102
103 if let Err(error) = validator.validate(&data) {
105 let error_msg = format_validation_error(&error, "ODCL");
106 return Err(error_msg);
107 }
108
109 Ok(())
110}
111
112#[cfg(not(feature = "schema-validation"))]
113pub fn validate_odcl_internal(_content: &str) -> Result<(), String> {
114 Ok(())
116}
117
118#[cfg(feature = "schema-validation")]
120pub fn validate_openapi_internal(content: &str) -> Result<(), String> {
121 use jsonschema::Validator;
122 use serde_json::Value;
123
124 let schema_content = include_str!("../../../../schemas/openapi-3.1.1.json");
126 let schema: Value = serde_json::from_str(schema_content)
127 .map_err(|e| format!("Failed to load OpenAPI schema: {}", e))?;
128
129 let validator =
130 Validator::new(&schema).map_err(|e| format!("Failed to compile OpenAPI schema: {}", e))?;
131
132 let data: Value = if content.trim_start().starts_with('{') {
134 serde_json::from_str(content).map_err(|e| format!("Failed to parse JSON: {}", e))?
135 } else {
136 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?
137 };
138
139 if let Err(error) = validator.validate(&data) {
141 return Err(format!("OpenAPI validation failed: {}", error));
142 }
143
144 Ok(())
145}
146
147#[cfg(not(feature = "schema-validation"))]
148pub fn validate_openapi_internal(_content: &str) -> Result<(), String> {
149 Ok(())
151}
152
153pub fn validate_protobuf_internal(content: &str) -> Result<(), String> {
155 if !content.contains("syntax") && !content.contains("message") && !content.contains("enum") {
157 return Err("File does not appear to be a valid Protobuf file".to_string());
158 }
159
160 let open_braces = content.matches('{').count();
162 let close_braces = content.matches('}').count();
163 if open_braces != close_braces {
164 return Err(format!(
165 "Unbalanced braces in Protobuf file ({} open, {} close)",
166 open_braces, close_braces
167 ));
168 }
169
170 Ok(())
171}
172
173pub fn validate_avro_internal(content: &str) -> Result<(), String> {
175 let _value: serde_json::Value =
177 serde_json::from_str(content).map_err(|e| format!("Failed to parse AVRO JSON: {}", e))?;
178
179 Ok(())
182}
183
184#[cfg(feature = "schema-validation")]
186pub fn validate_json_schema_internal(content: &str) -> Result<(), String> {
187 use jsonschema::Validator;
188 use serde_json::Value;
189
190 let schema: Value =
192 serde_json::from_str(content).map_err(|e| format!("Failed to parse JSON Schema: {}", e))?;
193
194 Validator::new(&schema).map_err(|e| format!("Invalid JSON Schema: {}", e))?;
196
197 Ok(())
198}
199
200#[cfg(not(feature = "schema-validation"))]
201pub fn validate_json_schema_internal(_content: &str) -> Result<(), String> {
202 Ok(())
204}
205
206#[cfg(feature = "schema-validation")]
208pub fn validate_odps_internal(content: &str) -> Result<(), String> {
209 use jsonschema::Validator;
210 use serde_json::Value;
211
212 let schema_content = include_str!("../../../../schemas/odps-json-schema-latest.json");
214 let schema: Value = serde_json::from_str(schema_content)
215 .map_err(|e| format!("Failed to load ODPS schema: {}", e))?;
216
217 let validator =
218 Validator::new(&schema).map_err(|e| format!("Failed to compile ODPS schema: {}", e))?;
219
220 let data: Value =
222 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
223
224 if let Err(error) = validator.validate(&data) {
226 let instance_path = error.instance_path();
227 let path_str = instance_path.to_string();
228 let path_str = if path_str == "/" || path_str.is_empty() {
229 "root".to_string()
230 } else {
231 path_str
232 };
233 return Err(format!(
234 "ODPS validation failed at path '{}': {}",
235 path_str, error
236 ));
237 }
238
239 Ok(())
240}
241
242#[cfg(not(feature = "schema-validation"))]
243pub fn validate_odps_internal(_content: &str) -> Result<(), String> {
244 Ok(())
246}
247
248#[cfg(feature = "schema-validation")]
250pub fn validate_cads_internal(content: &str) -> Result<(), String> {
251 use jsonschema::Validator;
252 use serde_json::Value;
253
254 let schema_content = include_str!("../../../../schemas/cads.schema.json");
256 let schema: Value = serde_json::from_str(schema_content)
257 .map_err(|e| format!("Failed to load CADS schema: {}", e))?;
258
259 let validator =
260 Validator::new(&schema).map_err(|e| format!("Failed to compile CADS schema: {}", e))?;
261
262 let data: Value =
264 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
265
266 if let Err(error) = validator.validate(&data) {
268 let instance_path = error.instance_path();
269 let path_str = instance_path.to_string();
270 let path_str = if path_str == "/" || path_str.is_empty() {
271 "root".to_string()
272 } else {
273 path_str
274 };
275 return Err(format!(
276 "CADS validation failed at path '{}': {}",
277 path_str, error
278 ));
279 }
280
281 Ok(())
282}
283
284#[cfg(not(feature = "schema-validation"))]
285pub fn validate_cads_internal(_content: &str) -> Result<(), String> {
286 Ok(())
288}
289
290pub fn validate_sql_internal(content: &str) -> Result<(), String> {
292 use sqlparser::dialect::GenericDialect;
293 use sqlparser::parser::Parser;
294
295 let dialect = GenericDialect {};
296
297 Parser::parse_sql(&dialect, content).map_err(|e| format!("SQL validation failed: {}", e))?;
298
299 Ok(())
300}
301
302#[cfg(feature = "schema-validation")]
304pub fn validate_workspace_internal(content: &str) -> Result<(), String> {
305 use jsonschema::Validator;
306 use serde_json::Value;
307
308 let schema_content = include_str!("../../../../schemas/workspace-schema.json");
310 let schema: Value = serde_json::from_str(schema_content)
311 .map_err(|e| format!("Failed to load workspace schema: {}", e))?;
312
313 let validator = Validator::new(&schema)
314 .map_err(|e| format!("Failed to compile workspace schema: {}", e))?;
315
316 let data: Value =
318 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
319
320 if let Err(error) = validator.validate(&data) {
322 let error_msg = format_validation_error(&error, "Workspace");
323 return Err(error_msg);
324 }
325
326 Ok(())
327}
328
329#[cfg(not(feature = "schema-validation"))]
330pub fn validate_workspace_internal(_content: &str) -> Result<(), String> {
331 Ok(())
333}
334
335pub fn validate_relationships_internal(content: &str) -> Result<(), String> {
337 use serde_json::Value;
338
339 let data: Value =
341 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
342
343 let relationships = data
345 .get("relationships")
346 .and_then(|v| v.as_array())
347 .or_else(|| data.as_array());
348
349 if let Some(rels) = relationships {
350 for (i, rel) in rels.iter().enumerate() {
351 if rel.get("source_table_id").is_none() {
353 return Err(format!("Relationship {} is missing 'source_table_id'", i));
354 }
355 if rel.get("target_table_id").is_none() {
356 return Err(format!("Relationship {} is missing 'target_table_id'", i));
357 }
358 }
359 }
360
361 Ok(())
362}
363
364#[cfg(feature = "schema-validation")]
366pub fn validate_decision_internal(content: &str) -> Result<(), String> {
367 use jsonschema::Validator;
368 use serde_json::Value;
369
370 let schema_content = include_str!("../../../../schemas/decision-schema.json");
372 let schema: Value = serde_json::from_str(schema_content)
373 .map_err(|e| format!("Failed to load decision schema: {}", e))?;
374
375 let validator =
376 Validator::new(&schema).map_err(|e| format!("Failed to compile decision schema: {}", e))?;
377
378 let data: Value =
380 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
381
382 if let Err(error) = validator.validate(&data) {
384 let instance_path = error.instance_path();
385 let path_str = instance_path.to_string();
386 let path_str = if path_str == "/" || path_str.is_empty() {
387 "root".to_string()
388 } else {
389 path_str
390 };
391 return Err(format!(
392 "Decision validation failed at path '{}': {}",
393 path_str, error
394 ));
395 }
396
397 Ok(())
398}
399
400#[cfg(not(feature = "schema-validation"))]
401pub fn validate_decision_internal(_content: &str) -> Result<(), String> {
402 Ok(())
404}
405
406#[cfg(feature = "schema-validation")]
408pub fn validate_knowledge_internal(content: &str) -> Result<(), String> {
409 use jsonschema::Validator;
410 use serde_json::Value;
411
412 let schema_content = include_str!("../../../../schemas/knowledge-schema.json");
414 let schema: Value = serde_json::from_str(schema_content)
415 .map_err(|e| format!("Failed to load knowledge schema: {}", e))?;
416
417 let validator = Validator::new(&schema)
418 .map_err(|e| format!("Failed to compile knowledge schema: {}", e))?;
419
420 let data: Value =
422 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
423
424 if let Err(error) = validator.validate(&data) {
426 let instance_path = error.instance_path();
427 let path_str = instance_path.to_string();
428 let path_str = if path_str == "/" || path_str.is_empty() {
429 "root".to_string()
430 } else {
431 path_str
432 };
433 return Err(format!(
434 "Knowledge validation failed at path '{}': {}",
435 path_str, error
436 ));
437 }
438
439 Ok(())
440}
441
442#[cfg(not(feature = "schema-validation"))]
443pub fn validate_knowledge_internal(_content: &str) -> Result<(), String> {
444 Ok(())
446}
447
448#[cfg(feature = "schema-validation")]
450pub fn validate_decisions_index_internal(content: &str) -> Result<(), String> {
451 use jsonschema::Validator;
452 use serde_json::Value;
453
454 let schema_content = include_str!("../../../../schemas/decisions-index-schema.json");
456 let schema: Value = serde_json::from_str(schema_content)
457 .map_err(|e| format!("Failed to load decisions-index schema: {}", e))?;
458
459 let validator = Validator::new(&schema)
460 .map_err(|e| format!("Failed to compile decisions-index schema: {}", e))?;
461
462 let data: Value =
464 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
465
466 if let Err(error) = validator.validate(&data) {
468 let error_msg = format_validation_error(&error, "Decisions Index");
469 return Err(error_msg);
470 }
471
472 Ok(())
473}
474
475#[cfg(not(feature = "schema-validation"))]
476pub fn validate_decisions_index_internal(_content: &str) -> Result<(), String> {
477 Ok(())
479}
480
481#[cfg(feature = "schema-validation")]
483pub fn validate_knowledge_index_internal(content: &str) -> Result<(), String> {
484 use jsonschema::Validator;
485 use serde_json::Value;
486
487 let schema_content = include_str!("../../../../schemas/knowledge-index-schema.json");
489 let schema: Value = serde_json::from_str(schema_content)
490 .map_err(|e| format!("Failed to load knowledge-index schema: {}", e))?;
491
492 let validator = Validator::new(&schema)
493 .map_err(|e| format!("Failed to compile knowledge-index schema: {}", e))?;
494
495 let data: Value =
497 serde_yaml::from_str(content).map_err(|e| format!("Failed to parse YAML: {}", e))?;
498
499 if let Err(error) = validator.validate(&data) {
501 let error_msg = format_validation_error(&error, "Knowledge Index");
502 return Err(error_msg);
503 }
504
505 Ok(())
506}
507
508#[cfg(not(feature = "schema-validation"))]
509pub fn validate_knowledge_index_internal(_content: &str) -> Result<(), String> {
510 Ok(())
512}