1use mockforge_plugin_core::*;
7
8pub struct ManifestBuilder {
24 manifest: PluginManifest,
25}
26
27impl ManifestBuilder {
28 pub fn new(id: &str, version: &str) -> Self {
30 let version = PluginVersion::parse(version).unwrap_or_else(|_| PluginVersion::new(0, 1, 0));
31 let info = PluginInfo {
32 id: PluginId::new(id),
33 version,
34 name: String::new(),
35 description: String::new(),
36 author: PluginAuthor::new("Unknown"),
37 };
38
39 Self {
40 manifest: PluginManifest::new(info),
41 }
42 }
43
44 pub fn name(mut self, name: &str) -> Self {
46 self.manifest.info.name = name.to_string();
47 self
48 }
49
50 pub fn description(mut self, description: &str) -> Self {
52 self.manifest.info.description = description.to_string();
53 self
54 }
55
56 pub fn author(mut self, name: &str, email: &str) -> Self {
58 self.manifest.info.author = PluginAuthor::with_email(name, email);
59 self
60 }
61
62 pub fn author_name(mut self, name: &str) -> Self {
64 self.manifest.info.author = PluginAuthor::new(name);
65 self
66 }
67
68 pub fn capability(mut self, capability: &str) -> Self {
72 self.manifest.capabilities.push(capability.to_string());
73 self
74 }
75
76 pub fn capabilities(mut self, capabilities: &[&str]) -> Self {
78 for cap in capabilities {
79 self.manifest.capabilities.push(cap.to_string());
80 }
81 self
82 }
83
84 pub fn dependency(mut self, plugin_id: &str, version: &str) -> Self {
86 if let Ok(parsed_version) = PluginVersion::parse(version) {
87 self.manifest.dependencies.insert(PluginId::new(plugin_id), parsed_version);
88 }
89 self
90 }
91
92 pub fn build(self) -> PluginManifest {
94 self.manifest
95 }
96
97 pub fn build_and_save(self, path: &str) -> std::result::Result<PluginManifest, std::io::Error> {
99 let manifest = self.manifest;
100 let yaml = serde_yaml::to_string(&manifest)
101 .map_err(|e| std::io::Error::other(format!("YAML error: {}", e)))?;
102 std::fs::write(path, yaml)?;
103 Ok(manifest)
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_manifest_builder() {
113 let manifest = ManifestBuilder::new("test-plugin", "1.0.0")
114 .name("Test Plugin")
115 .description("A test plugin")
116 .author("Test Author", "test@example.com")
117 .capability("network")
118 .capability("filesystem.read")
119 .build();
120
121 assert_eq!(manifest.info.id, PluginId::new("test-plugin"));
122 assert_eq!(manifest.info.name, "Test Plugin");
123 assert_eq!(manifest.info.description, "A test plugin");
124 assert_eq!(manifest.capabilities.len(), 2);
125 assert!(manifest.capabilities.contains(&"network".to_string()));
126 assert!(manifest.capabilities.contains(&"filesystem.read".to_string()));
127 }
128
129 #[test]
130 fn test_manifest_with_dependencies() {
131 let manifest = ManifestBuilder::new("test-plugin", "2.0.0")
132 .name("Test Plugin")
133 .dependency("dep1", "1.0.0")
134 .dependency("dep2", "1.5.0")
135 .build();
136
137 assert_eq!(manifest.dependencies.len(), 2);
138 }
139
140 #[test]
141 fn test_manifest_save() {
142 use tempfile::NamedTempFile;
143
144 let manifest = ManifestBuilder::new("test-plugin", "1.0.0")
145 .name("Test Plugin")
146 .description("A test plugin")
147 .author("Test", "test@example.com")
148 .build();
149
150 let temp_file = NamedTempFile::new().unwrap();
151 let path = temp_file.path().to_str().unwrap();
152
153 let yaml = serde_yaml::to_string(&manifest).unwrap();
154 std::fs::write(path, yaml).unwrap();
155
156 let loaded = PluginManifest::from_file(path).unwrap();
157 assert_eq!(loaded.info.id, manifest.info.id);
158 assert_eq!(loaded.info.name, manifest.info.name);
159 }
160
161 #[test]
162 fn test_manifest_builder_new() {
163 let manifest = ManifestBuilder::new("my-plugin", "1.2.3").build();
164 assert_eq!(manifest.info.id.as_str(), "my-plugin");
165 assert_eq!(manifest.info.version.major, 1);
166 assert_eq!(manifest.info.version.minor, 2);
167 assert_eq!(manifest.info.version.patch, 3);
168 }
169
170 #[test]
171 fn test_manifest_builder_invalid_version() {
172 let manifest = ManifestBuilder::new("my-plugin", "invalid").build();
174 assert_eq!(manifest.info.version.major, 0);
175 assert_eq!(manifest.info.version.minor, 1);
176 assert_eq!(manifest.info.version.patch, 0);
177 }
178
179 #[test]
180 fn test_manifest_builder_default_author() {
181 let manifest = ManifestBuilder::new("my-plugin", "1.0.0").build();
182 assert_eq!(manifest.info.author.name, "Unknown");
183 }
184
185 #[test]
186 fn test_manifest_builder_author_name() {
187 let manifest = ManifestBuilder::new("test", "1.0.0").author_name("John Doe").build();
188 assert_eq!(manifest.info.author.name, "John Doe");
189 }
190
191 #[test]
192 fn test_manifest_builder_author_with_email() {
193 let manifest = ManifestBuilder::new("test", "1.0.0")
194 .author("Jane Doe", "jane@example.com")
195 .build();
196 assert_eq!(manifest.info.author.name, "Jane Doe");
197 assert_eq!(manifest.info.author.email, Some("jane@example.com".to_string()));
198 }
199
200 #[test]
201 fn test_manifest_builder_capabilities_batch() {
202 let manifest = ManifestBuilder::new("test", "1.0.0")
203 .capabilities(&["network", "filesystem.read", "filesystem.write"])
204 .build();
205 assert_eq!(manifest.capabilities.len(), 3);
206 assert!(manifest.capabilities.contains(&"network".to_string()));
207 assert!(manifest.capabilities.contains(&"filesystem.read".to_string()));
208 assert!(manifest.capabilities.contains(&"filesystem.write".to_string()));
209 }
210
211 #[test]
212 fn test_manifest_builder_empty_capabilities() {
213 let manifest = ManifestBuilder::new("test", "1.0.0").capabilities(&[]).build();
214 assert!(manifest.capabilities.is_empty());
215 }
216
217 #[test]
218 fn test_manifest_builder_dependency_invalid_version() {
219 let manifest = ManifestBuilder::new("test", "1.0.0")
221 .dependency("dep1", "invalid-version")
222 .build();
223 assert!(manifest.dependencies.is_empty());
224 }
225
226 #[test]
227 fn test_manifest_builder_chaining() {
228 let manifest = ManifestBuilder::new("chain-test", "1.0.0")
229 .name("Chain Test Plugin")
230 .description("Testing method chaining")
231 .author_name("Test Author")
232 .capability("network")
233 .capability("database")
234 .dependency("base-plugin", "2.0.0")
235 .build();
236
237 assert_eq!(manifest.info.name, "Chain Test Plugin");
238 assert_eq!(manifest.info.description, "Testing method chaining");
239 assert_eq!(manifest.info.author.name, "Test Author");
240 assert_eq!(manifest.capabilities.len(), 2);
241 assert_eq!(manifest.dependencies.len(), 1);
242 }
243
244 #[test]
245 fn test_manifest_builder_overwrite_name() {
246 let manifest = ManifestBuilder::new("test", "1.0.0")
247 .name("First Name")
248 .name("Second Name")
249 .build();
250 assert_eq!(manifest.info.name, "Second Name");
251 }
252
253 #[test]
254 fn test_manifest_builder_overwrite_description() {
255 let manifest = ManifestBuilder::new("test", "1.0.0")
256 .description("First description")
257 .description("Second description")
258 .build();
259 assert_eq!(manifest.info.description, "Second description");
260 }
261
262 #[test]
263 fn test_manifest_builder_overwrite_author() {
264 let manifest = ManifestBuilder::new("test", "1.0.0")
265 .author("First Author", "first@example.com")
266 .author("Second Author", "second@example.com")
267 .build();
268 assert_eq!(manifest.info.author.name, "Second Author");
269 assert_eq!(manifest.info.author.email, Some("second@example.com".to_string()));
270 }
271
272 #[test]
273 fn test_build_and_save() {
274 use tempfile::NamedTempFile;
275
276 let temp_file = NamedTempFile::new().unwrap();
277 let path = temp_file.path().to_str().unwrap();
278
279 let result = ManifestBuilder::new("test-plugin", "1.0.0")
280 .name("Test Plugin")
281 .description("A test plugin")
282 .author("Test", "test@example.com")
283 .build_and_save(path);
284
285 assert!(result.is_ok());
286 let manifest = result.unwrap();
287 assert_eq!(manifest.info.name, "Test Plugin");
288
289 let content = std::fs::read_to_string(path).unwrap();
291 assert!(content.contains("test-plugin"));
292 assert!(content.contains("Test Plugin"));
293 }
294
295 #[test]
296 fn test_build_and_save_invalid_path() {
297 let result =
298 ManifestBuilder::new("test", "1.0.0").build_and_save("/nonexistent/path/manifest.yaml");
299 assert!(result.is_err());
300 }
301
302 #[test]
303 fn test_manifest_builder_empty_strings() {
304 let manifest = ManifestBuilder::new("", "").name("").description("").build();
305 assert_eq!(manifest.info.id.as_str(), "");
306 assert_eq!(manifest.info.name, "");
307 assert_eq!(manifest.info.description, "");
308 }
309
310 #[test]
311 fn test_manifest_builder_special_characters() {
312 let manifest = ManifestBuilder::new("plugin-with-dashes", "1.0.0")
313 .name("Plugin with Special Characters: !@#$%")
314 .description("Description with\nnewlines and\ttabs")
315 .build();
316 assert_eq!(manifest.info.id.as_str(), "plugin-with-dashes");
317 assert!(manifest.info.name.contains("Special Characters"));
318 assert!(manifest.info.description.contains("\n"));
319 }
320}