use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DependencySpec {
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct DependencyMetadata {
#[serde(skip_serializing_if = "Option::is_none")]
pub dependencies: Option<HashMap<String, Vec<DependencySpec>>>,
}
impl DependencyMetadata {
pub fn has_dependencies(&self) -> bool {
self.dependencies
.as_ref()
.is_some_and(|deps| !deps.is_empty() && deps.values().any(|v| !v.is_empty()))
}
pub fn dependency_count(&self) -> usize {
self.dependencies.as_ref().map_or(0, |deps| deps.values().map(std::vec::Vec::len).sum())
}
pub fn merge(&mut self, other: Self) {
if let Some(other_deps) = other.dependencies {
let deps = self.dependencies.get_or_insert_with(HashMap::new);
for (resource_type, specs) in other_deps {
deps.entry(resource_type).or_default().extend(specs);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dependency_spec_serialization() {
let spec = DependencySpec {
path: "agents/helper.md".to_string(),
version: Some("v1.0.0".to_string()),
};
let yaml = serde_yaml::to_string(&spec).unwrap();
assert!(yaml.contains("path: agents/helper.md"));
assert!(yaml.contains("version: v1.0.0"));
let deserialized: DependencySpec = serde_yaml::from_str(&yaml).unwrap();
assert_eq!(spec, deserialized);
}
#[test]
fn test_dependency_metadata_has_dependencies() {
let mut metadata = DependencyMetadata::default();
assert!(!metadata.has_dependencies());
metadata.dependencies = Some(HashMap::new());
assert!(!metadata.has_dependencies());
let mut deps = HashMap::new();
deps.insert("agents".to_string(), vec![]);
metadata.dependencies = Some(deps);
assert!(!metadata.has_dependencies());
let mut deps = HashMap::new();
deps.insert(
"agents".to_string(),
vec![DependencySpec {
path: "test.md".to_string(),
version: None,
}],
);
metadata.dependencies = Some(deps);
assert!(metadata.has_dependencies());
}
#[test]
fn test_dependency_metadata_merge() {
let mut metadata1 = DependencyMetadata::default();
let mut deps1 = HashMap::new();
deps1.insert(
"agents".to_string(),
vec![DependencySpec {
path: "agent1.md".to_string(),
version: None,
}],
);
metadata1.dependencies = Some(deps1);
let mut metadata2 = DependencyMetadata::default();
let mut deps2 = HashMap::new();
deps2.insert(
"agents".to_string(),
vec![DependencySpec {
path: "agent2.md".to_string(),
version: None,
}],
);
deps2.insert(
"snippets".to_string(),
vec![DependencySpec {
path: "snippet1.md".to_string(),
version: Some("v1.0.0".to_string()),
}],
);
metadata2.dependencies = Some(deps2);
metadata1.merge(metadata2);
assert_eq!(metadata1.dependency_count(), 3);
let deps = metadata1.dependencies.as_ref().unwrap();
assert_eq!(deps["agents"].len(), 2);
assert_eq!(deps["snippets"].len(), 1);
}
}