scim_server/resource/value_objects/
value_object_trait.rs

1//! Core trait system for schema-driven value objects.
2//!
3//! This module defines the fundamental traits that enable dynamic value object
4//! creation, validation, and schema-driven operations. These traits form the
5//! foundation for the schema-driven value object system.
6//!
7//! ## Design Principles
8//!
9//! - **Schema-Driven**: Value objects can be created from schema definitions
10//! - **Type-Safe**: Compile-time guarantees where possible, runtime validation where needed
11//! - **Extensible**: Support for custom and extension attributes
12//! - **Composable**: Enable validation across multiple value objects
13
14use crate::error::{ValidationError, ValidationResult};
15use crate::schema::types::{AttributeDefinition, AttributeType};
16use serde_json::Value;
17use std::any::Any;
18use std::fmt::Debug;
19
20/// Core trait for all SCIM value objects.
21///
22/// This trait enables dynamic value object operations while maintaining
23/// type safety through the use of Any for downcasting when needed.
24pub trait ValueObject: Debug + Send + Sync {
25    /// Get the SCIM attribute type this value object represents
26    fn attribute_type(&self) -> AttributeType;
27
28    /// Get the schema attribute name this value object corresponds to
29    fn attribute_name(&self) -> &str;
30
31    /// Serialize the value object to JSON
32    fn to_json(&self) -> ValidationResult<Value>;
33
34    /// Validate the value object against a schema definition
35    fn validate_against_schema(&self, definition: &AttributeDefinition) -> ValidationResult<()>;
36
37    /// Get the raw value as a JSON Value for schema-agnostic operations
38    fn as_json_value(&self) -> Value;
39
40    /// Check if this value object supports the given attribute definition
41    fn supports_definition(&self, definition: &AttributeDefinition) -> bool;
42
43    /// Clone the value object as a boxed trait object
44    fn clone_boxed(&self) -> Box<dyn ValueObject>;
45
46    /// Get type information for downcasting
47    fn as_any(&self) -> &dyn Any;
48}
49
50/// Trait for value objects that can be created from schema definitions.
51///
52/// This trait enables the dynamic factory pattern where value objects
53/// are constructed based on schema attribute definitions and JSON values.
54pub trait SchemaConstructible: ValueObject + Sized {
55    /// Create a value object from a JSON value and schema definition
56    fn from_schema_and_value(
57        definition: &AttributeDefinition,
58        value: &Value,
59    ) -> ValidationResult<Self>;
60
61    /// Check if this type can handle the given attribute definition
62    fn can_construct_from(definition: &AttributeDefinition) -> bool;
63
64    /// Get the priority for this constructor (higher = preferred)
65    /// Used when multiple constructors might handle the same definition
66    fn constructor_priority() -> u8 {
67        50 // Default priority
68    }
69}
70
71/// Trait for value objects that represent extension attributes.
72///
73/// Extension attributes are defined by custom schemas and may have
74/// different validation rules and behaviors than core SCIM attributes.
75pub trait ExtensionAttribute: ValueObject {
76    /// Get the schema URI that defines this extension attribute
77    fn schema_uri(&self) -> &str;
78
79    /// Get the extension namespace (usually derived from schema URI)
80    fn extension_namespace(&self) -> &str;
81
82    /// Validate against extension-specific rules
83    fn validate_extension_rules(&self) -> ValidationResult<()>;
84}
85
86/// Trait for composite validation across multiple value objects.
87///
88/// This enables validation rules that span multiple attributes or
89/// require context from other value objects to validate properly.
90pub trait CompositeValidator {
91    /// Validate relationships between multiple value objects
92    fn validate_composite(&self, objects: &[Box<dyn ValueObject>]) -> ValidationResult<()>;
93
94    /// Get the names of attributes this validator depends on
95    fn dependent_attributes(&self) -> Vec<String>;
96
97    /// Check if this validator applies to the given set of attributes
98    fn applies_to(&self, attribute_names: &[String]) -> bool;
99}
100
101/// Registry for value object constructors.
102///
103/// This registry maintains a mapping of attribute types and names to
104/// constructor functions, enabling dynamic value object creation.
105#[derive(Default)]
106pub struct ValueObjectRegistry {
107    constructors: Vec<Box<dyn ValueObjectConstructor>>,
108    composite_validators: Vec<Box<dyn CompositeValidator>>,
109}
110
111/// Trait for value object constructors that can be registered.
112pub trait ValueObjectConstructor: Send + Sync {
113    /// Attempt to construct a value object from the given definition and value
114    fn try_construct(
115        &self,
116        definition: &AttributeDefinition,
117        value: &Value,
118    ) -> Option<ValidationResult<Box<dyn ValueObject>>>;
119
120    /// Get the priority of this constructor
121    fn priority(&self) -> u8;
122
123    /// Get a description of what this constructor handles
124    fn description(&self) -> &str;
125}
126
127impl ValueObjectRegistry {
128    /// Create a new registry with default constructors
129    pub fn new() -> Self {
130        let mut registry = Self::default();
131        registry.register_default_constructors();
132        registry
133    }
134
135    /// Register a value object constructor
136    pub fn register_constructor(&mut self, constructor: Box<dyn ValueObjectConstructor>) {
137        self.constructors.push(constructor);
138        // Sort by priority (highest first)
139        self.constructors
140            .sort_by(|a, b| b.priority().cmp(&a.priority()));
141    }
142
143    /// Register a composite validator
144    pub fn register_composite_validator(&mut self, validator: Box<dyn CompositeValidator>) {
145        self.composite_validators.push(validator);
146    }
147
148    /// Create a value object from schema definition and JSON value
149    pub fn create_value_object(
150        &self,
151        definition: &AttributeDefinition,
152        value: &Value,
153    ) -> ValidationResult<Box<dyn ValueObject>> {
154        // Try each constructor in priority order
155        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    /// Validate composite rules across multiple value objects
168    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    /// Check if the registry has any constructors registered
187    pub fn has_constructors(&self) -> bool {
188        !self.constructors.is_empty()
189    }
190
191    /// Register default constructors for built-in value objects
192    fn register_default_constructors(&mut self) {
193        // These will be implemented as we add support for each type
194        // For now, we'll register placeholder constructors
195    }
196}
197
198/// Helper macro for implementing ValueObject trait for existing value objects
199#[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                // Basic type checking
224                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                // Additional validation can be added here based on the specific type
233                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)]
261/// Generic constructor for simple value objects
262pub 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    // Mock value object for testing
302    #[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        // Registry should be created successfully
362        assert_eq!(registry.constructors.len(), 0); // No default constructors yet
363        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}