scim_server/resource/value_objects/
value_object_trait.rs1use crate::error::{ValidationError, ValidationResult};
15use crate::schema::types::{AttributeDefinition, AttributeType};
16use serde_json::Value;
17use std::any::Any;
18use std::fmt::Debug;
19
20pub trait ValueObject: Debug + Send + Sync {
25 fn attribute_type(&self) -> AttributeType;
27
28 fn attribute_name(&self) -> &str;
30
31 fn to_json(&self) -> ValidationResult<Value>;
33
34 fn validate_against_schema(&self, definition: &AttributeDefinition) -> ValidationResult<()>;
36
37 fn as_json_value(&self) -> Value;
39
40 fn supports_definition(&self, definition: &AttributeDefinition) -> bool;
42
43 fn clone_boxed(&self) -> Box<dyn ValueObject>;
45
46 fn as_any(&self) -> &dyn Any;
48}
49
50pub trait SchemaConstructible: ValueObject + Sized {
55 fn from_schema_and_value(
57 definition: &AttributeDefinition,
58 value: &Value,
59 ) -> ValidationResult<Self>;
60
61 fn can_construct_from(definition: &AttributeDefinition) -> bool;
63
64 fn constructor_priority() -> u8 {
67 50 }
69}
70
71pub trait ExtensionAttribute: ValueObject {
76 fn schema_uri(&self) -> &str;
78
79 fn extension_namespace(&self) -> &str;
81
82 fn validate_extension_rules(&self) -> ValidationResult<()>;
84}
85
86pub trait CompositeValidator {
91 fn validate_composite(&self, objects: &[Box<dyn ValueObject>]) -> ValidationResult<()>;
93
94 fn dependent_attributes(&self) -> Vec<String>;
96
97 fn applies_to(&self, attribute_names: &[String]) -> bool;
99}
100
101#[derive(Default)]
106pub struct ValueObjectRegistry {
107 constructors: Vec<Box<dyn ValueObjectConstructor>>,
108 composite_validators: Vec<Box<dyn CompositeValidator>>,
109}
110
111pub trait ValueObjectConstructor: Send + Sync {
113 fn try_construct(
115 &self,
116 definition: &AttributeDefinition,
117 value: &Value,
118 ) -> Option<ValidationResult<Box<dyn ValueObject>>>;
119
120 fn priority(&self) -> u8;
122
123 fn description(&self) -> &str;
125}
126
127impl ValueObjectRegistry {
128 pub fn new() -> Self {
130 let mut registry = Self::default();
131 registry.register_default_constructors();
132 registry
133 }
134
135 pub fn register_constructor(&mut self, constructor: Box<dyn ValueObjectConstructor>) {
137 self.constructors.push(constructor);
138 self.constructors
140 .sort_by(|a, b| b.priority().cmp(&a.priority()));
141 }
142
143 pub fn register_composite_validator(&mut self, validator: Box<dyn CompositeValidator>) {
145 self.composite_validators.push(validator);
146 }
147
148 pub fn create_value_object(
150 &self,
151 definition: &AttributeDefinition,
152 value: &Value,
153 ) -> ValidationResult<Box<dyn ValueObject>> {
154 for constructor in &self.constructors {
156 if let Some(result) = constructor.try_construct(definition, value) {
157 return result;
158 }
159 }
160
161 Err(ValidationError::UnsupportedAttributeType {
162 attribute: definition.name.clone(),
163 type_name: format!("{:?}", definition.data_type),
164 })
165 }
166
167 pub fn validate_composite_rules(
169 &self,
170 objects: &[Box<dyn ValueObject>],
171 ) -> ValidationResult<()> {
172 let attribute_names: Vec<String> = objects
173 .iter()
174 .map(|obj| obj.attribute_name().to_string())
175 .collect();
176
177 for validator in &self.composite_validators {
178 if validator.applies_to(&attribute_names) {
179 validator.validate_composite(objects)?;
180 }
181 }
182
183 Ok(())
184 }
185
186 pub fn has_constructors(&self) -> bool {
188 !self.constructors.is_empty()
189 }
190
191 fn register_default_constructors(&mut self) {
193 }
196}
197
198#[macro_export]
200macro_rules! impl_value_object {
201 (
202 $type:ty,
203 attribute_type: $attr_type:expr,
204 attribute_name: $attr_name:expr
205 ) => {
206 impl $crate::resource::value_objects::value_object_trait::ValueObject for $type {
207 fn attribute_type(&self) -> $crate::schema::types::AttributeType {
208 $attr_type
209 }
210
211 fn attribute_name(&self) -> &str {
212 $attr_name
213 }
214
215 fn to_json(&self) -> $crate::error::ValidationResult<serde_json::Value> {
216 Ok(serde_json::to_value(self)?)
217 }
218
219 fn validate_against_schema(
220 &self,
221 definition: &$crate::schema::types::AttributeDefinition,
222 ) -> $crate::error::ValidationResult<()> {
223 if definition.data_type != self.attribute_type() {
225 return Err($crate::error::ValidationError::InvalidAttributeType(
226 definition.name.clone(),
227 format!("{:?}", definition.data_type),
228 format!("{:?}", self.attribute_type()),
229 ));
230 }
231
232 Ok(())
234 }
235
236 fn as_json_value(&self) -> serde_json::Value {
237 self.to_json().unwrap_or(serde_json::Value::Null)
238 }
239
240 fn supports_definition(
241 &self,
242 definition: &$crate::schema::types::AttributeDefinition,
243 ) -> bool {
244 definition.data_type == self.attribute_type()
245 && definition.name == self.attribute_name()
246 }
247
248 fn clone_boxed(
249 &self,
250 ) -> Box<dyn $crate::resource::value_objects::value_object_trait::ValueObject> {
251 Box::new(self.clone())
252 }
253
254 fn as_any(&self) -> &dyn std::any::Any {
255 self
256 }
257 }
258 };
259}
260#[allow(dead_code)]
261pub struct GenericValueObjectConstructor<T> {
263 _phantom: std::marker::PhantomData<T>,
264}
265
266impl<T> GenericValueObjectConstructor<T> where T: SchemaConstructible + 'static {}
267
268impl<T> ValueObjectConstructor for GenericValueObjectConstructor<T>
269where
270 T: SchemaConstructible + 'static,
271{
272 fn try_construct(
273 &self,
274 definition: &AttributeDefinition,
275 value: &Value,
276 ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
277 if T::can_construct_from(definition) {
278 Some(
279 T::from_schema_and_value(definition, value)
280 .map(|obj| Box::new(obj) as Box<dyn ValueObject>),
281 )
282 } else {
283 None
284 }
285 }
286
287 fn priority(&self) -> u8 {
288 T::constructor_priority()
289 }
290
291 fn description(&self) -> &str {
292 std::any::type_name::<T>()
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299 use crate::schema::types::{AttributeType, Mutability, Uniqueness};
300
301 #[derive(Debug, Clone)]
303 struct MockValueObject {
304 name: String,
305 value: String,
306 }
307
308 impl ValueObject for MockValueObject {
309 fn attribute_type(&self) -> AttributeType {
310 AttributeType::String
311 }
312
313 fn attribute_name(&self) -> &str {
314 &self.name
315 }
316
317 fn to_json(&self) -> ValidationResult<Value> {
318 Ok(Value::String(self.value.clone()))
319 }
320
321 fn validate_against_schema(
322 &self,
323 _definition: &AttributeDefinition,
324 ) -> ValidationResult<()> {
325 Ok(())
326 }
327
328 fn as_json_value(&self) -> Value {
329 Value::String(self.value.clone())
330 }
331
332 fn supports_definition(&self, definition: &AttributeDefinition) -> bool {
333 definition.data_type == AttributeType::String
334 }
335
336 fn clone_boxed(&self) -> Box<dyn ValueObject> {
337 Box::new(self.clone())
338 }
339
340 fn as_any(&self) -> &dyn Any {
341 self
342 }
343 }
344
345 #[test]
346 fn test_value_object_trait() {
347 let obj = MockValueObject {
348 name: "test".to_string(),
349 value: "value".to_string(),
350 };
351
352 assert_eq!(obj.attribute_type(), AttributeType::String);
353 assert_eq!(obj.attribute_name(), "test");
354 assert_eq!(obj.as_json_value(), Value::String("value".to_string()));
355 }
356
357 #[test]
358 fn test_value_object_registry() {
359 let registry = ValueObjectRegistry::new();
360
361 assert_eq!(registry.constructors.len(), 0); assert_eq!(registry.composite_validators.len(), 0);
364 }
365
366 #[test]
367 fn test_validate_against_schema() {
368 let obj = MockValueObject {
369 name: "test".to_string(),
370 value: "value".to_string(),
371 };
372
373 let definition = AttributeDefinition {
374 name: "test".to_string(),
375 data_type: AttributeType::String,
376 multi_valued: false,
377 required: false,
378 case_exact: false,
379 mutability: Mutability::ReadWrite,
380 uniqueness: Uniqueness::None,
381 canonical_values: vec![],
382 sub_attributes: vec![],
383 returned: None,
384 };
385
386 assert!(obj.validate_against_schema(&definition).is_ok());
387 }
388}