1use crate::{SpecArg, SpecCommand, SpecFlag};
34
35#[derive(Debug, Default, Clone)]
37pub struct SpecFlagBuilder {
38 inner: SpecFlag,
39}
40
41impl SpecFlagBuilder {
42 pub fn new() -> Self {
44 Self::default()
45 }
46
47 pub fn name(mut self, name: impl Into<String>) -> Self {
49 self.inner.name = name.into();
50 self
51 }
52
53 pub fn short(mut self, c: char) -> Self {
55 self.inner.short.push(c);
56 self
57 }
58
59 pub fn shorts(mut self, chars: impl IntoIterator<Item = char>) -> Self {
61 self.inner.short.extend(chars);
62 self
63 }
64
65 pub fn long(mut self, name: impl Into<String>) -> Self {
67 self.inner.long.push(name.into());
68 self
69 }
70
71 pub fn longs<I, S>(mut self, names: I) -> Self
73 where
74 I: IntoIterator<Item = S>,
75 S: Into<String>,
76 {
77 self.inner.long.extend(names.into_iter().map(Into::into));
78 self
79 }
80
81 pub fn default_value(mut self, value: impl Into<String>) -> Self {
83 self.inner.default.push(value.into());
84 self.inner.required = false;
85 self
86 }
87
88 pub fn default_values<I, S>(mut self, values: I) -> Self
90 where
91 I: IntoIterator<Item = S>,
92 S: Into<String>,
93 {
94 self.inner
95 .default
96 .extend(values.into_iter().map(Into::into));
97 if !self.inner.default.is_empty() {
98 self.inner.required = false;
99 }
100 self
101 }
102
103 pub fn help(mut self, text: impl Into<String>) -> Self {
105 self.inner.help = Some(text.into());
106 self
107 }
108
109 pub fn help_long(mut self, text: impl Into<String>) -> Self {
111 self.inner.help_long = Some(text.into());
112 self
113 }
114
115 pub fn help_md(mut self, text: impl Into<String>) -> Self {
117 self.inner.help_md = Some(text.into());
118 self
119 }
120
121 pub fn var(mut self, is_var: bool) -> Self {
123 self.inner.var = is_var;
124 self
125 }
126
127 pub fn var_min(mut self, min: usize) -> Self {
129 self.inner.var_min = Some(min);
130 self
131 }
132
133 pub fn var_max(mut self, max: usize) -> Self {
135 self.inner.var_max = Some(max);
136 self
137 }
138
139 pub fn required(mut self, is_required: bool) -> Self {
141 self.inner.required = is_required;
142 self
143 }
144
145 pub fn global(mut self, is_global: bool) -> Self {
147 self.inner.global = is_global;
148 self
149 }
150
151 pub fn hide(mut self, is_hidden: bool) -> Self {
153 self.inner.hide = is_hidden;
154 self
155 }
156
157 pub fn count(mut self, is_count: bool) -> Self {
159 self.inner.count = is_count;
160 self
161 }
162
163 pub fn arg(mut self, arg: SpecArg) -> Self {
165 self.inner.arg = Some(arg);
166 self
167 }
168
169 pub fn negate(mut self, negate: impl Into<String>) -> Self {
171 self.inner.negate = Some(negate.into());
172 self
173 }
174
175 pub fn env(mut self, env: impl Into<String>) -> Self {
177 self.inner.env = Some(env.into());
178 self
179 }
180
181 pub fn deprecated(mut self, msg: impl Into<String>) -> Self {
183 self.inner.deprecated = Some(msg.into());
184 self
185 }
186
187 pub fn build(mut self) -> SpecFlag {
189 self.inner.usage = self.inner.usage();
190 if self.inner.name.is_empty() {
191 if let Some(long) = self.inner.long.first() {
193 self.inner.name = long.clone();
194 } else if let Some(short) = self.inner.short.first() {
195 self.inner.name = short.to_string();
196 }
197 }
198 self.inner
199 }
200}
201
202#[derive(Debug, Default, Clone)]
204pub struct SpecArgBuilder {
205 inner: SpecArg,
206}
207
208impl SpecArgBuilder {
209 pub fn new() -> Self {
211 Self::default()
212 }
213
214 pub fn name(mut self, name: impl Into<String>) -> Self {
216 self.inner.name = name.into();
217 self
218 }
219
220 pub fn default_value(mut self, value: impl Into<String>) -> Self {
222 self.inner.default.push(value.into());
223 self.inner.required = false;
224 self
225 }
226
227 pub fn default_values<I, S>(mut self, values: I) -> Self
229 where
230 I: IntoIterator<Item = S>,
231 S: Into<String>,
232 {
233 self.inner
234 .default
235 .extend(values.into_iter().map(Into::into));
236 if !self.inner.default.is_empty() {
237 self.inner.required = false;
238 }
239 self
240 }
241
242 pub fn help(mut self, text: impl Into<String>) -> Self {
244 self.inner.help = Some(text.into());
245 self
246 }
247
248 pub fn help_long(mut self, text: impl Into<String>) -> Self {
250 self.inner.help_long = Some(text.into());
251 self
252 }
253
254 pub fn help_md(mut self, text: impl Into<String>) -> Self {
256 self.inner.help_md = Some(text.into());
257 self
258 }
259
260 pub fn var(mut self, is_var: bool) -> Self {
262 self.inner.var = is_var;
263 self
264 }
265
266 pub fn var_min(mut self, min: usize) -> Self {
268 self.inner.var_min = Some(min);
269 self
270 }
271
272 pub fn var_max(mut self, max: usize) -> Self {
274 self.inner.var_max = Some(max);
275 self
276 }
277
278 pub fn required(mut self, is_required: bool) -> Self {
280 self.inner.required = is_required;
281 self
282 }
283
284 pub fn hide(mut self, is_hidden: bool) -> Self {
286 self.inner.hide = is_hidden;
287 self
288 }
289
290 pub fn env(mut self, env: impl Into<String>) -> Self {
292 self.inner.env = Some(env.into());
293 self
294 }
295
296 pub fn build(mut self) -> SpecArg {
298 self.inner.usage = self.inner.usage();
299 self.inner
300 }
301}
302
303#[derive(Debug, Default, Clone)]
305pub struct SpecCommandBuilder {
306 inner: SpecCommand,
307}
308
309impl SpecCommandBuilder {
310 pub fn new() -> Self {
312 Self::default()
313 }
314
315 pub fn name(mut self, name: impl Into<String>) -> Self {
317 self.inner.name = name.into();
318 self
319 }
320
321 pub fn alias(mut self, alias: impl Into<String>) -> Self {
323 self.inner.aliases.push(alias.into());
324 self
325 }
326
327 pub fn aliases<I, S>(mut self, aliases: I) -> Self
329 where
330 I: IntoIterator<Item = S>,
331 S: Into<String>,
332 {
333 self.inner
334 .aliases
335 .extend(aliases.into_iter().map(Into::into));
336 self
337 }
338
339 pub fn hidden_alias(mut self, alias: impl Into<String>) -> Self {
341 self.inner.hidden_aliases.push(alias.into());
342 self
343 }
344
345 pub fn hidden_aliases<I, S>(mut self, aliases: I) -> Self
347 where
348 I: IntoIterator<Item = S>,
349 S: Into<String>,
350 {
351 self.inner
352 .hidden_aliases
353 .extend(aliases.into_iter().map(Into::into));
354 self
355 }
356
357 pub fn flag(mut self, flag: SpecFlag) -> Self {
359 self.inner.flags.push(flag);
360 self
361 }
362
363 pub fn flags(mut self, flags: impl IntoIterator<Item = SpecFlag>) -> Self {
365 self.inner.flags.extend(flags);
366 self
367 }
368
369 pub fn arg(mut self, arg: SpecArg) -> Self {
371 self.inner.args.push(arg);
372 self
373 }
374
375 pub fn args(mut self, args: impl IntoIterator<Item = SpecArg>) -> Self {
377 self.inner.args.extend(args);
378 self
379 }
380
381 pub fn help(mut self, text: impl Into<String>) -> Self {
383 self.inner.help = Some(text.into());
384 self
385 }
386
387 pub fn help_long(mut self, text: impl Into<String>) -> Self {
389 self.inner.help_long = Some(text.into());
390 self
391 }
392
393 pub fn help_md(mut self, text: impl Into<String>) -> Self {
395 self.inner.help_md = Some(text.into());
396 self
397 }
398
399 pub fn hide(mut self, is_hidden: bool) -> Self {
401 self.inner.hide = is_hidden;
402 self
403 }
404
405 pub fn subcommand_required(mut self, required: bool) -> Self {
407 self.inner.subcommand_required = required;
408 self
409 }
410
411 pub fn deprecated(mut self, msg: impl Into<String>) -> Self {
413 self.inner.deprecated = Some(msg.into());
414 self
415 }
416
417 pub fn restart_token(mut self, token: impl Into<String>) -> Self {
420 self.inner.restart_token = Some(token.into());
421 self
422 }
423
424 pub fn build(mut self) -> SpecCommand {
426 self.inner.usage = self.inner.usage();
427 self.inner
428 }
429}
430
431#[cfg(test)]
432mod tests {
433 use super::*;
434
435 #[test]
436 fn test_flag_builder_basic() {
437 let flag = SpecFlagBuilder::new()
438 .name("verbose")
439 .short('v')
440 .long("verbose")
441 .help("Enable verbose output")
442 .build();
443
444 assert_eq!(flag.name, "verbose");
445 assert_eq!(flag.short, vec!['v']);
446 assert_eq!(flag.long, vec!["verbose".to_string()]);
447 assert_eq!(flag.help, Some("Enable verbose output".to_string()));
448 }
449
450 #[test]
451 fn test_flag_builder_multiple_values() {
452 let flag = SpecFlagBuilder::new()
453 .shorts(['v', 'V'])
454 .longs(["verbose", "loud"])
455 .default_values(["info", "warn"])
456 .build();
457
458 assert_eq!(flag.short, vec!['v', 'V']);
459 assert_eq!(flag.long, vec!["verbose".to_string(), "loud".to_string()]);
460 assert_eq!(flag.default, vec!["info".to_string(), "warn".to_string()]);
461 assert!(!flag.required); }
463
464 #[test]
465 fn test_flag_builder_variadic() {
466 let flag = SpecFlagBuilder::new()
467 .long("file")
468 .var(true)
469 .var_min(1)
470 .var_max(10)
471 .build();
472
473 assert!(flag.var);
474 assert_eq!(flag.var_min, Some(1));
475 assert_eq!(flag.var_max, Some(10));
476 }
477
478 #[test]
479 fn test_flag_builder_name_derivation() {
480 let flag = SpecFlagBuilder::new().short('v').long("verbose").build();
481
482 assert_eq!(flag.name, "verbose");
484
485 let flag2 = SpecFlagBuilder::new().short('v').build();
486
487 assert_eq!(flag2.name, "v");
489 }
490
491 #[test]
492 fn test_arg_builder_basic() {
493 let arg = SpecArgBuilder::new()
494 .name("file")
495 .help("Input file")
496 .required(true)
497 .build();
498
499 assert_eq!(arg.name, "file");
500 assert_eq!(arg.help, Some("Input file".to_string()));
501 assert!(arg.required);
502 }
503
504 #[test]
505 fn test_arg_builder_variadic() {
506 let arg = SpecArgBuilder::new()
507 .name("files")
508 .var(true)
509 .var_min(1)
510 .var_max(10)
511 .help("Input files")
512 .build();
513
514 assert_eq!(arg.name, "files");
515 assert!(arg.var);
516 assert_eq!(arg.var_min, Some(1));
517 assert_eq!(arg.var_max, Some(10));
518 }
519
520 #[test]
521 fn test_arg_builder_defaults() {
522 let arg = SpecArgBuilder::new()
523 .name("file")
524 .default_values(["a.txt", "b.txt"])
525 .build();
526
527 assert_eq!(arg.default, vec!["a.txt".to_string(), "b.txt".to_string()]);
528 assert!(!arg.required);
529 }
530
531 #[test]
532 fn test_command_builder_basic() {
533 let cmd = SpecCommandBuilder::new()
534 .name("install")
535 .help("Install packages")
536 .build();
537
538 assert_eq!(cmd.name, "install");
539 assert_eq!(cmd.help, Some("Install packages".to_string()));
540 }
541
542 #[test]
543 fn test_command_builder_aliases() {
544 let cmd = SpecCommandBuilder::new()
545 .name("install")
546 .alias("i")
547 .aliases(["add", "get"])
548 .hidden_aliases(["inst"])
549 .build();
550
551 assert_eq!(
552 cmd.aliases,
553 vec!["i".to_string(), "add".to_string(), "get".to_string()]
554 );
555 assert_eq!(cmd.hidden_aliases, vec!["inst".to_string()]);
556 }
557
558 #[test]
559 fn test_command_builder_with_flags_and_args() {
560 let flag = SpecFlagBuilder::new().short('f').long("force").build();
561
562 let arg = SpecArgBuilder::new().name("package").required(true).build();
563
564 let cmd = SpecCommandBuilder::new()
565 .name("install")
566 .flag(flag)
567 .arg(arg)
568 .build();
569
570 assert_eq!(cmd.flags.len(), 1);
571 assert_eq!(cmd.flags[0].name, "force");
572 assert_eq!(cmd.args.len(), 1);
573 assert_eq!(cmd.args[0].name, "package");
574 }
575}