Skip to main content

automapper_validation/eval/
registry.rs

1//! Registry of condition evaluators keyed by (message_type, format_version).
2
3use std::collections::HashMap;
4use std::sync::{Arc, RwLock};
5
6use super::evaluator::ConditionEvaluator;
7
8/// Global registry of condition evaluators.
9///
10/// Evaluators are registered at startup (typically from generated code)
11/// and looked up at runtime based on the detected message type and format
12/// version.
13///
14/// Thread-safe: uses `RwLock` for concurrent read access with exclusive
15/// write access during registration.
16pub struct EvaluatorRegistry {
17    evaluators: RwLock<HashMap<(String, String), Arc<dyn ConditionEvaluator>>>,
18}
19
20impl EvaluatorRegistry {
21    /// Create a new empty registry.
22    pub fn new() -> Self {
23        Self {
24            evaluators: RwLock::new(HashMap::new()),
25        }
26    }
27
28    /// Register a condition evaluator for a message type and format version.
29    ///
30    /// Overwrites any previously registered evaluator for the same key.
31    pub fn register<E: ConditionEvaluator + 'static>(&self, evaluator: E) {
32        let key = (
33            evaluator.message_type().to_string(),
34            evaluator.format_version().to_string(),
35        );
36        self.evaluators
37            .write()
38            .expect("registry lock poisoned")
39            .insert(key, Arc::new(evaluator));
40    }
41
42    /// Register a condition evaluator under a custom key, overriding the
43    /// evaluator's own `message_type()` and `format_version()`.
44    ///
45    /// Useful for type aliases where the underlying evaluator reports a
46    /// different format version than the one we want to register under.
47    pub fn register_as<E: ConditionEvaluator + 'static>(
48        &self,
49        evaluator: E,
50        message_type: &str,
51        format_version: &str,
52    ) {
53        let key = (message_type.to_string(), format_version.to_string());
54        self.evaluators
55            .write()
56            .expect("registry lock poisoned")
57            .insert(key, Arc::new(evaluator));
58    }
59
60    /// Register an already-Arc'd evaluator.
61    pub fn register_arc(&self, evaluator: Arc<dyn ConditionEvaluator>) {
62        let key = (
63            evaluator.message_type().to_string(),
64            evaluator.format_version().to_string(),
65        );
66        self.evaluators
67            .write()
68            .expect("registry lock poisoned")
69            .insert(key, evaluator);
70    }
71
72    /// Look up an evaluator by message type and format version.
73    ///
74    /// Returns `None` if no evaluator is registered for the given key.
75    pub fn get(
76        &self,
77        message_type: &str,
78        format_version: &str,
79    ) -> Option<Arc<dyn ConditionEvaluator>> {
80        self.evaluators
81            .read()
82            .expect("registry lock poisoned")
83            .get(&(message_type.to_string(), format_version.to_string()))
84            .cloned()
85    }
86
87    /// Look up an evaluator, returning an error if not found.
88    pub fn get_or_err(
89        &self,
90        message_type: &str,
91        format_version: &str,
92    ) -> Result<Arc<dyn ConditionEvaluator>, crate::error::ValidationError> {
93        self.get(message_type, format_version).ok_or_else(|| {
94            crate::error::ValidationError::NoEvaluator {
95                message_type: message_type.to_string(),
96                format_version: format_version.to_string(),
97            }
98        })
99    }
100
101    /// List all registered (message_type, format_version) keys.
102    pub fn registered_keys(&self) -> Vec<(String, String)> {
103        self.evaluators
104            .read()
105            .expect("registry lock poisoned")
106            .keys()
107            .cloned()
108            .collect()
109    }
110
111    /// Clear all registered evaluators. Primarily for testing.
112    pub fn clear(&self) {
113        self.evaluators
114            .write()
115            .expect("registry lock poisoned")
116            .clear();
117    }
118}
119
120impl Default for EvaluatorRegistry {
121    fn default() -> Self {
122        Self::new()
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::super::context::EvaluationContext;
129    use super::super::evaluator::ConditionResult;
130    use super::*;
131
132    /// Minimal evaluator for testing registry operations.
133    struct TestEvaluator {
134        msg_type: String,
135        fmt_version: String,
136    }
137
138    impl TestEvaluator {
139        fn new(msg_type: &str, fmt_version: &str) -> Self {
140            Self {
141                msg_type: msg_type.to_string(),
142                fmt_version: fmt_version.to_string(),
143            }
144        }
145    }
146
147    impl ConditionEvaluator for TestEvaluator {
148        fn evaluate(&self, _condition: u32, _ctx: &EvaluationContext) -> ConditionResult {
149            ConditionResult::Unknown
150        }
151        fn is_external(&self, _condition: u32) -> bool {
152            false
153        }
154        fn message_type(&self) -> &str {
155            &self.msg_type
156        }
157        fn format_version(&self) -> &str {
158            &self.fmt_version
159        }
160    }
161
162    #[test]
163    fn test_register_and_get() {
164        let registry = EvaluatorRegistry::new();
165        registry.register(TestEvaluator::new("UTILMD", "FV2510"));
166
167        let eval = registry.get("UTILMD", "FV2510");
168        assert!(eval.is_some());
169        assert_eq!(eval.unwrap().message_type(), "UTILMD");
170    }
171
172    #[test]
173    fn test_get_nonexistent_returns_none() {
174        let registry = EvaluatorRegistry::new();
175        assert!(registry.get("UTILMD", "FV2510").is_none());
176    }
177
178    #[test]
179    fn test_get_or_err_returns_error() {
180        let registry = EvaluatorRegistry::new();
181        let result = registry.get_or_err("UTILMD", "FV2510");
182        assert!(result.is_err());
183    }
184
185    #[test]
186    fn test_register_overwrites() {
187        let registry = EvaluatorRegistry::new();
188        registry.register(TestEvaluator::new("UTILMD", "FV2510"));
189        registry.register(TestEvaluator::new("UTILMD", "FV2510"));
190
191        let keys = registry.registered_keys();
192        assert_eq!(keys.len(), 1);
193    }
194
195    #[test]
196    fn test_multiple_registrations() {
197        let registry = EvaluatorRegistry::new();
198        registry.register(TestEvaluator::new("UTILMD", "FV2504"));
199        registry.register(TestEvaluator::new("UTILMD", "FV2510"));
200        registry.register(TestEvaluator::new("ORDERS", "FV2510"));
201
202        let keys = registry.registered_keys();
203        assert_eq!(keys.len(), 3);
204
205        assert!(registry.get("UTILMD", "FV2504").is_some());
206        assert!(registry.get("UTILMD", "FV2510").is_some());
207        assert!(registry.get("ORDERS", "FV2510").is_some());
208        assert!(registry.get("ORDERS", "FV2504").is_none());
209    }
210
211    #[test]
212    fn test_clear() {
213        let registry = EvaluatorRegistry::new();
214        registry.register(TestEvaluator::new("UTILMD", "FV2510"));
215        assert!(!registry.registered_keys().is_empty());
216
217        registry.clear();
218        assert!(registry.registered_keys().is_empty());
219    }
220
221    #[test]
222    fn test_register_arc() {
223        let registry = EvaluatorRegistry::new();
224        let eval: Arc<dyn ConditionEvaluator> = Arc::new(TestEvaluator::new("UTILMD", "FV2510"));
225        registry.register_arc(eval);
226
227        assert!(registry.get("UTILMD", "FV2510").is_some());
228    }
229}