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