1use log::error;
4use std::any::{type_name, Any, TypeId};
5use std::collections::HashMap;
6use std::error::Error;
7use std::marker::PhantomData;
8use std::ops::Deref;
9use std::str::FromStr;
10
11use crate::{ok, Task};
12
13pub struct OptionRequest<T> {
15 flag: String,
16 values: Option<Vec<T>>,
17}
18
19impl<T> OptionRequest<T> {
20 pub fn flag(&self) -> &str {
22 &self.flag
23 }
24
25 pub fn values(&self) -> Option<&[T]> {
29 self.values.as_ref().map(|vals| &vals[..])
30 }
31}
32
33pub struct OptionDeclaration {
38 flag: String,
39 help: String,
40 takes_value: bool,
41 allow_multiple_values: bool,
42 optional: bool,
43 flag_type: TypeId,
44 parse_value: Option<Box<dyn Fn(&str) -> Result<Box<dyn Any>, Box<dyn Error + Send + Sync>>>>,
45 verify_value: Option<Box<dyn Fn(&str) -> Result<(), Box<dyn Error + Send + Sync>>>>,
46}
47
48impl OptionDeclaration {
49 pub fn flag(&self) -> &str {
50 &self.flag
51 }
52 pub fn help(&self) -> &str {
53 &self.help
54 }
55 pub fn takes_value(&self) -> bool {
56 self.takes_value
57 }
58
59 pub fn is_flag(&self) -> bool {
60 !self.takes_value
61 }
62
63 pub fn allow_multiple_values(&self) -> bool {
64 self.allow_multiple_values
65 }
66 pub fn optional(&self) -> bool {
67 self.optional
68 }
69}
70
71pub struct OptionDeclarations {
72 task_type: String,
73 declarations: HashMap<String, OptionDeclaration>,
74}
75
76impl OptionDeclarations {
77 pub fn new<T: Task, I: IntoIterator<Item = OptionDeclaration>>(options: I) -> Self {
78 Self {
79 task_type: type_name::<T>().to_string(),
80 declarations: options
81 .into_iter()
82 .map(|opt: OptionDeclaration| (opt.flag.to_string(), opt))
83 .collect(),
84 }
85 }
86
87 fn new_weak(&self, map: HashMap<String, Vec<String>>) -> WeakOptionsDecoder {
88 WeakOptionsDecoder {
89 option_dec_string: self.task_type.clone(),
90 fed_options: map,
91 }
92 }
93
94 pub fn slurper(&self) -> OptionsSlurper {
95 OptionsSlurper::new(self)
96 }
97}
98
99impl Deref for OptionDeclarations {
100 type Target = HashMap<String, OptionDeclaration>;
101
102 fn deref(&self) -> &Self::Target {
103 &self.declarations
104 }
105}
106
107pub struct OptionDeclarationBuilder<T> {
109 flag: String,
110 help: Option<String>,
111 takes_value: bool,
112 allow_multiple_values: bool,
113 optional: bool,
114 parse_value: Option<Box<dyn Fn(&str) -> Result<Box<dyn Any>, Box<dyn Error + Send + Sync>>>>,
115 verify_value: Option<Box<dyn Fn(&str) -> Result<(), Box<dyn Error + Send + Sync>>>>,
116 _phantom: PhantomData<T>,
117}
118
119impl<T: 'static> OptionDeclarationBuilder<T> {
120 pub fn new(flag: &str) -> Self {
121 Self {
122 flag: flag.to_string(),
123 help: None,
124 takes_value: true,
125 allow_multiple_values: false,
126 optional: false,
127 parse_value: None,
128 verify_value: None,
129 _phantom: PhantomData,
130 }
131 }
132
133 pub fn help(mut self, help: impl AsRef<str>) -> Self {
134 self.help = Some(help.as_ref().to_string());
135 self
136 }
137 pub fn takes_value(mut self, takes_value: bool) -> Self {
138 self.takes_value = takes_value;
139 self
140 }
141 pub fn allow_multiple_values(mut self, allow_multiple_values: bool) -> Self {
142 if allow_multiple_values {
143 self.takes_value = true;
144 }
145 self.allow_multiple_values = allow_multiple_values;
146 self
147 }
148 pub fn optional(mut self, optional: bool) -> Self {
149 self.optional = optional;
150 self
151 }
152
153 pub fn value_parser<F, E>(mut self, func: F) -> Self
154 where
155 F: Fn(&str) -> Result<T, E>,
156 F: 'static,
157 E: 'static + Error + Send + Sync,
158 {
159 let boxed: Box<(dyn Fn(&str) -> Result<Box<dyn Any>, Box<dyn Error + Send + Sync>>)> =
160 Box::new(move |str| {
161 let res = (func)(str);
162 res.map(|t| Box::new(t) as Box<dyn Any>)
163 .map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
164 });
165 self.parse_value = Some(boxed);
166 self
167 }
168
169 pub fn build(self) -> OptionDeclaration {
170 OptionDeclaration {
171 flag: self.flag,
172 help: self.help.unwrap_or_default(),
173 takes_value: self.takes_value,
174 allow_multiple_values: self.allow_multiple_values,
175 optional: self.optional,
176 flag_type: TypeId::of::<T>(),
177 parse_value: (self.takes_value).then_some(()).map(|_| {
178 self.parse_value
179 .expect("Value parser required for flags that take a value")
180 }),
181 verify_value: self.verify_value,
182 }
183 }
184}
185
186impl<T: FromStr + 'static> OptionDeclarationBuilder<T>
187where
188 <T as FromStr>::Err: Error + Send + Sync,
189{
190 pub fn use_from_str(self) -> Self {
192 self.value_parser(T::from_str)
193 }
194}
195
196impl OptionDeclarationBuilder<bool> {
197 pub fn flag(flag: &str) -> Self {
198 Self::new(flag).takes_value(false).optional(true)
199 }
200}
201
202pub struct OptionsSlurper<'dec> {
204 decs: &'dec OptionDeclarations,
205}
206
207fn flag_value_entry() -> Vec<String> {
208 vec![String::new()]
209}
210
211impl<'dec> OptionsSlurper<'dec> {
212 pub fn new(decs: &'dec OptionDeclarations) -> Self {
213 Self { decs }
214 }
215
216 pub fn slurp<S: AsRef<str>>(
219 self,
220 args_slice: &[S],
221 ) -> Result<(WeakOptionsDecoder, usize), OptionsSlurperError> {
222 let mut slurped_args: HashMap<String, Vec<String>> = HashMap::new();
223 let mut count = 0;
224
225 let mut prev_arg: Option<&OptionDeclaration> = None;
226
227 while let Some(arg) = args_slice.get(count).map(<S as AsRef<str>>::as_ref) {
228 if let Some(option) = arg.strip_prefix("--") {
229 if let Some(prev) = prev_arg {
232 return Err(OptionsSlurperError::OptionTakesValueButNoneProvided(
234 prev.flag().to_string(),
235 ));
236 }
237
238 if let Some(declaration) = self.decs.get(option) {
239 if declaration.takes_value() {
240 prev_arg = Some(declaration);
241 } else {
242 slurped_args
243 .entry(option.to_string())
244 .or_default()
245 .push(String::new());
246 }
247 } else {
248 return Err(OptionsSlurperError::UnknownOption(option.to_string()));
249 }
250 } else {
251 match prev_arg {
253 Some(v) => {
254 let value = arg;
255 let option = v.flag().to_string();
256 if v.takes_value() {
257 slurped_args
258 .entry(option)
259 .or_default()
260 .push(value.to_string());
261 prev_arg = None;
262 } else {
263 break;
264 }
265 }
266 None => {
267 break;
269 }
270 }
271 }
272
273 count += 1;
274 }
275
276 if let Some(prev) = prev_arg {
277 Err(OptionsSlurperError::OptionTakesValueButNoneProvided(
278 prev.flag().to_string(),
279 ))
280 } else {
281 ok!(self.decs.new_weak(slurped_args), count)
282 }
283 }
284}
285
286#[derive(Debug, thiserror::Error)]
288pub enum OptionsSlurperError {
289 #[error("No known option {0}")]
290 UnknownOption(String),
291 #[error("Given option {0} does not take a value")]
292 OptionDoesNotTakeValue(String),
293 #[error("Given option {0} takes a value but none provided")]
294 OptionTakesValueButNoneProvided(String),
295}
296
297#[derive(Clone, Debug)]
299pub struct WeakOptionsDecoder {
300 option_dec_string: String,
301 fed_options: HashMap<String, Vec<String>>,
302}
303
304impl WeakOptionsDecoder {
305 pub fn upgrade(self, decs: &OptionDeclarations) -> Result<OptionsDecoder, OptionsDecoderError> {
307 if decs.task_type != self.option_dec_string {
308 Err(OptionsDecoderError::OptionsMismatch)
309 } else {
310 Ok(OptionsDecoder {
311 decs,
312 fed_options: self.fed_options,
313 })
314 }
315 }
316}
317
318pub struct OptionsDecoder<'dec> {
322 decs: &'dec OptionDeclarations,
323 fed_options: HashMap<String, Vec<String>>,
324}
325
326pub type DecoderResult<T> = Result<T, OptionsDecoderError>;
327
328impl<'dec> OptionsDecoder<'dec> {
329 fn get_option_dec(&self, flag: &str) -> DecoderResult<&OptionDeclaration> {
331 let dec = self
332 .decs
333 .get(flag)
334 .ok_or(OptionsDecoderError::InvalidOption(flag.to_string()))?;
335
336 if dec.is_flag()
337 && self
338 .fed_options
339 .get(flag)
340 .map(|v| v != &flag_value_entry())
341 .unwrap_or(false)
342 {
343 error!("flag has bad value: {:?}", self.fed_options.get(flag));
344 return Err(OptionsDecoderError::OptionDoesNotTakeValue(
345 flag.to_string(),
346 ));
347 }
348
349 if dec.takes_value() {
350 match self.fed_options.get(flag) {
351 None => {
352 if !dec.optional() {
353 return Err(OptionsDecoderError::OptionNotOptional(flag.to_string()));
354 }
355 }
356 Some(v) => {
357 if v.len() > 1 && !dec.allow_multiple_values() {
358 return Err(OptionsDecoderError::OptionDoesNotTakeMultipleValue(
359 flag.to_string(),
360 ));
361 }
362 }
363 }
364 }
365
366 Ok(dec)
367 }
368
369 pub fn flag_present(&self, flag: &str) -> DecoderResult<bool> {
371 let dec = self.get_option_dec(flag)?;
372 if dec.is_flag() {
373 if let Some(entry) = self.fed_options.get(flag) {
374 assert_eq!(entry, &flag_value_entry(), "flag improperly set in options");
375 ok!(true)
376 } else {
377 ok!(false)
378 }
379 } else {
380 Err(OptionsDecoderError::OptionNotFlag(flag.to_string()))
381 }
382 }
383
384 pub fn get_value<T: 'static>(&self, flag: &str) -> DecoderResult<Option<T>> {
389 let declaration = self.get_option_dec(flag)?;
390 if declaration.is_flag() {
391 return Err(OptionsDecoderError::OptionDoesNotTakeValue(
392 flag.to_string(),
393 ));
394 }
395
396 if declaration.allow_multiple_values() {
397 return Err(OptionsDecoderError::OptionTakesMultipleValue(
398 flag.to_string(),
399 ));
400 }
401
402 if declaration.flag_type != TypeId::of::<T>() {
403 return Err(OptionsDecoderError::incorrect_type::<T>(flag));
404 }
405
406 if let Some(values) = self.fed_options.get(flag) {
407 let value = values.first().unwrap();
408 let parse_function = declaration.parse_value.as_ref().unwrap();
409 let parsed: Box<dyn Any> = parse_function(value)?;
410 Ok(Some(*parsed.downcast::<T>().unwrap()))
411 } else if declaration.optional {
412 Ok(None)
413 } else {
414 Err(OptionsDecoderError::OptionNotOptional(flag.to_string()))
415 }
416 }
417
418 pub fn get_values<T: 'static>(&self, flag: &str) -> DecoderResult<Option<Vec<T>>> {
423 let declaration = self.get_option_dec(flag)?;
424 if declaration.is_flag() {
425 return Err(OptionsDecoderError::OptionDoesNotTakeValue(
426 flag.to_string(),
427 ));
428 }
429
430 if !declaration.allow_multiple_values() {
431 return Err(OptionsDecoderError::OptionDoesNotTakeMultipleValue(
432 flag.to_string(),
433 ));
434 }
435
436 if declaration.flag_type != TypeId::of::<T>() {
437 return Err(OptionsDecoderError::incorrect_type::<T>(flag));
438 }
439
440 if let Some(values) = self.fed_options.get(flag) {
441 let parse_function = declaration.parse_value.as_ref().unwrap();
442 let output: Vec<T> = values
443 .iter()
444 .map(|value| {
445 let parsed: Box<dyn Any> = parse_function(value)?;
446 Ok(*parsed.downcast::<T>().unwrap())
447 })
448 .collect::<DecoderResult<Vec<_>>>()?;
449 Ok(Some(output))
450 } else if declaration.optional {
451 Ok(None)
452 } else {
453 Err(OptionsDecoderError::OptionNotOptional(flag.to_string()))
454 }
455 }
456}
457
458#[derive(Debug, thiserror::Error)]
460pub enum OptionsDecoderError {
461 #[error("Given options declarations not meant for this options decoder")]
462 OptionsMismatch,
463 #[error("Given option {0} is not a flag")]
464 OptionNotFlag(String),
465 #[error("Given option {0} requires a value to be provided")]
466 OptionNotOptional(String),
467 #[error("Given option {0} does not take a value")]
468 OptionDoesNotTakeValue(String),
469 #[error("Given option {0} does not take a value")]
470 OptionDoesNotTakeMultipleValue(String),
471 #[error("Given option {0} takes multiple values")]
472 OptionTakesMultipleValue(String),
473 #[error("Given string is not a registered option")]
474 InvalidOption(String),
475 #[error(transparent)]
476 ValueParserError(#[from] Box<dyn Error + Send + Sync>),
477 #[error("Given option {option} does not take values of type {given_type}")]
478 IncorrectType { given_type: String, option: String },
479}
480
481impl OptionsDecoderError {
482 pub fn incorrect_type<T>(option: &str) -> Self {
483 Self::IncorrectType {
484 given_type: type_name::<T>().to_string(),
485 option: option.to_string(),
486 }
487 }
488}
489
490#[cfg(test)]
491mod slurper_tests {
492 use super::*;
493 use crate::defaults::tasks::Empty;
494 use more_collection_macros::map;
495
496 #[test]
497 fn slurp_flags() {
498 let args = ["--flag1", "--flag2", "task"];
499 let options = OptionDeclarations::new::<Empty, _>([
500 OptionDeclarationBuilder::flag("flag1").build(),
501 OptionDeclarationBuilder::flag("flag2").build(),
502 ]);
503
504 let slurper = OptionsSlurper::new(&options);
505 let (map, slurped) = slurper.slurp(&args).unwrap();
506 assert_eq!(slurped, 2, "only 2 values should be slurped");
507 assert_eq!(
508 map.fed_options,
509 map![
510 "flag1".to_string() => flag_value_entry(),
511 "flag2".to_string() => flag_value_entry()
512 ]
513 );
514 }
515
516 #[test]
517 fn slurp_values() {
518 let args = ["--flag1", "value1", "--flag2", "value2", "task"];
519 let options = OptionDeclarations::new::<Empty, _>([
520 OptionDeclarationBuilder::<String>::new("flag1")
521 .use_from_str()
522 .build(),
523 OptionDeclarationBuilder::<String>::new("flag2")
524 .use_from_str()
525 .build(),
526 ]);
527
528 let slurper = OptionsSlurper::new(&options);
529 let (map, slurped) = slurper.slurp(&args).unwrap();
530 assert_eq!(slurped, 4, "only 4 values should be slurped");
531 assert_eq!(
532 map.fed_options,
533 map![
534 "flag1".to_string() => vec!["value1".to_string()],
535 "flag2".to_string() => vec!["value2".to_string()]
536 ]
537 );
538 }
539
540 #[test]
541 fn mix_slurp_values_and_flags() {
542 let args = ["--flag1", "value1", "--flag3", "--flag2", "value2", "task"];
543 let options = OptionDeclarations::new::<Empty, _>([
544 OptionDeclarationBuilder::<String>::new("flag1")
545 .use_from_str()
546 .build(),
547 OptionDeclarationBuilder::<String>::new("flag2")
548 .use_from_str()
549 .build(),
550 OptionDeclarationBuilder::flag("flag3").build(),
551 ]);
552
553 let slurper = OptionsSlurper::new(&options);
554 let (map, slurped) = slurper.slurp(&args).unwrap();
555 assert_eq!(slurped, 5, "only 5 values should be slurped");
556 assert_eq!(
557 map.fed_options,
558 map![
559 "flag1".to_string() => vec!["value1".to_string()],
560 "flag2".to_string() => vec!["value2".to_string()],
561 "flag3".to_string() => flag_value_entry()
562 ]
563 );
564 }
565
566 #[test]
567 fn slurp_multiple_values() {
568 let args = ["--flag1", "value1", "--flag1", "value2"];
569 let options =
570 OptionDeclarations::new::<Empty, _>([OptionDeclarationBuilder::<String>::new("flag1")
571 .use_from_str()
572 .allow_multiple_values(true)
573 .build()]);
574
575 let slurper = OptionsSlurper::new(&options);
576 let (map, slurped) = slurper.slurp(&args).unwrap();
577 assert_eq!(slurped, 4, "only 4 values should be slurped");
578 assert_eq!(
579 map.fed_options,
580 map![
581 "flag1".to_string() => vec!["value1".to_string(), "value2".to_string()],
582 ]
583 );
584 }
585
586 #[test]
587 fn flag_not_a_value() {
588 let args = ["--flag1", "--flag2", "task"];
589 let options = OptionDeclarations::new::<Empty, _>([
590 OptionDeclarationBuilder::<String>::new("flag1")
591 .use_from_str()
592 .build(),
593 OptionDeclarationBuilder::flag("flag2").build(),
594 ]);
595
596 let slurper = OptionsSlurper::new(&options);
597 assert!(slurper.slurp(&args).is_err());
598 }
599
600 #[test]
601 fn option_missing_value() {
602 let args = ["--flag1"];
603 let options = OptionDeclarations::new::<Empty, _>([
604 OptionDeclarationBuilder::<String>::new("flag1")
605 .use_from_str()
606 .build(),
607 OptionDeclarationBuilder::flag("flag2").build(),
608 ]);
609
610 let slurper = OptionsSlurper::new(&options);
611 assert!(slurper.slurp(&args).is_err());
612 }
613}
614
615#[cfg(test)]
616mod decoder_tests {
617 use super::*;
618 use crate::defaults::tasks::Empty;
619
620 #[test]
621 fn can_use_value() {
622 let options =
623 OptionDeclarations::new::<Empty, _>([OptionDeclarationBuilder::<i32>::new("count")
624 .use_from_str()
625 .build()]);
626
627 let slurper = options.slurper();
628 let (weak, _) = slurper.slurp(&["--count", "15"]).unwrap();
629
630 let upgraded = weak.upgrade(&options).unwrap();
631
632 let count = upgraded.get_value::<i32>("count").unwrap();
633 assert_eq!(count, Some(15));
634 }
635
636 #[test]
637 fn optional_not_required() {
638 let options =
639 OptionDeclarations::new::<Empty, _>([OptionDeclarationBuilder::<i32>::new("count")
640 .use_from_str()
641 .optional(true)
642 .build()]);
643
644 let slurper = options.slurper();
645 let (weak, _) = slurper.slurp::<&str>(&[]).unwrap();
646
647 let upgraded = weak.upgrade(&options).unwrap();
648
649 let count = upgraded.get_value::<i32>("count").unwrap();
650 assert_eq!(count, None);
651 }
652
653 #[test]
654 fn can_use_multiple_values() {
655 let options =
656 OptionDeclarations::new::<Empty, _>([OptionDeclarationBuilder::<i32>::new("count")
657 .use_from_str()
658 .allow_multiple_values(true)
659 .build()]);
660
661 let slurper = options.slurper();
662 let (weak, _) = slurper.slurp(&["--count", "15", "--count", "16"]).unwrap();
663
664 let upgraded = weak.upgrade(&options).unwrap();
665
666 let count = upgraded.get_values::<i32>("count").unwrap();
667 assert_eq!(count, Some(vec![15, 16]));
668 }
669
670 #[test]
671 fn optional_not_required_multiples() {
672 let options =
673 OptionDeclarations::new::<Empty, _>([OptionDeclarationBuilder::<i32>::new("count")
674 .use_from_str()
675 .optional(true)
676 .allow_multiple_values(true)
677 .build()]);
678
679 let slurper = options.slurper();
680 let (weak, _) = slurper.slurp::<&str>(&[]).unwrap();
681
682 let upgraded = weak.upgrade(&options).unwrap();
683
684 let count = upgraded.get_values::<i32>("count").unwrap();
685 assert_eq!(count, None);
686 }
687
688 #[test]
689 fn slurp_multiple_values_can_cause_errors_if_invalid() {
690 let args = ["--flag1", "value1", "--flag1", "value2"];
691 let options =
692 OptionDeclarations::new::<Empty, _>([OptionDeclarationBuilder::<String>::new("flag1")
693 .use_from_str()
694 .takes_value(true)
695 .allow_multiple_values(false)
696 .build()]);
697
698 let slurper = OptionsSlurper::new(&options);
699 let (weak, _) = slurper.slurp(&args).unwrap();
700
701 let upgraded = weak.upgrade(&options).unwrap();
702
703 let err = upgraded.get_value::<String>("flag1");
704 if let Err(OptionsDecoderError::OptionDoesNotTakeMultipleValue(_)) = err {
705 } else {
706 panic!("Should cause an error, does take multiple values error")
707 }
708 }
709
710 #[test]
711 fn non_optional_cause_errors_if_missing() {
712 let args = ["--flag1", "value1"];
713 let options = OptionDeclarations::new::<Empty, _>([
714 OptionDeclarationBuilder::<String>::new("flag1")
715 .use_from_str()
716 .build(),
717 OptionDeclarationBuilder::<String>::new("flag2")
718 .use_from_str()
719 .build(),
720 ]);
721
722 let slurper = OptionsSlurper::new(&options);
723 let (weak, _) = slurper.slurp(&args).unwrap();
724
725 let upgraded = weak.upgrade(&options).unwrap();
726
727 let err = upgraded.get_value::<String>("flag2");
728 if let Err(OptionsDecoderError::OptionNotOptional(_)) = err {
729 } else {
730 panic!("Should cause an error, flag2 is not optional")
731 }
732 }
733}