use std::collections::HashMap;
use std::sync::{Mutex, OnceLock};
use super::{Template, TemplateKey};
fn registry() -> &'static Mutex<HashMap<TemplateKey, Template>> {
static REGISTRY: OnceLock<Mutex<HashMap<TemplateKey, Template>>> = OnceLock::new();
REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
}
pub fn register(template: Template) {
let mut map = registry().lock().unwrap_or_else(|e| e.into_inner());
map.insert(template.key.clone(), template);
}
pub fn get(key: &TemplateKey) -> Option<Template> {
let map = registry().lock().unwrap_or_else(|e| e.into_inner());
map.get(key).cloned()
}
pub fn find_running(file: &str, line: u32) -> Option<Template> {
let map = registry().lock().unwrap_or_else(|e| e.into_inner());
map.values()
.find(|t| t.key.line == line && files_match(&t.key.file, file))
.cloned()
}
fn files_match(a: &str, b: &str) -> bool {
if a == b {
return true;
}
let a: Vec<&str> = a.split('/').filter(|s| !s.is_empty()).collect();
let b: Vec<&str> = b.split('/').filter(|s| !s.is_empty()).collect();
let n = a.len().min(b.len());
n > 0 && a[a.len() - n..] == b[b.len() - n..]
}
pub fn len() -> usize {
registry().lock().unwrap_or_else(|e| e.into_inner()).len()
}
pub fn snapshot() -> Vec<Template> {
registry().lock().unwrap_or_else(|e| e.into_inner()).values().cloned().collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::template::{StaticValue, TemplateNode};
fn tmpl(file: &str, line: u32, label: &str) -> Template {
Template::new(
TemplateKey::new(file, line, 1),
TemplateNode::new("Text").with_static("content", StaticValue::Str(label.into())),
)
}
#[test]
fn register_then_get_round_trips() {
let t = tmpl("src/reg_a.rs", 10, "hello");
register(t.clone());
assert_eq!(get(&t.key), Some(t));
}
#[test]
fn get_unknown_key_is_none() {
assert_eq!(get(&TemplateKey::new("src/never_registered.rs", 999, 1)), None);
}
#[test]
fn re_registering_a_key_replaces_the_shape() {
let key = TemplateKey::new("src/reg_b.rs", 20, 1);
register(Template::new(key.clone(), TemplateNode::new("Text").with_static("content", StaticValue::Str("v1".into()))));
register(Template::new(key.clone(), TemplateNode::new("Text").with_static("content", StaticValue::Str("v2".into()))));
let got = get(&key).unwrap();
assert_eq!(got.root.props[0].1, PropValueStr("v2"));
}
#[allow(non_snake_case)]
fn PropValueStr(s: &str) -> crate::template::PropValue {
crate::template::PropValue::Static(StaticValue::Str(s.into()))
}
}