agpm_cli/manifest/
dependency_spec.rs1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
15pub struct DependencySpec {
16 pub path: String,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
32 pub version: Option<String>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
41pub struct DependencyMetadata {
42 #[serde(skip_serializing_if = "Option::is_none")]
57 pub dependencies: Option<HashMap<String, Vec<DependencySpec>>>,
58}
59
60impl DependencyMetadata {
61 pub fn has_dependencies(&self) -> bool {
63 self.dependencies
64 .as_ref()
65 .is_some_and(|deps| !deps.is_empty() && deps.values().any(|v| !v.is_empty()))
66 }
67
68 pub fn dependency_count(&self) -> usize {
70 self.dependencies.as_ref().map_or(0, |deps| deps.values().map(std::vec::Vec::len).sum())
71 }
72
73 pub fn merge(&mut self, other: Self) {
77 if let Some(other_deps) = other.dependencies {
78 let deps = self.dependencies.get_or_insert_with(HashMap::new);
79 for (resource_type, specs) in other_deps {
80 deps.entry(resource_type).or_default().extend(specs);
81 }
82 }
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_dependency_spec_serialization() {
92 let spec = DependencySpec {
93 path: "agents/helper.md".to_string(),
94 version: Some("v1.0.0".to_string()),
95 };
96
97 let yaml = serde_yaml::to_string(&spec).unwrap();
98 assert!(yaml.contains("path: agents/helper.md"));
99 assert!(yaml.contains("version: v1.0.0"));
100
101 let deserialized: DependencySpec = serde_yaml::from_str(&yaml).unwrap();
102 assert_eq!(spec, deserialized);
103 }
104
105 #[test]
106 fn test_dependency_metadata_has_dependencies() {
107 let mut metadata = DependencyMetadata::default();
108 assert!(!metadata.has_dependencies());
109
110 metadata.dependencies = Some(HashMap::new());
111 assert!(!metadata.has_dependencies());
112
113 let mut deps = HashMap::new();
114 deps.insert("agents".to_string(), vec![]);
115 metadata.dependencies = Some(deps);
116 assert!(!metadata.has_dependencies());
117
118 let mut deps = HashMap::new();
119 deps.insert(
120 "agents".to_string(),
121 vec![DependencySpec {
122 path: "test.md".to_string(),
123 version: None,
124 }],
125 );
126 metadata.dependencies = Some(deps);
127 assert!(metadata.has_dependencies());
128 }
129
130 #[test]
131 fn test_dependency_metadata_merge() {
132 let mut metadata1 = DependencyMetadata::default();
133 let mut deps1 = HashMap::new();
134 deps1.insert(
135 "agents".to_string(),
136 vec![DependencySpec {
137 path: "agent1.md".to_string(),
138 version: None,
139 }],
140 );
141 metadata1.dependencies = Some(deps1);
142
143 let mut metadata2 = DependencyMetadata::default();
144 let mut deps2 = HashMap::new();
145 deps2.insert(
146 "agents".to_string(),
147 vec![DependencySpec {
148 path: "agent2.md".to_string(),
149 version: None,
150 }],
151 );
152 deps2.insert(
153 "snippets".to_string(),
154 vec![DependencySpec {
155 path: "snippet1.md".to_string(),
156 version: Some("v1.0.0".to_string()),
157 }],
158 );
159 metadata2.dependencies = Some(deps2);
160
161 metadata1.merge(metadata2);
162
163 assert_eq!(metadata1.dependency_count(), 3);
164 let deps = metadata1.dependencies.as_ref().unwrap();
165 assert_eq!(deps["agents"].len(), 2);
166 assert_eq!(deps["snippets"].len(), 1);
167 }
168}