use std::collections::HashMap;
use std::sync::Mutex;
use camel_api::{CamelError, RouteTemplateSpec, TemplateError, TemplateInstanceRecord};
pub struct TemplateRegistry {
templates: Mutex<HashMap<String, RouteTemplateSpec>>,
instances: Mutex<HashMap<String, Vec<TemplateInstanceRecord>>>,
}
impl TemplateRegistry {
pub fn new() -> Self {
Self {
templates: Mutex::new(HashMap::new()),
instances: Mutex::new(HashMap::new()),
}
}
pub fn register(&self, spec: RouteTemplateSpec) -> Result<(), CamelError> {
let id = spec.id.clone();
let mut templates = self
.templates
.lock()
.expect("template registry mutex poisoned"); if templates.contains_key(&id) {
return Err(TemplateError::AlreadyRegistered(id).into());
}
templates.insert(id, spec);
Ok(())
}
pub fn get(&self, id: &str) -> Option<RouteTemplateSpec> {
let templates = self
.templates
.lock()
.expect("template registry mutex poisoned"); templates.get(id).cloned()
}
pub fn template_ids(&self) -> Vec<String> {
let templates = self
.templates
.lock()
.expect("template registry mutex poisoned"); templates.keys().cloned().collect()
}
pub fn record_instance(&self, record: TemplateInstanceRecord) {
let template_id = record.template_id.clone();
let mut instances = self
.instances
.lock()
.expect("template instances mutex poisoned"); instances.entry(template_id).or_default().push(record);
}
pub fn instances(&self, template_id: &str) -> Vec<TemplateInstanceRecord> {
let instances = self
.instances
.lock()
.expect("template instances mutex poisoned"); instances.get(template_id).cloned().unwrap_or_default()
}
}
impl Default for TemplateRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
use uuid::Uuid;
fn make_template(id: &str) -> RouteTemplateSpec {
RouteTemplateSpec {
id: id.to_string(),
parameters: vec![],
routes: vec![serde_json::json!({"from": {"uri": "timer:tick"}})],
}
}
fn make_instance(template_id: &str, route_id: &str) -> TemplateInstanceRecord {
TemplateInstanceRecord {
template_id: template_id.to_string(),
instance_id: Uuid::nil(),
route_id: route_id.to_string(),
parameters: BTreeMap::new(),
}
}
#[test]
fn register_and_get_template() {
let registry = TemplateRegistry::new();
let spec = make_template("test-tpl");
registry.register(spec.clone()).unwrap();
let retrieved = registry.get("test-tpl").expect("template should exist");
assert_eq!(retrieved.id, "test-tpl");
}
#[test]
fn get_returns_none_for_unknown_template() {
let registry = TemplateRegistry::new();
assert!(registry.get("nonexistent").is_none());
}
#[test]
fn duplicate_registration_returns_error() {
let registry = TemplateRegistry::new();
registry.register(make_template("dup")).unwrap();
let result = registry.register(make_template("dup"));
assert!(result.is_err());
let err = result.unwrap_err();
assert!(format!("{err}").contains("already registered"));
}
#[test]
fn template_ids_returns_all_registered_ids() {
let registry = TemplateRegistry::new();
registry.register(make_template("a")).unwrap();
registry.register(make_template("b")).unwrap();
registry.register(make_template("c")).unwrap();
let mut ids = registry.template_ids();
ids.sort();
assert_eq!(ids, vec!["a", "b", "c"]);
}
#[test]
fn record_and_retrieve_instances() {
let registry = TemplateRegistry::new();
let inst1 = make_instance("tpl-1", "route-1");
let inst2 = make_instance("tpl-1", "route-2");
let inst3 = make_instance("tpl-2", "route-3");
registry.record_instance(inst1);
registry.record_instance(inst2);
registry.record_instance(inst3);
let tpl1_instances = registry.instances("tpl-1");
assert_eq!(tpl1_instances.len(), 2);
let tpl2_instances = registry.instances("tpl-2");
assert_eq!(tpl2_instances.len(), 1);
assert_eq!(tpl2_instances[0].route_id, "route-3");
}
#[test]
fn instances_returns_empty_for_unknown_template() {
let registry = TemplateRegistry::new();
let instances = registry.instances("nonexistent");
assert!(instances.is_empty());
}
#[test]
fn template_ids_empty_when_no_templates() {
let registry = TemplateRegistry::new();
assert!(registry.template_ids().is_empty());
}
}