cadi_core/
validator.rs

1use crate::ast::*;
2use std::collections::HashSet;
3
4#[derive(Debug, Clone)]
5pub struct ValidationError {
6    pub message: String,
7    pub path: String,
8}
9
10pub struct Validator {
11    errors: Vec<ValidationError>,
12}
13
14impl Default for Validator {
15    fn default() -> Self {
16        Self::new()
17    }
18}
19
20impl Validator {
21    pub fn new() -> Self {
22        Self { errors: Vec::new() }
23    }
24
25    pub fn validate(mut self, doc: &CadlDocument) -> Result<(), Vec<ValidationError>> {
26        self.doc(doc);
27        if self.errors.is_empty() {
28            Ok(())
29        } else {
30            Err(self.errors)
31        }
32    }
33
34    fn push_error(&mut self, message: String, path: &str) {
35        self.errors.push(ValidationError {
36            message,
37            path: path.to_string(),
38        });
39    }
40
41    fn doc(&mut self, doc: &CadlDocument) {
42        let mut interface_names = HashSet::new();
43        for iface in &doc.interfaces {
44            if !interface_names.insert(&iface.name) {
45                self.push_error(format!("Duplicate interface name: {}", iface.name), "root");
46            }
47            self.interface(iface);
48        }
49
50        for (i, imp) in doc.implementations.iter().enumerate() {
51            self.implementation(imp, i);
52        }
53
54        for (i, con) in doc.constraints.iter().enumerate() {
55            self.constraint(con, i);
56        }
57    }
58
59    fn interface(&mut self, iface: &InterfaceDef) {
60        let mut method_names = HashSet::new();
61        for method in &iface.methods {
62            if !method_names.insert(&method.name) {
63                self.push_error(format!("Duplicate method name: {}", method.name), &iface.name);
64            }
65        }
66
67        for ann in &iface.annotations {
68            match ann {
69                Annotation::Contract(c) => self.contract(c, &iface.name),
70                Annotation::Effects(e) => self.effects(e, &iface.name),
71                Annotation::Permissions(p) => self.permissions(p, &iface.name),
72                Annotation::Resources(r) => self.resources(r, &iface.name),
73                Annotation::DataFormat(df) => self.data_format(df, &iface.name),
74                Annotation::Protocol(proto) => self.protocol(proto, &iface.name),
75                Annotation::Numerical(num) => self.numerical(num, &iface.name),
76                Annotation::Observability(obs) => self.observability(obs, &iface.name),
77                Annotation::Abi(abi) => self.abi(abi, &iface.name),
78                Annotation::Unknown(name, _) => {
79                    // Unknown annotations at the interface level might be fine, but we can warn/error if desired.
80                    tracing::debug!("Unknown annotation: {} on interface {}", name, iface.name);
81                }
82            }
83        }
84    }
85
86    fn implementation(&mut self, imp: &ImplDef, index: usize) {
87        if imp.name.is_empty() {
88             self.push_error(format!("Implementation at index {} has no name", index), "implementations");
89        }
90        // Check if it follows InterfaceName.ImplName format
91        if !imp.name.contains('.') {
92            self.push_error(format!("Implementation name '{}' should be in 'Interface.Implementation' format", imp.name), &imp.name);
93        }
94    }
95
96    fn constraint(&mut self, con: &ConstraintDef, index: usize) {
97        if con.rules.is_empty() && con.other.is_empty() {
98            self.push_error(format!("Constraint at index {} is empty", index), "constraints");
99        }
100    }
101
102    fn contract(&mut self, c: &ContractDef, context: &str) {
103        if c.codec.is_none() && c.other.is_empty() {
104            self.push_error("Contract missing descriptive fields (codec, etc.)".to_string(), context);
105        }
106    }
107
108    fn effects(&mut self, e: &EffectsDef, context: &str) {
109        if let Some(c) = &e.concurrency {
110            let valid = ["thread_safe", "single_threaded", "immutable", "reentrant"];
111            if !valid.contains(&c.as_str()) {
112                 self.push_error(format!("Invalid concurrency value: {}", c), context);
113            }
114        }
115    }
116
117    fn permissions(&mut self, p: &PermissionsDef, context: &str) {
118         if p.allow.is_empty() && p.deny.is_empty() && p.sandbox.is_empty() {
119              self.push_error("Permissions annotation is empty".to_string(), context);
120         }
121    }
122
123    fn resources(&mut self, r: &ResourcesDef, context: &str) {
124        for (k, v) in &r.memory {
125             if k == "peak" && v.is_empty() {
126                 self.push_error("Resource memory peak cannot be empty".to_string(), context);
127             }
128        }
129    }
130
131    fn data_format(&mut self, _df: &DataFormatDef, _context: &str) {
132        // Placeholder for deeper data format validation
133    }
134
135    fn protocol(&mut self, proto: &ProtocolDef, context: &str) {
136        if !proto.states.is_empty() && proto.initial.is_none() {
137            self.push_error("Protocol with states must have an initial state".to_string(), context);
138        }
139        if let Some(initial) = &proto.initial {
140            if !proto.states.contains(initial) {
141                self.push_error(format!("Initial state '{}' not found in states list", initial), context);
142            }
143        }
144    }
145
146    fn numerical(&mut self, _num: &NumericalDef, _context: &str) {
147        // Placeholder for numerical precision validation
148    }
149
150    fn observability(&mut self, _obs: &ObservabilityDef, _context: &str) {
151        // Placeholder
152    }
153
154    fn abi(&mut self, abi: &AbiDef, context: &str) {
155        if let Some(encoding) = &abi.string_encoding {
156            let valid = ["utf8", "utf16", "ascii", "cesu8"];
157            if !valid.contains(&encoding.as_str()) {
158                self.push_error(format!("Invalid string encoding: {}", encoding), context);
159            }
160        }
161    }
162}
163