use super::manifest::SkillManifest;
use anyhow::{Context, Result};
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Skill {
pub name: Arc<str>,
pub path: PathBuf,
pub manifest: Option<SkillManifest>,
}
pub struct SkillLoader {
skills_dir: PathBuf,
skills: HashMap<Arc<str>, Skill>,
}
impl SkillLoader {
pub fn new(skills_dir: PathBuf) -> Self {
Self {
skills_dir,
skills: HashMap::new(),
}
}
pub fn skills_dir(&self) -> &Path {
&self.skills_dir
}
pub fn load_all(&mut self) -> Result<usize> {
if !self.skills_dir.exists() {
fs::create_dir_all(&self.skills_dir).context("Failed to create skills directory")?;
return Ok(0);
}
let mut count = 0;
let entries = fs::read_dir(&self.skills_dir).context("Failed to read skills directory")?;
for entry in entries {
let entry = entry.context("Failed to read directory entry")?;
let path = entry.path();
if path.is_file() {
let file_name = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or_default();
if file_name.ends_with(".manifest.json") {
continue;
}
let skill_name = Arc::from(file_name);
if self.skills.contains_key(&skill_name) {
continue;
}
let manifest = self.load_manifest(&path);
let skill = Skill {
name: skill_name.clone(),
path: path.clone(),
manifest,
};
self.skills.insert(skill_name, skill);
count += 1;
}
}
Ok(count)
}
fn load_manifest(&self, skill_path: &Path) -> Option<SkillManifest> {
let manifest_path = skill_path.with_extension("manifest.json");
if !manifest_path.exists() {
return None;
}
let content = fs::read_to_string(&manifest_path).ok()?;
let manifest: SkillManifest = serde_json::from_str(&content).ok()?;
Some(manifest)
}
pub fn get(&self, name: &str) -> Option<&Skill> {
self.skills.get(name)
}
pub fn list(&self) -> Vec<&Skill> {
self.skills.values().collect()
}
pub fn count(&self) -> usize {
self.skills.len()
}
}