capnweb_core/
validate.rs

1use crate::RpcError;
2#[cfg(feature = "validation")]
3use jsonschema;
4use schemars::{schema_for, JsonSchema};
5use serde_json::Value;
6
7#[cfg(feature = "validation")]
8pub struct Validator {
9    schema: jsonschema::Validator,
10}
11
12#[cfg(feature = "validation")]
13impl Validator {
14    pub fn new(schema: Value) -> Result<Self, RpcError> {
15        let compiled = jsonschema::validator_for(&schema)
16            .map_err(|e| RpcError::bad_request(format!("Invalid schema: {}", e)))?;
17        Ok(Validator { schema: compiled })
18    }
19
20    pub fn from_type<T: JsonSchema>() -> Result<Self, RpcError> {
21        let schema = schema_for!(T);
22        let schema_value = serde_json::to_value(schema)
23            .map_err(|e| RpcError::internal(format!("Schema serialization error: {}", e)))?;
24        Self::new(schema_value)
25    }
26
27    pub fn validate(&self, value: &Value) -> Result<(), Vec<String>> {
28        if self.schema.is_valid(value) {
29            Ok(())
30        } else {
31            Err(vec!["Validation failed".to_string()])
32        }
33    }
34
35    pub fn is_valid(&self, value: &Value) -> bool {
36        self.schema.is_valid(value)
37    }
38}
39
40#[cfg(not(feature = "validation"))]
41pub struct Validator;
42
43#[cfg(not(feature = "validation"))]
44impl Validator {
45    pub fn new(_schema: Value) -> Result<Self, RpcError> {
46        Err(RpcError::internal("Validation feature not enabled"))
47    }
48
49    pub fn validate(&self, _value: &Value) -> Result<(), Vec<String>> {
50        Err(vec!["Validation feature not enabled".to_string()])
51    }
52
53    pub fn is_valid(&self, _value: &Value) -> bool {
54        true
55    }
56}
57
58#[cfg(all(test, feature = "validation"))]
59mod tests {
60    use super::*;
61    use serde_json::json;
62
63    #[test]
64    fn test_validator_creation() {
65        let schema = json!({
66            "type": "object",
67            "properties": {
68                "name": { "type": "string" },
69                "age": { "type": "number" }
70            },
71            "required": ["name"]
72        });
73
74        let validator = Validator::new(schema).unwrap();
75        assert!(validator.is_valid(&json!({"name": "Alice", "age": 30})));
76        assert!(validator.is_valid(&json!({"name": "Bob"})));
77        assert!(!validator.is_valid(&json!({"age": 30})));
78    }
79
80    #[test]
81    fn test_validation_errors() {
82        let schema = json!({
83            "type": "object",
84            "properties": {
85                "count": {
86                    "type": "integer",
87                    "minimum": 0,
88                    "maximum": 100
89                }
90            }
91        });
92
93        let validator = Validator::new(schema).unwrap();
94
95        let valid = json!({"count": 50});
96        assert!(validator.validate(&valid).is_ok());
97
98        let invalid = json!({"count": 150});
99        let result = validator.validate(&invalid);
100        assert!(result.is_err());
101    }
102
103    #[derive(JsonSchema)]
104    #[allow(dead_code)]
105    struct TestStruct {
106        name: String,
107        age: Option<u32>,
108    }
109
110    #[test]
111    fn test_from_type() {
112        let validator = Validator::from_type::<TestStruct>().unwrap();
113
114        assert!(validator.is_valid(&json!({
115            "name": "Alice",
116            "age": 30
117        })));
118
119        assert!(validator.is_valid(&json!({
120            "name": "Bob"
121        })));
122
123        assert!(!validator.is_valid(&json!({
124            "age": 30
125        })));
126    }
127}