Skip to main content

runtimo_core/
schema.rs

1//! JSON Schema validation for capability arguments.
2//!
3//! Provides basic type-checking validation against a JSON Schema object.
4//! Currently supports simple `"type"` field matching. For full JSON Schema
5//! validation, integrate the `jsonschema` crate.
6//!
7//! # Example
8//!
9//! ```rust,ignore
10//! use runtimo_core::SchemaValidator;
11//! use serde_json::json;
12//!
13//! let validator = SchemaValidator::new();
14//! let schema = json!({"type": "object"});
15//! let args = json!({"path": "/tmp/test.txt"});
16//!
17//! assert!(validator.validate(&args, &schema).is_ok());
18//! ```
19
20use crate::Result;
21use serde_json::Value;
22
23/// Validates JSON values against a simplified JSON Schema.
24///
25/// Currently performs basic type checking. Future versions may use the
26/// `jsonschema` crate for full draft-07 validation.
27#[allow(clippy::exhaustive_structs)]
28pub struct SchemaValidator {
29    // Could use jsonschema crate for full JSON Schema validation
30}
31
32impl SchemaValidator {
33    /// Creates a new (stateless) schema validator.
34    #[must_use] 
35    pub fn new() -> Self {
36        Self {}
37    }
38
39    /// Validates arguments against a JSON Schema.
40    ///
41    /// # Arguments
42    ///
43    /// * `args` — The JSON value to validate
44    /// * `schema` — A JSON Schema object (currently only `"type"` is checked)
45    ///
46    /// # Returns
47    ///
48    /// `Ok(())` if the value matches the schema.
49    ///
50    /// # Errors
51    ///
52    /// Returns [`Error::SchemaValidationFailed`](crate::Error::SchemaValidationFailed)
53    /// if the value's type does not match the schema's `"type"` field.
54    pub fn validate(&self, args: &Value, schema: &Value) -> Result<()> {
55        if let Some(expected_type) = schema.get("type").and_then(|t| t.as_str()) {
56            let actual_type = if args.is_string() {
57                "string"
58            } else if args.is_number() {
59                "number"
60            } else if args.is_boolean() {
61                "boolean"
62            } else if args.is_array() {
63                "array"
64            } else if args.is_object() {
65                "object"
66            } else {
67                "null"
68            };
69
70            if expected_type != actual_type {
71                return Err(crate::Error::SchemaValidationFailed(format!(
72                    "Expected type '{}', got '{}'",
73                    expected_type, actual_type
74                )));
75            }
76        }
77
78        if let Some(required) = schema.get("required").and_then(|r| r.as_array()) {
79            if let Some(obj) = args.as_object() {
80                for key in required {
81                    if let Some(key_str) = key.as_str() {
82                        if !obj.contains_key(key_str) {
83                            return Err(crate::Error::SchemaValidationFailed(format!(
84                                "Missing required field: '{}'",
85                                key_str
86                            )));
87                        }
88                    }
89                }
90            }
91        }
92
93        Ok(())
94    }
95}
96
97impl Default for SchemaValidator {
98    fn default() -> Self {
99        Self::new()
100    }
101}