1use std::collections::HashMap;
2use std::sync::Mutex;
3
4use camel_api::{CamelError, RouteTemplateSpec, TemplateError, TemplateInstanceRecord};
5
6pub struct TemplateRegistry {
12 templates: Mutex<HashMap<String, RouteTemplateSpec>>,
13 instances: Mutex<HashMap<String, Vec<TemplateInstanceRecord>>>,
14}
15
16impl TemplateRegistry {
17 pub fn new() -> Self {
19 Self {
20 templates: Mutex::new(HashMap::new()),
21 instances: Mutex::new(HashMap::new()),
22 }
23 }
24
25 pub fn register(&self, spec: RouteTemplateSpec) -> Result<(), CamelError> {
29 let id = spec.id.clone();
30 let mut templates = self
31 .templates
32 .lock()
33 .expect("template registry mutex poisoned"); if templates.contains_key(&id) {
35 return Err(TemplateError::AlreadyRegistered(id).into());
36 }
37 templates.insert(id, spec);
38 Ok(())
39 }
40
41 pub fn get(&self, id: &str) -> Option<RouteTemplateSpec> {
43 let templates = self
44 .templates
45 .lock()
46 .expect("template registry mutex poisoned"); templates.get(id).cloned()
48 }
49
50 pub fn template_ids(&self) -> Vec<String> {
52 let templates = self
53 .templates
54 .lock()
55 .expect("template registry mutex poisoned"); templates.keys().cloned().collect()
57 }
58
59 pub fn record_instance(&self, record: TemplateInstanceRecord) {
61 let template_id = record.template_id.clone();
62 let mut instances = self
63 .instances
64 .lock()
65 .expect("template instances mutex poisoned"); instances.entry(template_id).or_default().push(record);
67 }
68
69 pub fn instances(&self, template_id: &str) -> Vec<TemplateInstanceRecord> {
71 let instances = self
72 .instances
73 .lock()
74 .expect("template instances mutex poisoned"); instances.get(template_id).cloned().unwrap_or_default()
76 }
77}
78
79impl Default for TemplateRegistry {
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use std::collections::BTreeMap;
89 use uuid::Uuid;
90
91 fn make_template(id: &str) -> RouteTemplateSpec {
92 RouteTemplateSpec {
93 id: id.to_string(),
94 parameters: vec![],
95 route: serde_json::json!({"from": {"uri": "timer:tick"}}),
96 }
97 }
98
99 fn make_instance(template_id: &str, route_id: &str) -> TemplateInstanceRecord {
100 TemplateInstanceRecord {
101 template_id: template_id.to_string(),
102 instance_id: Uuid::nil(),
103 route_id: route_id.to_string(),
104 parameters: BTreeMap::new(),
105 }
106 }
107
108 #[test]
109 fn register_and_get_template() {
110 let registry = TemplateRegistry::new();
111 let spec = make_template("test-tpl");
112 registry.register(spec.clone()).unwrap();
113
114 let retrieved = registry.get("test-tpl").expect("template should exist");
115 assert_eq!(retrieved.id, "test-tpl");
116 }
117
118 #[test]
119 fn get_returns_none_for_unknown_template() {
120 let registry = TemplateRegistry::new();
121 assert!(registry.get("nonexistent").is_none());
122 }
123
124 #[test]
125 fn duplicate_registration_returns_error() {
126 let registry = TemplateRegistry::new();
127 registry.register(make_template("dup")).unwrap();
128 let result = registry.register(make_template("dup"));
129 assert!(result.is_err());
130 let err = result.unwrap_err();
131 assert!(format!("{err}").contains("already registered"));
132 }
133
134 #[test]
135 fn template_ids_returns_all_registered_ids() {
136 let registry = TemplateRegistry::new();
137 registry.register(make_template("a")).unwrap();
138 registry.register(make_template("b")).unwrap();
139 registry.register(make_template("c")).unwrap();
140
141 let mut ids = registry.template_ids();
142 ids.sort();
143 assert_eq!(ids, vec!["a", "b", "c"]);
144 }
145
146 #[test]
147 fn record_and_retrieve_instances() {
148 let registry = TemplateRegistry::new();
149 let inst1 = make_instance("tpl-1", "route-1");
150 let inst2 = make_instance("tpl-1", "route-2");
151 let inst3 = make_instance("tpl-2", "route-3");
152
153 registry.record_instance(inst1);
154 registry.record_instance(inst2);
155 registry.record_instance(inst3);
156
157 let tpl1_instances = registry.instances("tpl-1");
158 assert_eq!(tpl1_instances.len(), 2);
159
160 let tpl2_instances = registry.instances("tpl-2");
161 assert_eq!(tpl2_instances.len(), 1);
162 assert_eq!(tpl2_instances[0].route_id, "route-3");
163 }
164
165 #[test]
166 fn instances_returns_empty_for_unknown_template() {
167 let registry = TemplateRegistry::new();
168 let instances = registry.instances("nonexistent");
169 assert!(instances.is_empty());
170 }
171
172 #[test]
173 fn template_ids_empty_when_no_templates() {
174 let registry = TemplateRegistry::new();
175 assert!(registry.template_ids().is_empty());
176 }
177}