scim_server/resource/value_objects/
factory.rs

1//! Dynamic value object factory for schema-driven construction.
2//!
3//! This module provides a factory system that can dynamically create value objects
4//! based on SCIM schema definitions and JSON values. It supports both core SCIM
5//! attributes and extension attributes, enabling flexible resource construction
6//! while maintaining type safety and validation.
7//!
8//! ## Design Principles
9//!
10//! - **Schema-Driven**: Factory decisions based on attribute definitions
11//! - **Extensible**: Support for registering new value object types
12//! - **Type-Safe**: Compile-time guarantees where possible
13//! - **Performance**: Efficient lookup and construction mechanisms
14
15#![allow(dead_code)]
16
17use super::extension::ExtensionAttributeValue;
18use super::value_object_trait::{ValueObject, ValueObjectConstructor, ValueObjectRegistry};
19use super::{EmailAddress, ExternalId, Name, ResourceId, SchemaUri, UserName};
20use crate::error::{ValidationError, ValidationResult};
21use crate::schema::types::{AttributeDefinition, AttributeType};
22use serde_json::Value;
23use std::collections::HashMap;
24
25/// Factory for creating value objects from schema definitions and JSON values.
26///
27/// The factory maintains a registry of constructors and can dynamically
28/// create appropriate value objects based on attribute definitions.
29pub struct ValueObjectFactory {
30    registry: ValueObjectRegistry,
31    type_mappings: HashMap<String, AttributeType>,
32}
33
34impl ValueObjectFactory {
35    /// Create a new factory with default constructors registered.
36    pub fn new() -> Self {
37        let mut factory = Self {
38            registry: ValueObjectRegistry::new(),
39            type_mappings: HashMap::new(),
40        };
41
42        factory.register_default_constructors();
43        factory.setup_type_mappings();
44        factory
45    }
46
47    /// Create a value object from a schema definition and JSON value.
48    pub fn create_value_object(
49        &self,
50        definition: &AttributeDefinition,
51        value: &Value,
52    ) -> ValidationResult<Box<dyn ValueObject>> {
53        // Handle null values for optional attributes
54        if matches!(value, Value::Null) && !definition.required {
55            return Err(ValidationError::NullValueForOptionalAttribute(
56                definition.name.clone(),
57            ));
58        }
59
60        // Handle multi-valued attributes
61        if definition.multi_valued {
62            return self.create_multi_valued_object(definition, value);
63        }
64
65        // Try registered constructors first
66        match self.registry.create_value_object(definition, value) {
67            Ok(obj) => Ok(obj),
68            Err(_) => {
69                // Fall back to extension attribute if no specific constructor found
70                self.create_extension_attribute(definition, value)
71            }
72        }
73    }
74
75    /// Create a multi-valued attribute object.
76    fn create_multi_valued_object(
77        &self,
78        definition: &AttributeDefinition,
79        value: &Value,
80    ) -> ValidationResult<Box<dyn ValueObject>> {
81        let array = value
82            .as_array()
83            .ok_or_else(|| ValidationError::ExpectedArray(definition.name.clone()))?;
84
85        // Create individual value objects for each array element
86        let mut objects = Vec::new();
87        for item_value in array {
88            // Create a single-valued definition for each item
89            let item_definition = AttributeDefinition {
90                multi_valued: false,
91                ..definition.clone()
92            };
93
94            let obj = self.create_value_object(&item_definition, item_value)?;
95            objects.push(obj);
96        }
97
98        // For now, we'll create a generic multi-valued container
99        // In a more sophisticated implementation, we could have specific
100        // multi-valued types for different attribute types
101        Ok(Box::new(GenericMultiValuedAttribute::new(
102            definition.name.clone(),
103            objects,
104        )))
105    }
106
107    /// Create an extension attribute value object.
108    fn create_extension_attribute(
109        &self,
110        definition: &AttributeDefinition,
111        value: &Value,
112    ) -> ValidationResult<Box<dyn ValueObject>> {
113        // Create a default schema URI for unknown extensions
114        let schema_uri = SchemaUri::new(format!(
115            "urn:ietf:params:scim:schemas:extension:unknown:{}",
116            definition.name
117        ))?;
118
119        let ext_attr = ExtensionAttributeValue::new(
120            schema_uri,
121            definition.name.clone(),
122            value.clone(),
123            Some(definition.clone()),
124        )?;
125
126        Ok(Box::new(ext_attr))
127    }
128
129    /// Register default constructors for built-in value objects.
130    fn register_default_constructors(&mut self) {
131        // Register constructors for core value objects
132        self.registry
133            .register_constructor(Box::new(ResourceIdConstructor::new()));
134        self.registry
135            .register_constructor(Box::new(UserNameConstructor::new()));
136        self.registry
137            .register_constructor(Box::new(ExternalIdConstructor::new()));
138        self.registry
139            .register_constructor(Box::new(EmailAddressConstructor::new()));
140        self.registry
141            .register_constructor(Box::new(SchemaUriConstructor::new()));
142        self.registry
143            .register_constructor(Box::new(NameConstructor::new()));
144        // TODO: Add constructors for Address, PhoneNumber, and Meta when from_json methods are implemented
145    }
146
147    /// Setup mappings from attribute names to expected types.
148    fn setup_type_mappings(&mut self) {
149        self.type_mappings
150            .insert("id".to_string(), AttributeType::String);
151        self.type_mappings
152            .insert("userName".to_string(), AttributeType::String);
153        self.type_mappings
154            .insert("externalId".to_string(), AttributeType::String);
155        self.type_mappings
156            .insert("schemas".to_string(), AttributeType::Reference);
157        self.type_mappings
158            .insert("name".to_string(), AttributeType::Complex);
159        self.type_mappings
160            .insert("emails".to_string(), AttributeType::Complex);
161        self.type_mappings
162            .insert("phoneNumbers".to_string(), AttributeType::Complex);
163        self.type_mappings
164            .insert("addresses".to_string(), AttributeType::Complex);
165        self.type_mappings
166            .insert("meta".to_string(), AttributeType::Complex);
167    }
168
169    /// Get the expected attribute type for a given attribute name.
170    pub fn get_expected_type(&self, attribute_name: &str) -> Option<AttributeType> {
171        self.type_mappings.get(attribute_name).cloned()
172    }
173
174    /// Register a custom constructor.
175    pub fn register_constructor(&mut self, constructor: Box<dyn ValueObjectConstructor>) {
176        self.registry.register_constructor(constructor);
177    }
178
179    /// Validate composite rules across multiple value objects.
180    pub fn validate_composite_rules(
181        &self,
182        objects: &[Box<dyn ValueObject>],
183    ) -> ValidationResult<()> {
184        self.registry.validate_composite_rules(objects)
185    }
186
187    /// Check if the factory has any constructors registered.
188    pub fn has_constructors(&self) -> bool {
189        self.registry.has_constructors()
190    }
191
192    /// Create a collection of value objects from a JSON object.
193    pub fn create_value_objects_from_json(
194        &self,
195        definitions: &[AttributeDefinition],
196        json_obj: &serde_json::Map<String, Value>,
197    ) -> ValidationResult<Vec<Box<dyn ValueObject>>> {
198        let mut objects = Vec::new();
199
200        for definition in definitions {
201            if let Some(value) = json_obj.get(&definition.name) {
202                let obj = self.create_value_object(definition, value)?;
203                objects.push(obj);
204            } else if definition.required {
205                return Err(ValidationError::RequiredAttributeMissing(
206                    definition.name.clone(),
207                ));
208            }
209        }
210
211        Ok(objects)
212    }
213}
214
215impl Default for ValueObjectFactory {
216    fn default() -> Self {
217        Self::new()
218    }
219}
220
221/// Generic multi-valued attribute container for factory-created objects.
222#[derive(Debug)]
223pub struct GenericMultiValuedAttribute {
224    attribute_name: String,
225    values: Vec<Box<dyn ValueObject>>,
226    primary_index: Option<usize>,
227}
228
229impl GenericMultiValuedAttribute {
230    pub fn new(attribute_name: String, values: Vec<Box<dyn ValueObject>>) -> Self {
231        Self {
232            attribute_name,
233            values,
234            primary_index: None,
235        }
236    }
237
238    pub fn values(&self) -> &[Box<dyn ValueObject>] {
239        &self.values
240    }
241
242    pub fn primary(&self) -> Option<&Box<dyn ValueObject>> {
243        self.primary_index.map(|idx| &self.values[idx])
244    }
245
246    pub fn set_primary(&mut self, index: usize) -> ValidationResult<()> {
247        if index >= self.values.len() {
248            return Err(ValidationError::InvalidPrimaryIndex {
249                attribute: self.attribute_name.clone(),
250                index,
251                length: self.values.len(),
252            });
253        }
254        self.primary_index = Some(index);
255        Ok(())
256    }
257}
258
259impl ValueObject for GenericMultiValuedAttribute {
260    fn attribute_type(&self) -> AttributeType {
261        AttributeType::Complex // Multi-valued attributes are complex
262    }
263
264    fn attribute_name(&self) -> &str {
265        &self.attribute_name
266    }
267
268    fn to_json(&self) -> ValidationResult<Value> {
269        let mut array = Vec::new();
270        for value_obj in &self.values {
271            array.push(value_obj.to_json()?);
272        }
273        Ok(Value::Array(array))
274    }
275
276    fn validate_against_schema(&self, definition: &AttributeDefinition) -> ValidationResult<()> {
277        if !definition.multi_valued {
278            return Err(ValidationError::NotMultiValued(definition.name.clone()));
279        }
280
281        // Validate each value object
282        let single_def = AttributeDefinition {
283            multi_valued: false,
284            ..definition.clone()
285        };
286
287        for value_obj in &self.values {
288            value_obj.validate_against_schema(&single_def)?;
289        }
290
291        Ok(())
292    }
293
294    fn as_json_value(&self) -> Value {
295        self.to_json().unwrap_or(Value::Null)
296    }
297
298    fn supports_definition(&self, definition: &AttributeDefinition) -> bool {
299        definition.multi_valued && definition.name == self.attribute_name
300    }
301
302    fn clone_boxed(&self) -> Box<dyn ValueObject> {
303        Box::new(GenericMultiValuedAttribute {
304            attribute_name: self.attribute_name.clone(),
305            values: self.values.iter().map(|v| v.clone_boxed()).collect(),
306            primary_index: self.primary_index,
307        })
308    }
309
310    fn as_any(&self) -> &dyn std::any::Any {
311        self
312    }
313}
314
315// Specific constructors for built-in value objects
316
317pub struct ResourceIdConstructor;
318
319impl ResourceIdConstructor {
320    pub fn new() -> Self {
321        Self
322    }
323}
324
325impl ValueObjectConstructor for ResourceIdConstructor {
326    fn try_construct(
327        &self,
328        definition: &AttributeDefinition,
329        value: &Value,
330    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
331        if definition.name == "id" && definition.data_type == AttributeType::String {
332            if let Some(id_str) = value.as_str() {
333                Some(
334                    ResourceId::new(id_str.to_string())
335                        .map(|id| Box::new(id) as Box<dyn ValueObject>),
336                )
337            } else {
338                Some(Err(ValidationError::InvalidAttributeType {
339                    attribute: definition.name.clone(),
340                    expected: "string".to_string(),
341                    actual: "non-string".to_string(),
342                }))
343            }
344        } else {
345            None
346        }
347    }
348
349    fn priority(&self) -> u8 {
350        100 // High priority for exact matches
351    }
352
353    fn description(&self) -> &str {
354        "ResourceId constructor for 'id' attributes"
355    }
356}
357
358pub struct UserNameConstructor;
359
360impl UserNameConstructor {
361    pub fn new() -> Self {
362        Self
363    }
364}
365
366impl ValueObjectConstructor for UserNameConstructor {
367    fn try_construct(
368        &self,
369        definition: &AttributeDefinition,
370        value: &Value,
371    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
372        if definition.name == "userName" && definition.data_type == AttributeType::String {
373            if let Some(username_str) = value.as_str() {
374                Some(
375                    UserName::new(username_str.to_string())
376                        .map(|username| Box::new(username) as Box<dyn ValueObject>),
377                )
378            } else {
379                Some(Err(ValidationError::InvalidAttributeType {
380                    attribute: definition.name.clone(),
381                    expected: "string".to_string(),
382                    actual: "non-string".to_string(),
383                }))
384            }
385        } else {
386            None
387        }
388    }
389
390    fn priority(&self) -> u8 {
391        100
392    }
393
394    fn description(&self) -> &str {
395        "UserName constructor for 'userName' attributes"
396    }
397}
398
399pub struct ExternalIdConstructor;
400
401impl ExternalIdConstructor {
402    pub fn new() -> Self {
403        Self
404    }
405}
406
407impl ValueObjectConstructor for ExternalIdConstructor {
408    fn try_construct(
409        &self,
410        definition: &AttributeDefinition,
411        value: &Value,
412    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
413        if definition.name == "externalId" && definition.data_type == AttributeType::String {
414            if let Some(ext_id_str) = value.as_str() {
415                Some(
416                    ExternalId::new(ext_id_str.to_string())
417                        .map(|ext_id| Box::new(ext_id) as Box<dyn ValueObject>),
418                )
419            } else {
420                Some(Err(ValidationError::InvalidAttributeType {
421                    attribute: definition.name.clone(),
422                    expected: "string".to_string(),
423                    actual: "non-string".to_string(),
424                }))
425            }
426        } else {
427            None
428        }
429    }
430
431    fn priority(&self) -> u8 {
432        100
433    }
434
435    fn description(&self) -> &str {
436        "ExternalId constructor for 'externalId' attributes"
437    }
438}
439
440pub struct EmailAddressConstructor;
441
442impl EmailAddressConstructor {
443    pub fn new() -> Self {
444        Self
445    }
446}
447
448impl ValueObjectConstructor for EmailAddressConstructor {
449    fn try_construct(
450        &self,
451        definition: &AttributeDefinition,
452        value: &Value,
453    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
454        if (definition.name == "value" || definition.name.contains("email"))
455            && definition.data_type == AttributeType::String
456        {
457            if let Some(email_str) = value.as_str() {
458                Some(
459                    EmailAddress::new(email_str.to_string(), None, None, None)
460                        .map(|email| Box::new(email) as Box<dyn ValueObject>),
461                )
462            } else {
463                Some(Err(ValidationError::InvalidAttributeType {
464                    attribute: definition.name.clone(),
465                    expected: "string".to_string(),
466                    actual: "non-string".to_string(),
467                }))
468            }
469        } else {
470            None
471        }
472    }
473
474    fn priority(&self) -> u8 {
475        80 // Lower priority than exact name matches
476    }
477
478    fn description(&self) -> &str {
479        "EmailAddress constructor for email-related attributes"
480    }
481}
482
483pub struct SchemaUriConstructor;
484
485impl SchemaUriConstructor {
486    pub fn new() -> Self {
487        Self
488    }
489}
490
491impl ValueObjectConstructor for SchemaUriConstructor {
492    fn try_construct(
493        &self,
494        _definition: &AttributeDefinition,
495        _value: &Value,
496    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
497        // TODO: Implement when SchemaUri implements ValueObject trait
498        None
499    }
500
501    fn priority(&self) -> u8 {
502        100
503    }
504
505    fn description(&self) -> &str {
506        "SchemaUri constructor for 'schemas' attributes"
507    }
508}
509
510pub struct NameConstructor;
511
512impl NameConstructor {
513    pub fn new() -> Self {
514        Self
515    }
516}
517
518impl ValueObjectConstructor for NameConstructor {
519    fn try_construct(
520        &self,
521        definition: &AttributeDefinition,
522        value: &Value,
523    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
524        if definition.name == "name" && definition.data_type == AttributeType::Complex {
525            Some(Name::from_json(value).map(|name| Box::new(name) as Box<dyn ValueObject>))
526        } else {
527            None
528        }
529    }
530
531    fn priority(&self) -> u8 {
532        100
533    }
534
535    fn description(&self) -> &str {
536        "Name constructor for 'name' complex attributes"
537    }
538}
539
540// TODO: Implement constructors for Address, PhoneNumber, and Meta when from_json methods are available
541
542#[cfg(test)]
543mod tests {
544    use super::*;
545    use crate::schema::types::{Mutability, Uniqueness};
546
547    fn create_string_definition(name: &str) -> AttributeDefinition {
548        AttributeDefinition {
549            name: name.to_string(),
550            data_type: AttributeType::String,
551            multi_valued: false,
552            required: false,
553            case_exact: false,
554            mutability: Mutability::ReadWrite,
555            uniqueness: Uniqueness::None,
556            canonical_values: vec![],
557            sub_attributes: vec![],
558            returned: None,
559        }
560    }
561
562    #[test]
563    fn test_factory_creation() {
564        let factory = ValueObjectFactory::new();
565        assert!(factory.has_constructors());
566    }
567
568    #[test]
569    fn test_resource_id_construction() {
570        let factory = ValueObjectFactory::new();
571        let definition = create_string_definition("id");
572        let value = Value::String("test-id".to_string());
573
574        let result = factory.create_value_object(&definition, &value);
575        assert!(result.is_ok());
576
577        let obj = result.unwrap();
578        assert_eq!(obj.attribute_name(), "id");
579        assert_eq!(obj.attribute_type(), AttributeType::String);
580    }
581
582    #[test]
583    fn test_username_construction() {
584        let factory = ValueObjectFactory::new();
585        let definition = create_string_definition("userName");
586        let value = Value::String("testuser".to_string());
587
588        let result = factory.create_value_object(&definition, &value);
589        assert!(result.is_ok());
590
591        let obj = result.unwrap();
592        assert_eq!(obj.attribute_name(), "userName");
593    }
594}