1#![cfg_attr(feature = "no_std", no_std)]
62#![allow(unused_labels)]
63
64#[cfg(feature = "no_std")]
65#[macro_use]
66extern crate alloc;
67
68#[cfg(feature = "no_std")]
69use alloc::string::{String, ToString};
70#[cfg(feature = "no_std")]
71use alloc::vec::Vec;
72
73pub mod check;
74pub mod err;
75pub use err::OptParseError;
76
77#[cfg(any(not(feature = "single_error"), feature = "dox"))]
78pub use err::OptParseErrors;
79
80#[cfg(any(not(feature = "single_error"), feature = "dox"))]
82pub type OpErr = OptParseErrors;
83
84#[cfg(feature = "single_error")]
86pub type OpErr = OptParseError;
87
88#[cfg(feature = "optnum_u16")]
90pub type OptNum = u16;
91
92#[cfg(any(not(feature = "optnum_u16"), feature = "dox"))]
94pub type OptNum = u8;
95
96pub use err::OptParseErrorKind;
97
98pub trait HelpVersion {
100 fn is_help(&self) -> bool;
101 fn is_version(&self) -> bool;
102}
103
104pub trait SubCommand {
106 fn set_subcmd(&mut self, subcmd: String);
107}
108
109#[cfg(any(feature = "stop_at_mm", feature = "dox"))]
111pub fn parse_simple_gnu_style<'a, T, F>(
112 conf: &mut T,
113 opt_ary: &'a [Opt],
114 sho_idx_ary: &'a [(u8, usize)],
115 args: &'a [&'a str],
116 parse_match: F,
117) -> (Option<Vec<String>>, Result<(), OpErr>)
118where
119 F: Fn(&mut T, &NameVal<'_>) -> Result<(), OptParseError>,
120 T: HelpVersion,
121{
122 let lex = Lex::create_with(opt_ary, sho_idx_ary);
123 let tokens = match lex.tokens_from(args) {
124 Ok(t) => t,
125 Err(errs) => {
126 return (None, Err(errs));
127 }
128 };
129 #[cfg(not(feature = "single_error"))]
131 let mut errs = OptParseErrors::new();
132 for nv in tokens.namevals.iter() {
134 match parse_match(conf, nv) {
135 Ok(_) => {}
136 Err(err) => {
137 #[cfg(feature = "single_error")]
138 return (None, Err(err));
139 #[cfg(not(feature = "single_error"))]
140 errs.push(err);
141 }
142 }
143 if conf.is_help() || conf.is_version() {
144 break;
145 }
146 }
147 let mut v: Vec<String> = Vec::new();
149 v.extend(tokens.free.iter().map(|&s| s.to_string()));
150 #[cfg(feature = "single_error")]
152 return (Some(v), Ok(()));
153 #[cfg(not(feature = "single_error"))]
154 return (Some(v), Err(errs));
155}
156
157#[cfg(any(all(feature = "stop_at_mm", feature = "subcommand"), feature = "dox"))]
159pub fn parse_simple_gnu_style_subcmd<'a, T, F>(
160 conf: &mut T,
161 opt_ary: &'a [Opt],
162 sho_idx_ary: &'a [(u8, usize)],
163 args: &'a [&'a str],
164 parse_match: F,
165 subcmds: &'a [&'a str],
166) -> (Option<Vec<String>>, Result<(), OpErr>)
167where
168 F: Fn(&mut T, &NameVal<'_>) -> Result<(), OptParseError>,
169 T: HelpVersion + SubCommand,
170{
171 let lex = Lex::create_with(opt_ary, sho_idx_ary).subcmd(subcmds);
172 let tokens = match lex.tokens_from(args) {
173 Ok(t) => t,
174 Err(errs) => {
175 return (None, Err(errs));
176 }
177 };
178 #[cfg(not(feature = "single_error"))]
180 let mut errs = OptParseErrors::new();
181 for nv in tokens.namevals.iter() {
183 match parse_match(conf, nv) {
184 Ok(_) => {}
185 Err(err) => {
186 #[cfg(feature = "single_error")]
187 return (None, Err(err));
188 #[cfg(not(feature = "single_error"))]
189 errs.push(err);
190 }
191 }
192 if conf.is_help() || conf.is_version() {
193 break;
194 }
195 }
196 match tokens.subcmd {
198 Some(s) => conf.set_subcmd(String::from(s)),
199 None => {
200 #[cfg(feature = "single_error")]
201 return (None, Err(OptParseError::missing_subcommand("<command>")));
202 #[cfg(not(feature = "single_error"))]
203 errs.push(OptParseError::missing_subcommand("<command>"));
204 }
205 };
206 let mut v: Vec<String> = Vec::new();
208 v.extend(tokens.free.iter().map(|&s| s.to_string()));
209 #[cfg(feature = "single_error")]
211 return (Some(v), Ok(()));
212 #[cfg(not(feature = "single_error"))]
213 return (Some(v), Err(errs));
214}
215
216#[repr(u8)]
218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219pub enum Arg {
220 No = 0,
221 Yes,
222 Maybe,
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub struct Opt<'a> {
252 pub sho: u8,
254 pub lon: &'a str,
256 #[cfg(any(feature = "option_argument", feature = "dox"))]
258 pub has: Arg,
259 pub num: OptNum,
261}
262impl Opt<'_> {
263 pub fn lon_or_sho(&self) -> String {
265 if !self.lon.is_empty() {
266 self.lon.to_string()
267 } else if self.sho != 0_u8 {
268 let v = vec![self.sho];
269 String::from_utf8_lossy(&v).to_string()
270 } else {
271 "".to_string()
272 }
273 }
274}
275
276#[derive(Debug)]
278pub struct NameVal<'a> {
279 pub opt: &'a Opt<'a>,
280 #[cfg(any(feature = "option_argument", feature = "dox"))]
281 pub val: Option<&'a str>,
282 #[cfg(any(feature = "was_long", feature = "dox"))]
283 pub was_long: bool,
284}
285
286impl NameVal<'_> {
287 pub fn name(&self) -> String {
293 #[cfg(feature = "was_long")]
294 let b = self.was_long;
295 #[cfg(not(feature = "was_long"))]
296 let b = !self.opt.lon.is_empty();
297 if b {
299 self.opt.lon.to_string()
300 } else {
301 String::from_utf8_lossy(&[self.opt.sho]).to_string()
302 }
303 }
304}
305
306#[derive(Debug)]
308pub struct Tokens<'a> {
309 pub namevals: Vec<NameVal<'a>>,
310 #[cfg(any(feature = "stop_at_mm", feature = "dox"))]
311 pub double_m: bool,
312 #[cfg(any(feature = "subcommand", feature = "dox"))]
313 pub subcmd: Option<&'a str>,
314 pub free: Vec<&'a str>,
315}
316
317#[derive(Debug)]
354pub struct Lex<'a> {
355 opts: &'a [Opt<'a>],
356 sho_idx: &'a [(u8, usize)],
357 #[cfg(any(feature = "subcommand", feature = "dox"))]
358 subcmds: &'a [&'a str],
359}
360
361impl<'a> Lex<'a> {
362 pub fn create_with(opt_ary: &'a [Opt], sho_idx_ary: &'a [(u8, usize)]) -> Lex<'a> {
364 Lex {
365 opts: opt_ary,
366 sho_idx: sho_idx_ary,
367 #[cfg(feature = "subcommand")]
368 subcmds: &[],
369 }
370 }
371 #[cfg(any(feature = "subcommand", feature = "dox"))]
373 #[inline]
374 pub fn subcmd(mut self, subcmd_ary: &'a [&'a str]) -> Self {
375 self.subcmds = subcmd_ary;
376 self
377 }
378 pub fn tokens_from(&'a self, args: &'a [&'a str]) -> Result<Tokens<'a>, OpErr> {
380 #[cfg(not(feature = "single_error"))]
381 let mut v_errs = OpErr::new();
382 let mut v_free: Vec<&str> = Vec::new();
383 let mut v_namevals: Vec<NameVal> = Vec::new();
384 let mut cursor = args.iter();
386 'itr_cursor: while let Some(cur) = cursor.next() {
387 #[cfg(feature = "stop_at_mm")]
388 {
389 if *cur == "--" {
390 v_free.push(cur);
392 v_free.extend(cursor);
393 break 'itr_cursor;
394 }
395 }
396 let f_single = if !cur.starts_with('-') {
397 v_free.push(cur);
399 #[cfg(feature = "stop_at_free")]
400 {
401 v_free.extend(cursor);
402 break 'itr_cursor;
403 }
404 #[cfg(not(feature = "stop_at_free"))]
405 false
406 } else {
407 true
408 };
409 #[cfg(not(feature = "long_only"))]
410 let f_single = if f_single && cur.starts_with("--") {
411 match self.parse_long_name(&mut cursor, &cur[2..]) {
413 Ok(nv) => v_namevals.push(nv),
414 Err(err) => {
415 #[cfg(feature = "single_error")]
416 return Err(err);
417 #[cfg(not(feature = "single_error"))]
418 v_errs.push(err)
419 }
420 };
421 false
422 } else {
423 f_single
424 };
425 if f_single {
426 #[cfg(not(feature = "long_only"))]
429 {
430 #[cfg(feature = "single_error")]
431 self.parse_short_name(&mut cursor, &cur[1..], &mut v_namevals)?;
432 #[cfg(not(feature = "single_error"))]
433 if let Err(errs) =
434 self.parse_short_name(&mut cursor, &cur[1..], &mut v_namevals)
435 {
436 v_errs.append(errs);
437 }
438 }
439 #[cfg(feature = "long_only")]
440 {
441 #[cfg(feature = "single_error")]
442 self.parse_long_only(&mut cursor, cur, &mut v_namevals)?;
443 #[cfg(not(feature = "single_error"))]
444 if let Err(errs) = self.parse_long_only(&mut cursor, cur, &mut v_namevals) {
445 v_errs.append(errs);
446 }
447 }
448 }
449 }
450 #[cfg(not(feature = "single_error"))]
452 {
453 if !v_errs.is_empty() {
454 return Err(v_errs);
455 }
456 }
457 #[cfg(feature = "stop_at_mm")]
459 let is_stop_at_double_m = {
460 if !v_free.is_empty() && v_free[0] == "--" {
461 v_free.remove(0);
462 true
463 } else {
464 false
465 }
466 };
467 #[cfg(feature = "subcommand")]
469 {
470 #[cfg(feature = "stop_at_mm")]
471 let b = !self.subcmds.is_empty() && !is_stop_at_double_m;
472 #[cfg(not(feature = "stop_at_mm"))]
473 let b = !self.subcmds.is_empty();
474 let v_cmd = if b {
475 match self.parse_subcmd(&v_free) {
476 Ok((opt, remove_1st)) => {
477 if remove_1st {
478 v_free.remove(0);
479 }
480 opt
481 }
482 Err(err) => {
483 #[cfg(feature = "single_error")]
484 return Err(err);
485 #[cfg(not(feature = "single_error"))]
486 {
487 v_errs.push(err);
488 return Err(v_errs);
489 }
490 }
491 }
492 } else {
493 None
494 };
495 Ok(Tokens {
496 namevals: v_namevals,
497 free: v_free,
498 #[cfg(feature = "stop_at_mm")]
499 double_m: is_stop_at_double_m,
500 subcmd: v_cmd,
501 })
502 }
503 #[cfg(not(feature = "subcommand"))]
504 {
505 Ok(Tokens {
506 namevals: v_namevals,
507 free: v_free,
508 #[cfg(feature = "stop_at_mm")]
509 double_m: is_stop_at_double_m,
510 })
511 }
512 }
513}
514
515impl<'a> Lex<'a> {
516 #[cfg(feature = "abbreviate")]
519 fn find_abbreviate(&'a self, name: &'a str) -> Result<&'a Opt<'a>, OptParseError> {
520 #[rustfmt::skip]
521 let ambiguous: Vec<&Opt<'a>> = self.opts.iter()
522 .filter(|&o| o.lon.starts_with(name)).collect();
523 match ambiguous.len() {
524 1 => Ok(ambiguous[0]),
525 0 => mkerr_invalid_option(name),
526 _ => mkerr_ambiguous_option(name, &ambiguous),
527 }
528 }
529 #[cfg(feature = "subcommand")]
531 #[cfg(feature = "abbreviate")]
532 fn find_abbreviate_subcmd<'b>(&'a self, name: &'b str) -> Result<&'a str, OptParseError> {
533 #[rustfmt::skip]
534 let ambiguous: Vec<&'a str> = self.subcmds.iter()
535 .filter(|&o| o.starts_with(name)).copied().collect();
536 match ambiguous.len() {
537 1 => Ok(ambiguous[0]),
538 0 => mkerr_invalid_subcommand(name),
539 _ => mkerr_ambiguous_subcommand(name, &ambiguous),
540 }
541 }
542 #[cfg(feature = "subcommand")]
544 #[cfg(not(feature = "abbreviate"))]
545 fn find_match_subcmd<'b>(&'a self, name: &'b str) -> Result<&'a str, OptParseError> {
546 #[rustfmt::skip]
547 let ambiguous: Vec<&'a str> = self.subcmds.iter()
548 .filter(|&o| o == &name).copied().collect();
549 match ambiguous.len() {
550 1 => Ok(ambiguous[0]),
551 _ => mkerr_invalid_subcommand(name),
552 }
553 }
554 fn parse_long_name(
556 &'a self,
557 _cursor: &mut dyn Iterator<Item = &&'a str>,
558 tail: &'a str,
559 ) -> Result<NameVal<'a>, OptParseError> {
560 #[cfg(feature = "option_argument")]
561 let (name, val) = {
562 let eq_idx = tail.find('=');
563 match eq_idx {
564 Some(usz) => (&tail[0..usz], Some(&tail[usz + 1..])),
565 None => (tail, None),
566 }
567 };
568 #[cfg(not(feature = "option_argument"))]
569 let name = tail;
570 let v_opt = {
572 let found = self.opts.binary_search_by_key(&name, |&o| o.lon);
573 match found {
574 Ok(idx) => &self.opts[idx],
575 _ => {
576 #[cfg(feature = "abbreviate")]
577 {
578 self.find_abbreviate(name)?
579 }
580 #[cfg(not(feature = "abbreviate"))]
581 return mkerr_invalid_option(name);
582 }
583 }
584 };
585 #[cfg(feature = "option_argument")]
587 let val2 = match v_opt.has {
588 Arg::No => {
589 if let Some(v) = val {
590 return mkerr_unexpected_option_argument(name, v);
591 }
592 val
593 }
594 Arg::Maybe => {
595 if val.is_none() {
596 Some(&tail[0..0])
597 } else {
598 val
599 }
600 }
601 Arg::Yes => {
602 if val.is_none() {
603 if let Some(&cur_val) = _cursor.next() {
604 Some(cur_val)
605 } else {
606 return mkerr_missing_option_argument(name);
607 }
608 } else {
609 val
610 }
611 }
612 };
613 Ok(NameVal {
615 opt: v_opt,
616 #[cfg(feature = "option_argument")]
617 val: val2,
618 #[cfg(feature = "was_long")]
619 was_long: true,
620 })
621 }
622 fn parse_short_name(
624 &'a self,
625 _cursor: &mut dyn Iterator<Item = &&'a str>,
626 tail: &'a str,
627 namevals: &mut Vec<NameVal<'a>>,
628 ) -> Result<(), OpErr> {
629 #[cfg(not(feature = "single_error"))]
630 let mut errs = OpErr::new();
631 let tail_len = tail.len();
632 '_ic_iter: for i in 0..tail_len {
633 let c_name = &tail[i..=i];
634 let b_name = c_name.as_bytes()[0];
635 let v_opt = {
636 let found = self.sho_idx.binary_search_by_key(&b_name, |&o| o.0);
637 match found {
638 Ok(idx) => &self.opts[self.sho_idx[idx].1],
639 _ => {
640 #[cfg(feature = "single_error")]
641 return Err(OptParseError::invalid_option(c_name));
642 #[cfg(not(feature = "single_error"))]
643 {
644 errs.push(OptParseError::invalid_option(c_name));
645 continue '_ic_iter;
646 }
647 }
648 }
649 };
650 #[cfg(feature = "option_argument")]
651 let c_val = if v_opt.has == Arg::No {
652 None
653 } else if i < tail_len - 1 {
654 let rest = &tail[i + 1..];
655 namevals.push(NameVal {
656 opt: v_opt,
657 val: Some(rest),
658 #[cfg(feature = "was_long")]
659 was_long: false,
660 });
661 break '_ic_iter;
662 } else if v_opt.has == Arg::Maybe {
663 let rest = &tail[tail_len..tail_len];
664 namevals.push(NameVal {
665 opt: v_opt,
666 val: Some(rest),
667 #[cfg(feature = "was_long")]
668 was_long: false,
669 });
670 break '_ic_iter;
671 } else if let Some(&cur_val) = _cursor.next() {
672 Some(cur_val)
673 } else {
674 #[cfg(feature = "single_error")]
675 return Err(OptParseError::missing_option_argument(c_name));
676 #[cfg(not(feature = "single_error"))]
677 {
678 errs.push(OptParseError::missing_option_argument(c_name));
679 continue '_ic_iter;
680 }
681 };
682 namevals.push(NameVal {
684 opt: v_opt,
685 #[cfg(feature = "option_argument")]
686 val: c_val,
687 #[cfg(feature = "was_long")]
688 was_long: false,
689 });
690 }
691 #[cfg(not(feature = "single_error"))]
693 {
694 if !errs.is_empty() {
695 return Err(errs);
696 }
697 }
698 Ok(())
700 }
701 #[cfg(feature = "long_only")]
703 fn parse_long_only(
704 &'a self,
705 mut cursor: &mut dyn Iterator<Item = &'a &'a str>,
706 cur: &'a str,
707 namevals: &mut Vec<NameVal<'a>>,
708 ) -> Result<(), OpErr> {
709 if cur.len() == 2 {
710 let e = self.parse_short_name(&mut cursor, &cur[1..], namevals);
713 if let Err(errs) = e {
714 #[cfg(not(feature = "single_error"))]
715 let err = &errs.iter().as_slice()[0];
716 #[cfg(feature = "single_error")]
717 let err = &errs;
718 if err.kind() == OptParseErrorKind::InvalidOption {
719 match self.parse_long_name(&mut cursor, &cur[1..]) {
721 Ok(nv) => {
722 namevals.push(nv);
723 }
724 Err(err) => {
725 #[cfg(feature = "single_error")]
726 return Err(err);
727 #[cfg(not(feature = "single_error"))]
728 {
729 let mut errs = OpErr::new();
730 errs.push(err);
731 return Err(errs);
732 }
733 }
734 };
735 } else {
736 return Err(errs);
737 }
738 }
739 } else {
740 match self.parse_long_name(&mut cursor, &cur[1..]) {
741 Ok(nv) => namevals.push(nv),
742 Err(err) => {
743 #[cfg(feature = "single_error")]
744 return Err(err);
745 #[cfg(not(feature = "single_error"))]
746 {
747 let mut errs = OpErr::new();
748 errs.push(err);
749 return Err(errs);
750 }
751 }
752 }
753 };
754 Ok(())
756 }
757 #[cfg(feature = "subcommand")]
759 fn parse_subcmd(&'a self, v_free: &[&str]) -> Result<(Option<&'a str>, bool), OptParseError> {
760 let mut v_cmd: Option<&'a str> = None;
761 let mut remove_1st = false;
762 if !v_free.is_empty() {
763 let free_1st = v_free[0];
764 if free_1st != "--" && !free_1st.is_empty() {
765 #[cfg(feature = "abbreviate")]
766 match self.find_abbreviate_subcmd(free_1st) {
767 Ok(subcmd) => {
768 v_cmd = Some(subcmd);
769 remove_1st = true;
770 }
771 Err(err) => return Err(err),
772 };
773 #[cfg(not(feature = "abbreviate"))]
774 match self.find_match_subcmd(free_1st) {
775 Ok(subcmd) => {
776 v_cmd = Some(subcmd);
777 remove_1st = true;
778 }
779 Err(err) => return Err(err),
780 };
781 }
782 }
783 Ok((v_cmd, remove_1st))
784 }
785}
786
787#[inline]
788fn mkerr_invalid_option<T>(name: &str) -> Result<T, OptParseError> {
789 Err(OptParseError::invalid_option(name))
790}
791
792#[cfg(feature = "option_argument")]
793#[inline]
794fn mkerr_unexpected_option_argument<T>(name: &str, val: &str) -> Result<T, OptParseError> {
795 Err(OptParseError::unexpected_option_argument(name, val))
796}
797
798#[cfg(feature = "option_argument")]
799#[inline]
800fn mkerr_missing_option_argument<T>(name: &str) -> Result<T, OptParseError> {
801 Err(OptParseError::missing_option_argument(name))
802}
803
804#[cfg(feature = "subcommand")]
805#[inline]
806fn mkerr_invalid_subcommand<T>(name: &str) -> Result<T, OptParseError> {
807 Err(OptParseError::invalid_subcommand(name))
808}
809
810#[cfg(feature = "abbreviate")]
811fn mkerr_ambiguous_option<'a, T>(
812 name: &'a str,
813 ambiguous: &[&Opt<'a>],
814) -> Result<T, OptParseError> {
815 let mut hint = "possibilities:".to_string();
816 for &a in ambiguous {
817 let ss = format!(" '--{}'", a.lon);
818 hint.push_str(ss.as_str());
819 }
820 Err(OptParseError::ambiguous_option(name, hint.as_str()))
821}
822
823#[cfg(all(feature = "abbreviate", feature = "subcommand"))]
824fn mkerr_ambiguous_subcommand<'a, T>(
825 name: &'a str,
826 ambiguous: &[&'a str],
827) -> Result<T, OptParseError> {
828 let mut hint = "possibilities:".to_string();
829 for &a in ambiguous {
830 let ss = format!(" '{}'", a);
831 hint.push_str(ss.as_str());
832 }
833 Err(OptParseError::ambiguous_subcommand(name, hint.as_str()))
834}