use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::OnceCell;
use crate::personas::PersonaRegistry;
use crate::skills::SkillRegistry;
struct Registries {
skills: Arc<SkillRegistry>,
personas: Arc<PersonaRegistry>,
}
static REGISTRIES: OnceCell<Registries> = OnceCell::const_new();
async fn get() -> &'static Registries {
REGISTRIES
.get_or_init(|| async {
let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let built = tokio::task::spawn_blocking(move || {
let (skills, mut warnings) = SkillRegistry::discover(&cwd);
let (personas, persona_warnings) = PersonaRegistry::discover(&skills);
warnings.extend(persona_warnings);
(skills, personas, warnings)
})
.await;
match built {
Ok((skills, personas, warnings)) => {
for warning in warnings {
crate::diag::warn(warning);
}
Registries {
skills: Arc::new(skills),
personas: Arc::new(personas),
}
}
Err(e) => {
crate::diag::warn(format!("Skill discovery failed: {}", e));
Registries {
skills: Arc::new(SkillRegistry::new()),
personas: Arc::new(PersonaRegistry::new()),
}
}
}
})
.await
}
pub async fn skills() -> Arc<SkillRegistry> {
Arc::clone(&get().await.skills)
}
pub async fn personas() -> Arc<PersonaRegistry> {
Arc::clone(&get().await.personas)
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn discovery_happens_once_and_is_shared() {
let first = skills().await;
let second = skills().await;
assert!(
Arc::ptr_eq(&first, &second),
"the registry must be shared, not rebuilt per call"
);
let personas_first = personas().await;
let personas_second = personas().await;
assert!(Arc::ptr_eq(&personas_first, &personas_second));
}
#[tokio::test]
async fn every_persona_comes_from_a_discovered_skill() {
let skills = skills().await;
for persona in personas().await.all() {
assert!(
skills.get(&persona.skill_name).is_some(),
"persona {} has no backing skill",
persona.name
);
}
}
}