dynamic_cli/config/
mod.rs1pub mod loader;
86pub mod schema;
87pub mod validator;
88
89#[allow(unused_imports)]
91pub use schema::{
92 ArgumentDefinition, ArgumentType, CommandDefinition, CommandsConfig, Metadata,
93 OptionDefinition, ValidationRule,
94};
95
96#[allow(unused_imports)]
97pub use loader::{load_config, load_json, load_yaml};
98
99#[allow(unused_imports)]
100pub use validator::{validate_argument_types, validate_command, validate_config};
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
108 fn test_integration_load_and_validate() {
109 let yaml = r#"
110metadata:
111 version: "1.0.0"
112 prompt: "test"
113 prompt_suffix: " > "
114
115commands:
116 - name: process
117 aliases: [proc, p]
118 description: "Process data files"
119 required: true
120 arguments:
121 - name: input
122 arg_type: path
123 required: true
124 description: "Input file path"
125 validation:
126 - must_exist: true
127 - extensions: [csv, tsv]
128 - name: output
129 arg_type: path
130 required: false
131 description: "Output file path"
132 validation: []
133 options:
134 - name: verbose
135 short: v
136 long: verbose
137 option_type: bool
138 required: false
139 description: "Verbose output"
140 choices: []
141 - name: format
142 short: f
143 long: format
144 option_type: string
145 required: false
146 default: "json"
147 description: "Output format"
148 choices: [json, xml, csv]
149 implementation: "process_handler"
150
151 - name: help
152 aliases: [h]
153 description: "Show help information"
154 required: false
155 arguments: []
156 options: []
157 implementation: "help_handler"
158
159global_options:
160 - name: config
161 short: c
162 long: config
163 option_type: path
164 required: false
165 description: "Configuration file"
166 choices: []
167 "#;
168
169 let config = load_yaml(yaml).unwrap();
171
172 validate_config(&config).unwrap();
174
175 assert_eq!(config.metadata.version, "1.0.0");
177 assert_eq!(config.commands.len(), 2);
178 assert_eq!(config.commands[0].name, "process");
179 assert_eq!(config.commands[0].arguments.len(), 2);
180 assert_eq!(config.commands[0].options.len(), 2);
181 assert_eq!(config.global_options.len(), 1);
182 }
183
184 #[test]
186 fn test_integration_invalid_config() {
187 let yaml = r#"
188metadata:
189 version: "1.0.0"
190 prompt: "test"
191
192commands:
193 - name: cmd1
194 aliases: []
195 description: "Command 1"
196 required: false
197 arguments: []
198 options: []
199 implementation: "handler1"
200 - name: cmd1
201 aliases: []
202 description: "Command 2 with duplicate name"
203 required: false
204 arguments: []
205 options: []
206 implementation: "handler2"
207
208global_options: []
209 "#;
210
211 let config = load_yaml(yaml).unwrap();
212 let result = validate_config(&config);
213
214 assert!(result.is_err());
216 }
217
218 #[test]
220 fn test_integration_json_format() {
221 let json = r#"
222{
223 "metadata": {
224 "version": "2.0.0",
225 "prompt": "myapp",
226 "prompt_suffix": " $ "
227 },
228 "commands": [
229 {
230 "name": "test",
231 "aliases": [],
232 "description": "Test command",
233 "required": false,
234 "arguments": [
235 {
236 "name": "value",
237 "arg_type": "integer",
238 "required": true,
239 "description": "A test value",
240 "validation": [
241 {
242 "min": 0.0,
243 "max": 100.0
244 }
245 ]
246 }
247 ],
248 "options": [],
249 "implementation": "test_handler"
250 }
251 ],
252 "global_options": []
253}
254 "#;
255
256 let config = load_json(json).unwrap();
257 validate_config(&config).unwrap();
258
259 assert_eq!(config.metadata.version, "2.0.0");
260 assert_eq!(
261 config.commands[0].arguments[0].arg_type,
262 ArgumentType::Integer
263 );
264 }
265
266 #[test]
268 fn test_reexports() {
269 let _config = CommandsConfig::minimal();
272 let _arg_type = ArgumentType::String;
273
274 }
276
277 #[test]
279 fn test_integration_complex_validation() {
280 let yaml = r#"
281metadata:
282 version: "1.0.0"
283 prompt: "test"
284
285commands:
286 - name: analyze
287 aliases: []
288 description: "Analyze data"
289 required: false
290 arguments:
291 - name: data_file
292 arg_type: path
293 required: true
294 description: "Data file"
295 validation:
296 - must_exist: true
297 - extensions: [dat, bin]
298 - name: threshold
299 arg_type: float
300 required: true
301 description: "Analysis threshold"
302 validation:
303 - min: 0.0
304 max: 1.0
305 options:
306 - name: iterations
307 short: i
308 long: iterations
309 option_type: integer
310 required: false
311 default: "100"
312 description: "Number of iterations"
313 choices: []
314 implementation: "analyze_handler"
315
316global_options: []
317 "#;
318
319 let config = load_yaml(yaml).unwrap();
320 let result = validate_config(&config);
321
322 assert!(result.is_ok());
323
324 let cmd = &config.commands[0];
326 assert_eq!(cmd.arguments[0].validation.len(), 2);
327 assert_eq!(cmd.arguments[1].validation.len(), 1);
328 }
329
330 #[test]
332 fn test_integration_error_messages() {
333 let bad_yaml = "metadata:\n version: [unclosed";
337 let result = load_yaml(bad_yaml);
338 assert!(result.is_err());
339
340 let yaml_type_error = r#"
342metadata:
343 version: "1.0.0"
344 prompt: "test"
345commands:
346 - name: cmd
347 aliases: []
348 description: "Test"
349 required: false
350 arguments:
351 - name: count
352 arg_type: integer
353 required: true
354 description: "Count"
355 validation:
356 - must_exist: true
357 options: []
358 implementation: "handler"
359global_options: []
360 "#;
361
362 let config = load_yaml(yaml_type_error).unwrap();
363 let result = validate_config(&config);
364 assert!(result.is_err());
365 }
366}