flood_tide/
lib.rs

1/*!
2Command line flag and option parse utilities.
3
4# Features
5
6- `no_std` and `std` are supported.
7- flags, options, subcommand and free arguments
8- short flags and options (like `-a`)
9- long flags and options (like `--long`)
10- combined short flags (like `-abc` ::= `-a` `-b` `-c`)
11- single long options (like `-long`)
12- abbreviate long options (like `--abbr` ::= `--abbreviate`)
13- single error or multiple errors
14- only UTF-8 arguments
15- it can be used optimally by a compile switch with many features.
16- minimum support rustc 1.60.0 (7737e0b5c 2022-04-04)
17
18# Todos
19
20- [x] multiple errors
21- [x] `no_std`
22- [ ] option suggestion (do you mean? '--abc')
23- [ ] windows style (like `/a`)
24- [ ] source code generator support tools
25- [ ] more easy use
26
27# Non-Supports
28
29- procedural macro style
30- traditional macro style
31- non UTF-8 arguments, multibyte or wide charactor
32
33# Examples
34
35in [examples](https://github.com/aki-akaguma/flood-tide/tree/main/examples) directory.
36
37- manual coding style: bsd-sed.rs, gnu-cat.rs
38- single long options: ffmpeg.rs
39- source code generating by xtask and parse_simple_gnu_style(): curl.rs
40
41# Supports
42
43- [flood-tide-gen](https://crates.io/crates/flood-tide-gen) - the generating *flood-tide* tables
44- [aki-gsub](https://crates.io/crates/aki-gsub) - the sample used *flood-tide*
45
46# Alternatives
47
48This parser is *not* a new special idea. It's just comparing characters one by one.
49Is there anything simpler than this?
50
51- [clap](https://crates.io/crates/clap) - is the most popular and complete one
52- [structopt](https://crates.io/crates/structopt) - clap parser that uses procedural macros
53- [gumdrop](https://crates.io/crates/gumdrop) - a simple parser that uses procedural macros
54- [argh](https://crates.io/crates/argh) - procedural macros
55- [rustop](https://crates.io/crates/rustop) - traditional macro
56- [pico-args](https://crates.io/crates/pico-args) - a simple use
57- [getopts](https://crates.io/crates/getopts) - a simple use
58- [docopt](https://crates.io/crates/docopt) - a simple use
59
60*/
61#![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/// Option parse error type
81#[cfg(any(not(feature = "single_error"), feature = "dox"))]
82pub type OpErr = OptParseErrors;
83
84/// Option parse error type
85#[cfg(feature = "single_error")]
86pub type OpErr = OptParseError;
87
88/// Option number type
89#[cfg(feature = "optnum_u16")]
90pub type OptNum = u16;
91
92/// Option number type
93#[cfg(any(not(feature = "optnum_u16"), feature = "dox"))]
94pub type OptNum = u8;
95
96pub use err::OptParseErrorKind;
97
98/// check help and version of conf
99pub trait HelpVersion {
100    fn is_help(&self) -> bool;
101    fn is_version(&self) -> bool;
102}
103
104/// setter subcmd of conf
105pub trait SubCommand {
106    fn set_subcmd(&mut self, subcmd: String);
107}
108
109/// Parse simple gnu style.
110#[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    //
130    #[cfg(not(feature = "single_error"))]
131    let mut errs = OptParseErrors::new();
132    //
133    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    //
148    let mut v: Vec<String> = Vec::new();
149    v.extend(tokens.free.iter().map(|&s| s.to_string()));
150    //
151    #[cfg(feature = "single_error")]
152    return (Some(v), Ok(()));
153    #[cfg(not(feature = "single_error"))]
154    return (Some(v), Err(errs));
155}
156
157/// Parse simple gnu style with sub command.
158#[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    //
179    #[cfg(not(feature = "single_error"))]
180    let mut errs = OptParseErrors::new();
181    //
182    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    //
197    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    //
207    let mut v: Vec<String> = Vec::new();
208    v.extend(tokens.free.iter().map(|&s| s.to_string()));
209    //
210    #[cfg(feature = "single_error")]
211    return (Some(v), Ok(()));
212    #[cfg(not(feature = "single_error"))]
213    return (Some(v), Err(errs));
214}
215
216/// Option argument
217#[repr(u8)]
218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219pub enum Arg {
220    No = 0,
221    Yes,
222    Maybe,
223}
224
225/// Record type of opt ary table
226///
227/// # Examples
228/// ```
229/// #[cfg(feature = "option_argument")]
230/// {
231///     use flood_tide::Arg;
232///     use flood_tide::Lex;
233///     use flood_tide::Opt;
234///     use flood_tide::OptNum;
235///    
236///     #[rustfmt::skip]
237///     #[repr(u8)]
238///     #[derive(Debug, PartialEq)]
239///     enum CmdOP { A = 1, Barn, Eat, };
240///     impl CmdOP { pub const fn to(self) -> OptNum { self as OptNum } }
241///    
242///     #[rustfmt::skip]
243///     const OPT_ARY: [Opt;3] = [
244///         Opt { sho: b'a', lon: "",     has: Arg::No,  num: CmdOP::A.to(), },
245///         Opt { sho: b'b', lon: "barn", has: Arg::No,  num: CmdOP::Barn.to(), },
246///         Opt { sho: 0u8,  lon: "eat",  has: Arg::Yes, num: CmdOP::Eat.to(), },
247///     ];
248/// }
249///
250#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub struct Opt<'a> {
252    /// short name
253    pub sho: u8,
254    /// long name
255    pub lon: &'a str,
256    /// has arg / option argument
257    #[cfg(any(feature = "option_argument", feature = "dox"))]
258    pub has: Arg,
259    /// uniq number
260    pub num: OptNum,
261}
262impl Opt<'_> {
263    /// long or short name
264    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/// Entity as the result of lex
277#[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    /// long name or short name
288    ///
289    /// At the compiling with feature = "was_long",
290    /// this return a result according to a command line keyword
291    /// Otherwise this return long name or short name.
292    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        //
298        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/// Tokens as the result of lex
307#[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/// Lexical analyzer
318///
319/// this is analyzing command line arguments, returning tokens.
320///
321/// # Examples
322/// ```
323/// #[cfg(not(feature = "long_only"))]
324/// #[cfg(feature = "option_argument")]
325/// {
326///     use flood_tide::{Arg, Lex, Opt, OptNum};
327///     
328///     #[rustfmt::skip]
329///     let args = ["-ab", "--barn", "--eat", "jum"];
330///     
331///     #[rustfmt::skip]
332///     #[repr(u8)]
333///     #[derive(Debug, PartialEq)]
334///     enum CmdOP { A = 1, Barn, Eat, };
335///     impl CmdOP { pub const fn to(self) -> OptNum { self as OptNum } }
336///      
337///     #[rustfmt::skip]
338///     const OPT_ARY: [Opt;3] = [
339///         Opt { sho: b'a', lon: "",     has: Arg::No,  num: CmdOP::A.to(), },
340///         Opt { sho: b'b', lon: "barn", has: Arg::No,  num: CmdOP::Barn.to(), },
341///         Opt { sho: 0u8,  lon: "eat",  has: Arg::Yes, num: CmdOP::Eat.to(), },
342///     ];
343///     #[rustfmt::skip]
344///     const OPT_ARY_SHO_IDX: [(u8,usize);2] = [(b'a',0),(b'b',1)];
345///    
346///     let lex = Lex::create_with(&OPT_ARY, &OPT_ARY_SHO_IDX);
347///     let tokens = match lex.tokens_from(&args) {
348///         Ok(t) => t,
349///         Err(e) => unreachable!(),
350///     };
351/// }
352/// ```
353#[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    /// create lexical analyzer
363    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    /// setup subcommand ary
372    #[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    /// analyze and return tokens
379    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        //
385        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                    // stop on
391                    v_free.push(cur);
392                    v_free.extend(cursor);
393                    break 'itr_cursor;
394                }
395            }
396            let f_single = if !cur.starts_with('-') {
397                // free
398                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                // option: long name
412                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                // option: short name or long only
427                //
428                #[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        //
451        #[cfg(not(feature = "single_error"))]
452        {
453            if !v_errs.is_empty() {
454                return Err(v_errs);
455            }
456        }
457        //
458        #[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        //
468        #[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    // parse
517    //
518    #[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    //
530    #[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    //
543    #[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    //
555    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        //
571        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        //
586        #[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        //
614        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    //
623    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            //
683            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        //
692        #[cfg(not(feature = "single_error"))]
693        {
694            if !errs.is_empty() {
695                return Err(errs);
696            }
697        }
698        //
699        Ok(())
700    }
701    //
702    #[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            //  "-f"
711            // short name
712            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                    // long name
720                    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        //
755        Ok(())
756    }
757    //
758    #[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}