aopt 1.4.5

A flexible and typed getopt tools
Documentation
use std::borrow::Cow;
use std::fmt::Debug;
use std::marker::PhantomData;

use crate::args;
use crate::args::ArgInfo;
use crate::args::Args;
use crate::ctx::Ctx;
use crate::ctx::Invoker;
use crate::opt::Opt;
use crate::opt::OptParser;
use crate::parser::OptStyleManager;
use crate::parser::Policy;
use crate::parser::PolicySettings;
use crate::parser::Return;
use crate::parser::UserStyle;
use crate::set::OptValidator;
use crate::set::SetOpt;
use crate::shell::CompleteGuess;
use crate::trace;
use crate::Error;

pub struct CompletePolicy<S> {
    strict: bool,

    process_pos: bool,

    style_manager: OptStyleManager,

    marker_s: PhantomData<S>,
}

impl<S> Clone for CompletePolicy<S> {
    fn clone(&self) -> Self {
        Self {
            strict: self.strict,
            process_pos: self.process_pos,
            style_manager: self.style_manager.clone(),
            marker_s: self.marker_s,
        }
    }
}

impl<S> Debug for CompletePolicy<S> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("CompletePolicy")
            .field("strict", &self.strict)
            .field("process_pos", &self.process_pos)
            .field("style_manager", &self.style_manager)
            .finish()
    }
}

impl<S> Default for CompletePolicy<S> {
    fn default() -> Self {
        Self {
            strict: true,
            process_pos: false,
            style_manager: OptStyleManager::default(),
            marker_s: PhantomData,
        }
    }
}

impl<S> CompletePolicy<S> {
    pub fn new(strict: bool, style: OptStyleManager) -> Self {
        Self {
            strict,
            style_manager: style,
            ..Default::default()
        }
    }
}

impl<S> CompletePolicy<S> {
    /// In strict mode, if an argument looks like an option (it matched any option prefix),
    /// then it must matched.
    pub fn with_strict(mut self, strict: bool) -> Self {
        self.strict = strict;
        self
    }

    pub fn with_styles(mut self, styles: Vec<UserStyle>) -> Self {
        self.style_manager.set(styles);
        self
    }

    pub fn with_process_pos(mut self, process_pos: bool) -> Self {
        self.process_pos = process_pos;
        self
    }

    pub(crate) fn noa_cmd() -> usize {
        1
    }

    pub(crate) fn noa_main() -> usize {
        0
    }

    pub(crate) fn noa_pos(idx: usize) -> usize {
        idx
    }

    pub fn process_pos(&self) -> bool {
        self.process_pos
    }
}

impl<S> PolicySettings for CompletePolicy<S> {
    fn style_manager(&self) -> &OptStyleManager {
        &self.style_manager
    }

    fn style_manager_mut(&mut self) -> &mut OptStyleManager {
        &mut self.style_manager
    }

    fn strict(&self) -> bool {
        self.strict
    }

    fn styles(&self) -> &[UserStyle] {
        &self.style_manager
    }

    fn no_delay(&self) -> Option<&[String]> {
        None
    }

    fn overload(&self) -> bool {
        false
    }

    fn prepolicy(&self) -> bool {
        false
    }

    fn set_strict(&mut self, strict: bool) -> &mut Self {
        self.strict = strict;
        self
    }

    fn set_styles(&mut self, styles: Vec<UserStyle>) -> &mut Self {
        self.style_manager.set(styles);
        self
    }

    fn set_no_delay(&mut self, _: impl Into<String>) -> &mut Self {
        self
    }

    fn set_overload(&mut self, _: bool) -> &mut Self {
        self
    }

    fn set_prepolicy(&mut self, _: bool) -> &mut Self {
        self
    }
}

