use super::{MacroError, Macroforge, error::Result};
use dashmap::DashMap;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MacroKey {
pub module: String,
pub name: String,
}
impl MacroKey {
pub fn new(module: impl Into<String>, name: impl Into<String>) -> Self {
Self {
module: module.into(),
name: name.into(),
}
}
}
pub struct MacroRegistry {
macros: DashMap<MacroKey, Arc<dyn Macroforge>>,
}
impl MacroRegistry {
pub fn new() -> Self {
Self {
macros: DashMap::new(),
}
}
pub fn register(
&self,
module: impl Into<String>,
name: impl Into<String>,
macro_impl: Arc<dyn Macroforge>,
) -> Result<()> {
let key = MacroKey::new(module, name);
if self.macros.contains_key(&key) {
return Err(MacroError::InvalidConfig(format!(
"Macro '{}::{}' is already registered",
key.module, key.name
)));
}
self.macros.insert(key, macro_impl);
Ok(())
}
pub fn lookup(&self, module: &str, name: &str) -> Result<Arc<dyn Macroforge>> {
let key = MacroKey::new(module, name);
self.macros
.get(&key)
.map(|entry| Arc::clone(&entry))
.ok_or_else(|| MacroError::MacroNotFound {
module: module.to_string(),
name: name.to_string(),
})
}
pub fn all_macros(&self) -> Vec<(MacroKey, Arc<dyn Macroforge>)> {
self.macros
.iter()
.map(|entry| (entry.key().clone(), Arc::clone(entry.value())))
.collect()
}
pub fn contains(&self, module: &str, name: &str) -> bool {
let key = MacroKey::new(module, name);
self.macros.contains_key(&key)
}
pub fn lookup_by_name(&self, name: &str) -> Result<Arc<dyn Macroforge>> {
for entry in self.macros.iter() {
if entry.key().name == name {
return Ok(Arc::clone(&entry));
}
}
Err(MacroError::MacroNotFound {
module: "<any>".to_string(),
name: name.to_string(),
})
}
pub fn lookup_with_fallback(&self, module: &str, name: &str) -> Result<Arc<dyn Macroforge>> {
if let Ok(m) = self.lookup(module, name) {
return Ok(m);
}
self.lookup_by_name(name)
}
pub fn clear(&self) {
self.macros.clear();
}
}
impl Default for MacroRegistry {
fn default() -> Self {
Self::new()
}
}