1use crate::{spec::arg::SpecDoubleDashChoices, 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 double_dash(mut self, behavior: SpecDoubleDashChoices) -> Self {
298 self.inner.double_dash = behavior;
299 self
300 }
301
302 pub fn build(mut self) -> SpecArg {
304 self.inner.usage = self.inner.usage();
305 self.inner
306 }
307}
308
309#[derive(Debug, Default, Clone)]
311pub struct SpecCommandBuilder {
312 inner: SpecCommand,
313}
314
315impl SpecCommandBuilder {
316 pub fn new() -> Self {
318 Self::default()
319 }
320
321 pub fn name(mut self, name: impl Into<String>) -> Self {
323 self.inner.name = name.into();
324 self
325 }
326
327 pub fn alias(mut self, alias: impl Into<String>) -> Self {
329 self.inner.aliases.push(alias.into());
330 self
331 }
332
333 pub fn aliases<I, S>(mut self, aliases: I) -> Self
335 where
336 I: IntoIterator<Item = S>,
337 S: Into<String>,
338 {
339 self.inner
340 .aliases
341 .extend(aliases.into_iter().map(Into::into));
342 self
343 }
344
345 pub fn hidden_alias(mut self, alias: impl Into<String>) -> Self {
347 self.inner.hidden_aliases.push(alias.into());
348 self
349 }
350
351 pub fn hidden_aliases<I, S>(mut self, aliases: I) -> Self
353 where
354 I: IntoIterator<Item = S>,
355 S: Into<String>,
356 {
357 self.inner
358 .hidden_aliases
359 .extend(aliases.into_iter().map(Into::into));
360 self
361 }
362
363 pub fn flag(mut self, flag: SpecFlag) -> Self {
365 self.inner.flags.push(flag);
366 self
367 }
368
369 pub fn flags(mut self, flags: impl IntoIterator<Item = SpecFlag>) -> Self {
371 self.inner.flags.extend(flags);
372 self
373 }
374
375 pub fn arg(mut self, arg: SpecArg) -> Self {
377 self.inner.args.push(arg);
378 self
379 }
380
381 pub fn args(mut self, args: impl IntoIterator<Item = SpecArg>) -> Self {
383 self.inner.args.extend(args);
384 self
385 }
386
387 pub fn help(mut self, text: impl Into<String>) -> Self {
389 self.inner.help = Some(text.into());
390 self
391 }
392
393 pub fn help_long(mut self, text: impl Into<String>) -> Self {
395 self.inner.help_long = Some(text.into());
396 self
397 }
398
399 pub fn help_md(mut self, text: impl Into<String>) -> Self {
401 self.inner.help_md = Some(text.into());
402 self
403 }
404
405 pub fn hide(mut self, is_hidden: bool) -> Self {
407 self.inner.hide = is_hidden;
408 self
409 }
410
411 pub fn subcommand_required(mut self, required: bool) -> Self {
413 self.inner.subcommand_required = required;
414 self
415 }
416
417 pub fn deprecated(mut self, msg: impl Into<String>) -> Self {
419 self.inner.deprecated = Some(msg.into());
420 self
421 }
422
423 pub fn restart_token(mut self, token: impl Into<String>) -> Self {
426 self.inner.restart_token = Some(token.into());
427 self
428 }
429
430 pub fn build(mut self) -> SpecCommand {
432 self.inner.usage = self.inner.usage();
433 self.inner
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440
441 #[test]
442 fn test_flag_builder_basic() {
443 let flag = SpecFlagBuilder::new()
444 .name("verbose")
445 .short('v')
446 .long("verbose")
447 .help("Enable verbose output")
448 .build();
449
450 assert_eq!(flag.name, "verbose");
451 assert_eq!(flag.short, vec!['v']);
452 assert_eq!(flag.long, vec!["verbose".to_string()]);
453 assert_eq!(flag.help, Some("Enable verbose output".to_string()));
454 }
455
456 #[test]
457 fn test_flag_builder_multiple_values() {
458 let flag = SpecFlagBuilder::new()
459 .shorts(['v', 'V'])
460 .longs(["verbose", "loud"])
461 .default_values(["info", "warn"])
462 .build();
463
464 assert_eq!(flag.short, vec!['v', 'V']);
465 assert_eq!(flag.long, vec!["verbose".to_string(), "loud".to_string()]);
466 assert_eq!(flag.default, vec!["info".to_string(), "warn".to_string()]);
467 assert!(!flag.required); }
469
470 #[test]
471 fn test_flag_builder_variadic() {
472 let flag = SpecFlagBuilder::new()
473 .long("file")
474 .var(true)
475 .var_min(1)
476 .var_max(10)
477 .build();
478
479 assert!(flag.var);
480 assert_eq!(flag.var_min, Some(1));
481 assert_eq!(flag.var_max, Some(10));
482 }
483
484 #[test]
485 fn test_flag_builder_name_derivation() {
486 let flag = SpecFlagBuilder::new().short('v').long("verbose").build();
487
488 assert_eq!(flag.name, "verbose");
490
491 let flag2 = SpecFlagBuilder::new().short('v').build();
492
493 assert_eq!(flag2.name, "v");
495 }
496
497 #[test]
498 fn test_arg_builder_basic() {
499 let arg = SpecArgBuilder::new()
500 .name("file")
501 .help("Input file")
502 .required(true)
503 .build();
504
505 assert_eq!(arg.name, "file");
506 assert_eq!(arg.help, Some("Input file".to_string()));
507 assert!(arg.required);
508 }
509
510 #[test]
511 fn test_arg_builder_variadic() {
512 let arg = SpecArgBuilder::new()
513 .name("files")
514 .var(true)
515 .var_min(1)
516 .var_max(10)
517 .help("Input files")
518 .build();
519
520 assert_eq!(arg.name, "files");
521 assert!(arg.var);
522 assert_eq!(arg.var_min, Some(1));
523 assert_eq!(arg.var_max, Some(10));
524 }
525
526 #[test]
527 fn test_arg_builder_defaults() {
528 let arg = SpecArgBuilder::new()
529 .name("file")
530 .default_values(["a.txt", "b.txt"])
531 .build();
532
533 assert_eq!(arg.default, vec!["a.txt".to_string(), "b.txt".to_string()]);
534 assert!(!arg.required);
535 }
536
537 #[test]
538 fn test_command_builder_basic() {
539 let cmd = SpecCommandBuilder::new()
540 .name("install")
541 .help("Install packages")
542 .build();
543
544 assert_eq!(cmd.name, "install");
545 assert_eq!(cmd.help, Some("Install packages".to_string()));
546 }
547
548 #[test]
549 fn test_command_builder_aliases() {
550 let cmd = SpecCommandBuilder::new()
551 .name("install")
552 .alias("i")
553 .aliases(["add", "get"])
554 .hidden_aliases(["inst"])
555 .build();
556
557 assert_eq!(
558 cmd.aliases,
559 vec!["i".to_string(), "add".to_string(), "get".to_string()]
560 );
561 assert_eq!(cmd.hidden_aliases, vec!["inst".to_string()]);
562 }
563
564 #[test]
565 fn test_command_builder_with_flags_and_args() {
566 let flag = SpecFlagBuilder::new().short('f').long("force").build();
567
568 let arg = SpecArgBuilder::new().name("package").required(true).build();
569
570 let cmd = SpecCommandBuilder::new()
571 .name("install")
572 .flag(flag)
573 .arg(arg)
574 .build();
575
576 assert_eq!(cmd.flags.len(), 1);
577 assert_eq!(cmd.flags[0].name, "force");
578 assert_eq!(cmd.args.len(), 1);
579 assert_eq!(cmd.args[0].name, "package");
580 }
581}