impl<S> CompletePolicy<S>
where
    SetOpt<S>: Opt,
    S: crate::set::Set + OptParser + OptValidator,
{
    pub(crate) fn parse_impl<'a>(
        &mut self,
        set: &mut <Self as Policy>::Set,
        inv: &mut <Self as Policy>::Inv<'_>,
        orig: &'a Args,
        ctx: &mut Ctx<'a>,
    ) -> Result<(), <Self as Policy>::Error> {
        let opt_styles = &self.style_manager;
        let args: Vec<_> = orig.iter().map(|v| v.as_os_str()).collect();
        let total = args.len();
        let mut left = vec![];
        let mut iter = args::iter2(&args).enumerate();

        trace!("parsing {ctx:?} using fwd policy");
        ctx.set_args(args.clone());
        while let Some((idx, (opt, next))) = iter.next() {
            let mut matched = false;
            let mut consume = false;

            if let Ok(ArgInfo { name, value }) = ArgInfo::parse(opt) {
                trace!(
                    "guess name: {:?} value: {:?} & next: {:?}",
                    name,
                    value,
                    next
                );
                if set.check(&name).map_err(Into::into)? {
                    let arg = value.clone();
                    let next = next.map(|v| Cow::Borrowed(*v));
                    let mut guess = CompleteGuess {
                        idx,
                        arg,
                        set,
                        inv,
                        total,
                        ctx,
                        next,
                        name: Some(name.clone()),
                    };

                    for style in opt_styles.iter() {
                        if let Some(ret) = guess.guess_complete(style)? {
                            (matched, consume) = (ret.matched, ret.consume);
                        }
                        if matched {
                            break;
                        }
                    }
                }
            }
            // if consume the argument, skip it
            if matched && consume {
                iter.next();
            } else if !matched {
                // add it to NOA if current argument not matched
                left.push(*opt);
            }
        }

        let args = left;
        let total = args.len();

        ctx.set_args(args.clone());
        // when style is pos, noa index is [1..=len]
        if total > 0 {
            let name = crate::str::osstr_to_str_i(&args, Self::noa_cmd());
            let mut guess = CompleteGuess {
                set,
                inv,
                total,
                name,
                ctx,
                arg: None,
                next: None,
                idx: Self::noa_cmd(),
            };

            guess.guess_complete(&UserStyle::Cmd)?;

            if self.process_pos {
                let mut guess = CompleteGuess {
                    set,
                    inv,
                    total,
                    ctx,
                    name: None,
                    arg: None,
                    next: None,
                    idx: Self::noa_cmd(),
                };

                for idx in 1..total {
                    guess.idx = Self::noa_pos(idx);
                    guess.name = crate::str::osstr_to_str_i(&args, Self::noa_pos(idx));
                    guess.guess_complete(&UserStyle::Pos)?;
                }
            }
        }

        let name = crate::str::osstr_to_str_i(&args, Self::noa_main());
        let mut guess = CompleteGuess {
            set,
            inv,
            total,
            name,
            ctx,
            arg: None,
            next: None,
            idx: Self::noa_main(),
        };

        guess.guess_complete(&UserStyle::Main)?;
        Ok(())
    }
}

impl<S> Policy for CompletePolicy<S>
where
    SetOpt<S>: Opt,
    S: crate::set::Set + OptParser + OptValidator,
{
    type Ret = Return;

    type Set = S;

    type Inv<'a> = Invoker<'a, S>;

    type Error = Error;

    fn parse(
        &mut self,
        set: &mut Self::Set,
        inv: &mut Self::Inv<'_>,
        orig: Args,
    ) -> Result<Self::Ret, Self::Error> {
        let mut ctx = Ctx::default().with_orig(orig.clone());

        match self.parse_impl(set, inv, &orig, &mut ctx) {
            Ok(_) => Ok(Return::new(ctx)),
            Err(e) => {
                if e.is_failure() {
                    Ok(Return::new(ctx).with_failure(e))
                } else {
                    Err(e)
                }
            }
        }
    }
}