peat_schema/validation/
core.rs1use super::{ValidationError, ValidationResult};
6use crate::capability::v1::Capability;
7use crate::cell::v1::{CellConfig, CellState};
8use crate::node::v1::{NodeConfig, NodeState};
9
10pub fn validate_capability(cap: &Capability) -> ValidationResult<()> {
12 if cap.confidence < 0.0 || cap.confidence > 1.0 {
14 return Err(ValidationError::InvalidConfidence(cap.confidence));
15 }
16
17 if cap.id.is_empty() {
19 return Err(ValidationError::MissingField("id".to_string()));
20 }
21
22 if cap.name.is_empty() {
23 return Err(ValidationError::MissingField("name".to_string()));
24 }
25
26 Ok(())
27}
28
29pub fn validate_node_config(config: &NodeConfig) -> ValidationResult<()> {
31 if config.id.is_empty() {
33 return Err(ValidationError::MissingField("id".to_string()));
34 }
35
36 if config.platform_type.is_empty() {
37 return Err(ValidationError::MissingField("platform_type".to_string()));
38 }
39
40 for cap in &config.capabilities {
42 validate_capability(cap)?;
43 }
44
45 if config.comm_range_m <= 0.0 {
47 return Err(ValidationError::InvalidValue(
48 "comm_range_m must be positive".to_string(),
49 ));
50 }
51
52 if config.max_speed_mps <= 0.0 {
54 return Err(ValidationError::InvalidValue(
55 "max_speed_mps must be positive".to_string(),
56 ));
57 }
58
59 Ok(())
60}
61
62pub fn validate_node_state(state: &NodeState) -> ValidationResult<()> {
64 if let Some(pos) = &state.position {
66 if pos.latitude < -90.0 || pos.latitude > 90.0 {
67 return Err(ValidationError::InvalidValue(
68 "latitude must be between -90 and 90".to_string(),
69 ));
70 }
71 if pos.longitude < -180.0 || pos.longitude > 180.0 {
72 return Err(ValidationError::InvalidValue(
73 "longitude must be between -180 and 180".to_string(),
74 ));
75 }
76 }
77
78 Ok(())
79}
80
81pub fn validate_cell_config(config: &CellConfig) -> ValidationResult<()> {
83 if config.id.is_empty() {
85 return Err(ValidationError::MissingField("id".to_string()));
86 }
87
88 if config.max_size < config.min_size {
90 return Err(ValidationError::ConstraintViolation(
91 "max_size must be >= min_size".to_string(),
92 ));
93 }
94
95 if config.min_size < 2 {
97 return Err(ValidationError::ConstraintViolation(
98 "min_size must be at least 2".to_string(),
99 ));
100 }
101
102 Ok(())
103}
104
105pub fn validate_cell_state(state: &CellState) -> ValidationResult<()> {
107 if let Some(config) = &state.config {
109 validate_cell_config(config)?;
110
111 let member_count = state.members.len();
113 if member_count > config.max_size as usize {
114 return Err(ValidationError::ConstraintViolation(format!(
115 "member count ({}) exceeds max_size ({})",
116 member_count, config.max_size
117 )));
118 }
119 }
120
121 for cap in &state.capabilities {
123 validate_capability(cap)?;
124 }
125
126 if let Some(leader_id) = &state.leader_id {
128 if !state.members.contains(leader_id) {
129 return Err(ValidationError::ConstraintViolation(
130 "leader_id must be in members list".to_string(),
131 ));
132 }
133 }
134
135 Ok(())
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::capability::v1::CapabilityType;
142
143 #[test]
144 fn test_validate_capability_success() {
145 let cap = Capability {
146 id: "cap-1".to_string(),
147 name: "Camera".to_string(),
148 capability_type: CapabilityType::Sensor as i32,
149 confidence: 0.9,
150 metadata_json: String::new(),
151 registered_at: None,
152 };
153
154 assert!(validate_capability(&cap).is_ok());
155 }
156
157 #[test]
158 fn test_validate_capability_invalid_confidence() {
159 let cap = Capability {
160 id: "cap-1".to_string(),
161 name: "Camera".to_string(),
162 capability_type: CapabilityType::Sensor as i32,
163 confidence: 1.5, metadata_json: String::new(),
165 registered_at: None,
166 };
167
168 assert!(validate_capability(&cap).is_err());
169 }
170
171 #[test]
172 fn test_validate_capability_missing_id() {
173 let cap = Capability {
174 id: String::new(), name: "Camera".to_string(),
176 capability_type: CapabilityType::Sensor as i32,
177 confidence: 0.9,
178 metadata_json: String::new(),
179 registered_at: None,
180 };
181
182 assert!(validate_capability(&cap).is_err());
183 }
184
185 #[test]
186 fn test_validate_cell_config_invalid_sizes() {
187 let config = CellConfig {
188 id: "cell-1".to_string(),
189 max_size: 2,
190 min_size: 5, created_at: None,
192 };
193
194 assert!(validate_cell_config(&config).is_err());
195 }
196}