scim_server/resource/
handlers.rs

1//! Handler infrastructure for dynamic resource operations.
2//!
3//! This module provides the infrastructure for creating and managing
4//! dynamic resource handlers that can be configured at runtime with
5//! custom attribute handlers, mappers, and methods.
6
7use super::mapper::SchemaMapper;
8use crate::error::ScimError;
9use crate::schema::Schema;
10use serde_json::Value;
11use std::collections::HashMap;
12use std::sync::Arc;
13
14/// Dynamic attribute handler for schema-driven operations
15#[derive(Clone)]
16pub enum AttributeHandler {
17    Getter(Arc<dyn Fn(&Value) -> Option<Value> + Send + Sync>),
18    Setter(Arc<dyn Fn(&mut Value, Value) -> Result<(), ScimError> + Send + Sync>),
19    Transformer(Arc<dyn Fn(&Value, &str) -> Option<Value> + Send + Sync>),
20}
21
22/// Handler for a specific resource type containing all its dynamic behaviors
23#[derive(Clone)]
24pub struct ResourceHandler {
25    pub schema: Schema,
26    pub handlers: HashMap<String, AttributeHandler>,
27    pub mappers: Vec<Arc<dyn SchemaMapper>>,
28    pub custom_methods:
29        HashMap<String, Arc<dyn Fn(&DynamicResource) -> Result<Value, ScimError> + Send + Sync>>,
30}
31
32impl std::fmt::Debug for ResourceHandler {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        f.debug_struct("ResourceHandler")
35            .field("schema", &self.schema)
36            .field("handlers", &format!("{} handlers", self.handlers.len()))
37            .field("mappers", &format!("{} mappers", self.mappers.len()))
38            .field(
39                "custom_methods",
40                &format!("{} custom methods", self.custom_methods.len()),
41            )
42            .finish()
43    }
44}
45
46/// Builder for creating resource handlers with fluent API
47pub struct SchemaResourceBuilder {
48    schema: Schema,
49    handlers: HashMap<String, AttributeHandler>,
50    mappers: Vec<Arc<dyn SchemaMapper>>,
51    custom_methods:
52        HashMap<String, Arc<dyn Fn(&DynamicResource) -> Result<Value, ScimError> + Send + Sync>>,
53}
54
55impl SchemaResourceBuilder {
56    pub fn new(schema: Schema) -> Self {
57        Self {
58            schema,
59            handlers: HashMap::new(),
60            mappers: Vec::new(),
61            custom_methods: HashMap::new(),
62        }
63    }
64
65    pub fn with_getter<F>(mut self, attribute: &str, getter: F) -> Self
66    where
67        F: Fn(&Value) -> Option<Value> + Send + Sync + 'static,
68    {
69        self.handlers.insert(
70            format!("get_{}", attribute),
71            AttributeHandler::Getter(Arc::new(getter)),
72        );
73        self
74    }
75
76    pub fn with_setter<F>(mut self, attribute: &str, setter: F) -> Self
77    where
78        F: Fn(&mut Value, Value) -> Result<(), ScimError> + Send + Sync + 'static,
79    {
80        self.handlers.insert(
81            format!("set_{}", attribute),
82            AttributeHandler::Setter(Arc::new(setter)),
83        );
84        self
85    }
86
87    pub fn with_transformer<F>(mut self, attribute: &str, transformer: F) -> Self
88    where
89        F: Fn(&Value, &str) -> Option<Value> + Send + Sync + 'static,
90    {
91        self.handlers.insert(
92            format!("transform_{}", attribute),
93            AttributeHandler::Transformer(Arc::new(transformer)),
94        );
95        self
96    }
97
98    pub fn with_custom_method<F>(mut self, method_name: &str, method: F) -> Self
99    where
100        F: Fn(&DynamicResource) -> Result<Value, ScimError> + Send + Sync + 'static,
101    {
102        self.custom_methods
103            .insert(method_name.to_string(), Arc::new(method));
104        self
105    }
106
107    pub fn with_mapper(mut self, mapper: Arc<dyn SchemaMapper>) -> Self {
108        self.mappers.push(mapper);
109        self
110    }
111
112    pub fn with_database_mapping(
113        self,
114        table_name: &str,
115        column_mappings: HashMap<String, String>,
116    ) -> Self {
117        self.with_mapper(Arc::new(super::mapper::DatabaseMapper::new(
118            table_name,
119            column_mappings,
120        )))
121    }
122
123    pub fn build(self) -> ResourceHandler {
124        ResourceHandler {
125            schema: self.schema,
126            handlers: self.handlers,
127            mappers: self.mappers,
128            custom_methods: self.custom_methods,
129        }
130    }
131}
132
133/// Dynamic resource that uses registered handlers for operations
134#[derive(Clone, Debug)]
135pub struct DynamicResource {
136    pub resource_type: String,
137    pub data: Value,
138    pub handler: Arc<ResourceHandler>,
139}
140
141impl DynamicResource {
142    pub fn new(resource_type: String, data: Value, handler: Arc<ResourceHandler>) -> Self {
143        Self {
144            resource_type,
145            data,
146            handler,
147        }
148    }
149
150    pub fn get_attribute_dynamic(&self, attribute: &str) -> Option<Value> {
151        let getter_key = format!("get_{}", attribute);
152        if let Some(AttributeHandler::Getter(getter)) = self.handler.handlers.get(&getter_key) {
153            getter(&self.data)
154        } else {
155            // Fallback to direct access
156            self.data.get(attribute).cloned()
157        }
158    }
159
160    pub fn set_attribute_dynamic(
161        &mut self,
162        attribute: &str,
163        value: Value,
164    ) -> Result<(), ScimError> {
165        let setter_key = format!("set_{}", attribute);
166        if let Some(AttributeHandler::Setter(setter)) = self.handler.handlers.get(&setter_key) {
167            setter(&mut self.data, value)
168        } else {
169            // Fallback to direct setting
170            if let Some(obj) = self.data.as_object_mut() {
171                obj.insert(attribute.to_string(), value);
172            }
173            Ok(())
174        }
175    }
176
177    pub fn call_custom_method(&self, method_name: &str) -> Result<Value, ScimError> {
178        if let Some(method) = self.handler.custom_methods.get(method_name) {
179            method(self)
180        } else {
181            Err(ScimError::MethodNotFound(method_name.to_string()))
182        }
183    }
184
185    pub fn to_implementation_schema(&self, mapper_index: usize) -> Result<Value, ScimError> {
186        if let Some(mapper) = self.handler.mappers.get(mapper_index) {
187            mapper.to_implementation(&self.data)
188        } else {
189            Err(ScimError::MapperNotFound(mapper_index))
190        }
191    }
192
193    pub fn from_implementation_schema(
194        &mut self,
195        impl_data: &Value,
196        mapper_index: usize,
197    ) -> Result<(), ScimError> {
198        if let Some(mapper) = self.handler.mappers.get(mapper_index) {
199            self.data = mapper.from_implementation(impl_data)?;
200            Ok(())
201        } else {
202            Err(ScimError::MapperNotFound(mapper_index))
203        }
204    }
205}