Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  validator.rs

  Sprache: Rust
 

// Internal
use crate::builder::StyledStr;
use crate::builder::{Arg, ArgGroup, ArgPredicate, Command, PossibleValue};
use crate::error::{Error, Result as ClapResult};
use crate::output::Usage;
use crate::parser::{ArgMatcher, ParseState};
use crate::util::ChildGraph;
use crate::util::FlatMap;
use crate::util::FlatSet;
use crate::util::Id;
use crate::INTERNAL_ERROR_MSG;

pub(cratestruct Validator<'cmd> {
    cmd: &'cmd Command,
    required: ChildGraph<Id>,
}

impl<'cmd> Validator<'cmd> {
    pub(cratefn new(cmd: &'cmd Command) -> Self {
        let required = cmd.required_graph();
        Validator { cmd, required }
    }

    pub(cratefn validate(
        &mut self,
        parse_state: ParseState,
        matcher: &mut ArgMatcher,
    ) -> ClapResult<()> {
        debug!("Validator::validate");
        let conflicts = Conflicts::with_args(self.cmd, matcher);
        let has_subcmd = matcher.subcommand_name().is_some();

        if let ParseState::Opt(a) = parse_state {
            debug!("Validator::validate: needs_val_of={a:?}");

            let o = &self.cmd[&a];
            let should_err = if let Some(v) = matcher.args.get(o.get_id()) {
                v.all_val_groups_empty() && o.get_min_vals() != 0
            } else {
                true
            };
            if should_err {
                return Err(Error::empty_value(
                    self.cmd,
                    &get_possible_values_cli(o)
                        .iter()
                        .filter(|pv| !pv.is_hide_set())
                        .map(|n| n.get_name().to_owned())
                        .collect::<Vec<_>>(),
                    o.to_string(),
                ));
            }
        }

        if !has_subcmd && self.cmd.is_arg_required_else_help_set() {
            let num_user_values = matcher
                .args()
                .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
                .count();
            if num_user_values == 0 {
                let message = self.cmd.write_help_err(false);
                return Err(Error::display_help_error(self.cmd, message));
            }
        }
        if !has_subcmd && self.cmd.is_subcommand_required_set() {
            let bn = self.cmd.get_bin_name_fallback();
            return Err(Error::missing_subcommand(
                self.cmd,
                bn.to_string(),
                self.cmd
                    .all_subcommand_names()
                    .map(|s| s.to_owned())
                    .collect::<Vec<_>>(),
                Usage::new(self.cmd)
                    .required(&self.required)
                    .create_usage_with_title(&[]),
            ));
        }

        ok!(self.validate_conflicts(matcher, &conflicts));
        if !(self.cmd.is_subcommand_negates_reqs_set() && has_subcmd) {
            ok!(self.validate_required(matcher, &conflicts));
        }

        Ok(())
    }

    fn validate_conflicts(
        &mut self,
        matcher: &ArgMatcher,
        conflicts: &Conflicts,
    ) -> ClapResult<()> {
        debug!("Validator::validate_conflicts");

        ok!(self.validate_exclusive(matcher));

        for (arg_id, _) in matcher
            .args()
            .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
            .filter(|(arg_id, _)| self.cmd.find(arg_id).is_some())
        {
            debug!("Validator::validate_conflicts::iter: id={arg_id:?}");
            let conflicts = conflicts.gather_conflicts(self.cmd, arg_id);
            ok!(self.build_conflict_err(arg_id, &conflicts, matcher));
        }

        Ok(())
    }

    fn validate_exclusive(&self, matcher: &ArgMatcher) -> ClapResult<()> {
        debug!("Validator::validate_exclusive");
        let args_count = matcher
            .args()
            .filter(|(arg_id, matched)| {
                matched.check_explicit(&ArgPredicate::IsPresent)
                    // Avoid including our own groups by checking none of them.  If a group is present, the
                    // args for the group will be.
                    && self.cmd.find(arg_id).is_some()
            })
            .count();
        if args_count <= 1 {
            // Nothing present to conflict with
            return Ok(());
        }

        matcher
            .args()
            .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
            .find_map(|(id, _)| {
                debug!("Validator::validate_exclusive:iter:{id:?}");
                self.cmd
                    .find(id)
                    // Find `arg`s which are exclusive but also appear with other args.
                    .filter(|&arg| arg.is_exclusive_set() && args_count > 1)
            })
            .map(|arg| {
                // Throw an error for the first conflict found.
                Err(Error::argument_conflict(
                    self.cmd,
                    arg.to_string(),
                    Vec::new(),
                    Usage::new(self.cmd)
                        .required(&self.required)
                        .create_usage_with_title(&[]),
                ))
            })
            .unwrap_or(Ok(()))
    }

    fn build_conflict_err(
        &self,
        name: &Id,
        conflict_ids: &[Id],
        matcher: &ArgMatcher,
    ) -> ClapResult<()> {
        if conflict_ids.is_empty() {
            return Ok(());
        }

        debug!("Validator::build_conflict_err: name={name:?}");
        let mut seen = FlatSet::new();
        let conflicts = conflict_ids
            .iter()
            .flat_map(|c_id| {
                if self.cmd.find_group(c_id).is_some() {
                    self.cmd.unroll_args_in_group(c_id)
                } else {
                    vec![c_id.clone()]
                }
            })
            .filter_map(|c_id| {
                seen.insert(c_id.clone()).then(|| {
                    let c_arg = self.cmd.find(&c_id).expect(INTERNAL_ERROR_MSG);
                    c_arg.to_string()
                })
            })
            .collect();

        let former_arg = self.cmd.find(name).expect(INTERNAL_ERROR_MSG);
        let usg = self.build_conflict_err_usage(matcher, conflict_ids);
        Err(Error::argument_conflict(
            self.cmd,
            former_arg.to_string(),
            conflicts,
            usg,
        ))
    }

    fn build_conflict_err_usage(
        &self,
        matcher: &ArgMatcher,
        conflicting_keys: &[Id],
    ) -> Option<StyledStr> {
        let used_filtered: Vec<Id> = matcher
            .args()
            .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
            .map(|(n, _)| n)
            .filter(|n| {
                // Filter out the args we don't want to specify.
                self.cmd
                    .find(n)
                    .map(|a| !a.is_hide_set())
                    .unwrap_or_default()
            })
            .filter(|key| !conflicting_keys.contains(key))
            .cloned()
            .collect();
        let required: Vec<Id> = used_filtered
            .iter()
            .filter_map(|key| self.cmd.find(key))
            .flat_map(|arg| arg.requires.iter().map(|item| &item.1))
            .filter(|key| !used_filtered.contains(key) && !conflicting_keys.contains(key))
            .chain(used_filtered.iter())
            .cloned()
            .collect();
        Usage::new(self.cmd)
            .required(&self.required)
            .create_usage_with_title(&required)
    }

    fn gather_requires(&mut self, matcher: &ArgMatcher) {
        debug!("Validator::gather_requires");
        for (name, matched) in matcher
            .args()
            .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
        {
            debug!("Validator::gather_requires:iter:{name:?}");
            if let Some(arg) = self.cmd.find(name) {
                let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option<Id> {
                    let required = matched.check_explicit(val);
                    required.then(|| req_arg.clone())
                };

                for req in self.cmd.unroll_arg_requires(is_relevant, arg.get_id()) {
                    self.required.insert(req);
                }
            } else if let Some(g) = self.cmd.find_group(name) {
                debug!("Validator::gather_requires:iter:{name:?}:group");
                for r in &g.requires {
                    self.required.insert(r.clone());
                }
            }
        }
    }

    fn validate_required(&mut self, matcher: &ArgMatcher, conflicts: &Conflicts) -> ClapResult<()> {
        debug!("Validator::validate_required: required={:?}"self.required);
        self.gather_requires(matcher);

        let mut missing_required = Vec::new();
        let mut highest_index = 0;

        let is_exclusive_present = matcher
            .args()
            .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
            .any(|(id, _)| {
                self.cmd
                    .find(id)
                    .map(|arg| arg.is_exclusive_set())
                    .unwrap_or_default()
            });
        debug!("Validator::validate_required: is_exclusive_present={is_exclusive_present}");

        for arg_or_group in self
            .required
            .iter()
            .filter(|r| !matcher.check_explicit(r, &ArgPredicate::IsPresent))
        {
            debug!("Validator::validate_required:iter:aog={arg_or_group:?}");
            if let Some(arg) = self.cmd.find(arg_or_group) {
                debug!("Validator::validate_required:iter: This is an arg");
                if !is_exclusive_present && !self.is_missing_required_ok(arg, conflicts) {
                    debug!(
                        "Validator::validate_required:iter: Missing {:?}",
                        arg.get_id()
                    );
                    missing_required.push(arg.get_id().clone());
                    if !arg.is_last_set() {
                        highest_index = highest_index.max(arg.get_index().unwrap_or(0));
                    }
                }
            } else if let Some(group) = self.cmd.find_group(arg_or_group) {
                debug!("Validator::validate_required:iter: This is a group");
                if !self
                    .cmd
                    .unroll_args_in_group(&group.id)
                    .iter()
                    .any(|a| matcher.check_explicit(a, &ArgPredicate::IsPresent))
                {
                    debug!(
                        "Validator::validate_required:iter: Missing {:?}",
                        group.get_id()
                    );
                    missing_required.push(group.get_id().clone());
                }
            }
        }

        // Validate the conditionally required args
        for a in self
            .cmd
            .get_arguments()
            .filter(|a| !matcher.check_explicit(a.get_id(), &ArgPredicate::IsPresent))
        {
            let mut required = false;

            for (other, val) in &a.r_ifs {
                if matcher.check_explicit(other, &ArgPredicate::Equals(val.into())) {
                    debug!(
                        "Validator::validate_required:iter: Missing {:?}",
                        a.get_id()
                    );
                    required = true;
                }
            }

            let match_all = a.r_ifs_all.iter().all(|(other, val)| {
                matcher.check_explicit(other, &ArgPredicate::Equals(val.into()))
            });
            if match_all && !a.r_ifs_all.is_empty() {
                debug!(
                    "Validator::validate_required:iter: Missing {:?}",
                    a.get_id()
                );
                required = true;
            }

            if (!a.r_unless.is_empty() || !a.r_unless_all.is_empty())
                && self.fails_arg_required_unless(a, matcher)
            {
                debug!(
                    "Validator::validate_required:iter: Missing {:?}",
                    a.get_id()
                );
                required = true;
            }

            if !is_exclusive_present && required {
                missing_required.push(a.get_id().clone());
                if !a.is_last_set() {
                    highest_index = highest_index.max(a.get_index().unwrap_or(0));
                }
            }
        }

        // For display purposes, include all of the preceding positional arguments
        if !self.cmd.is_allow_missing_positional_set() {
            for pos in self
                .cmd
                .get_positionals()
                .filter(|a| !matcher.check_explicit(a.get_id(), &ArgPredicate::IsPresent))
            {
                if pos.get_index() < Some(highest_index) {
                    debug!(
                        "Validator::validate_required:iter: Missing {:?}",
                        pos.get_id()
                    );
                    missing_required.push(pos.get_id().clone());
                }
            }
        }

        if !missing_required.is_empty() {
            ok!(self.missing_required_error(matcher, missing_required));
        }

        Ok(())
    }

    fn is_missing_required_ok(&self, a: &Arg, conflicts: &Conflicts) -> bool {
        debug!("Validator::is_missing_required_ok: {}", a.get_id());
        if !conflicts.gather_conflicts(self.cmd, a.get_id()).is_empty() {
            debug!("Validator::is_missing_required_ok: true (self)");
            return true;
        }
        for group_id in self.cmd.groups_for_arg(a.get_id()) {
            if !conflicts.gather_conflicts(self.cmd, &group_id).is_empty() {
                debug!("Validator::is_missing_required_ok: true ({group_id})");
                return true;
            }
        }
        false
    }

    // Failing a required unless means, the arg's "unless" wasn't present, and neither were they
    fn fails_arg_required_unless(&self, a: &Arg, matcher: &ArgMatcher) -> bool {
        debug!("Validator::fails_arg_required_unless: a={:?}", a.get_id());
        let exists = |id| matcher.check_explicit(id, &ArgPredicate::IsPresent);

        (a.r_unless_all.is_empty() || !a.r_unless_all.iter().all(exists))
            && !a.r_unless.iter().any(exists)
    }

    // `req_args`: an arg to include in the error even if not used
    fn missing_required_error(
        &self,
        matcher: &ArgMatcher,
        raw_req_args: Vec<Id>,
    ) -> ClapResult<()> {
        debug!("Validator::missing_required_error; incl={raw_req_args:?}");
        debug!(
            "Validator::missing_required_error: reqs={:?}",
            self.required
        );

        let usg = Usage::new(self.cmd).required(&self.required);

        let req_args = {
            #[cfg(feature = "usage")]
            {
                usg.get_required_usage_from(&raw_req_args, Some(matcher), true)
                    .into_iter()
                    .map(|s| s.to_string())
                    .collect::<Vec<_>>()
            }

            #[cfg(not(feature = "usage"))]
            {
                raw_req_args
                    .iter()
                    .map(|id| {
                        if let Some(arg) = self.cmd.find(id) {
                            arg.to_string()
                        } else if let Some(_group) = self.cmd.find_group(id) {
                            self.cmd.format_group(id).to_string()
                        } else {
                            debug_assert!(false"id={id:?} is unknown");
                            "".to_owned()
                        }
                    })
                    .collect::<FlatSet<_>>()
                    .into_iter()
                    .collect::<Vec<_>>()
            }
        };

        debug!("Validator::missing_required_error: req_args={req_args:#?}");

        let used: Vec<Id> = matcher
            .args()
            .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
            .map(|(n, _)| n)
            .filter(|n| {
                // Filter out the args we don't want to specify.
                self.cmd
                    .find(n)
                    .map(|a| !a.is_hide_set())
                    .unwrap_or_default()
            })
            .cloned()
            .chain(raw_req_args)
            .collect();

        Err(Error::missing_required_argument(
            self.cmd,
            req_args,
            usg.create_usage_with_title(&used),
        ))
    }
}

#[derive(Default, Clone, Debug)]
struct Conflicts {
    potential: FlatMap<Id, Vec<Id>>,
}

impl Conflicts {
    fn with_args(cmd: &Command, matcher: &ArgMatcher) -> Self {
        let mut potential = FlatMap::new();
        potential.extend_unchecked(
            matcher
                .args()
                .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
                .map(|(id, _)| {
                    let conf = gather_direct_conflicts(cmd, id);
                    (id.clone(), conf)
                }),
        );
        Self { potential }
    }

    fn gather_conflicts(&self, cmd: &Command, arg_id: &Id) -> Vec<Id> {
        debug!("Conflicts::gather_conflicts: arg={arg_id:?}");
        let mut conflicts = Vec::new();

        let arg_id_conflicts_storage;
        let arg_id_conflicts = if let Some(arg_id_conflicts) = self.get_direct_conflicts(arg_id) {
            arg_id_conflicts
        } else {
            // `is_missing_required_ok` is a case where we check not-present args for conflicts
            arg_id_conflicts_storage = gather_direct_conflicts(cmd, arg_id);
            &arg_id_conflicts_storage
        };
        for (other_arg_id, other_arg_id_conflicts) in self.potential.iter() {
            if arg_id == other_arg_id {
                continue;
            }

            if arg_id_conflicts.contains(other_arg_id) {
                conflicts.push(other_arg_id.clone());
            }
            if other_arg_id_conflicts.contains(arg_id) {
                conflicts.push(other_arg_id.clone());
            }
        }

        debug!("Conflicts::gather_conflicts: conflicts={conflicts:?}");
        conflicts
    }

    fn get_direct_conflicts(&self, arg_id: &Id) -> Option<&[Id]> {
        self.potential.get(arg_id).map(Vec::as_slice)
    }
}

fn gather_direct_conflicts(cmd: &Command, id: &Id) -> Vec<Id> {
    let conf = if let Some(arg) = cmd.find(id) {
        gather_arg_direct_conflicts(cmd, arg)
    } else if let Some(group) = cmd.find_group(id) {
        gather_group_direct_conflicts(group)
    } else {
        debug_assert!(false"id={id:?} is unknown");
        Vec::new()
    };
    debug!("Conflicts::gather_direct_conflicts id={id:?}, conflicts={conf:?}",);
    conf
}

fn gather_arg_direct_conflicts(cmd: &Command, arg: &Arg) -> Vec<Id> {
    let mut conf = arg.blacklist.clone();
    for group_id in cmd.groups_for_arg(arg.get_id()) {
        let group = cmd.find_group(&group_id).expect(INTERNAL_ERROR_MSG);
        conf.extend(group.conflicts.iter().cloned());
        if !group.multiple {
            for member_id in &group.args {
                if member_id != arg.get_id() {
                    conf.push(member_id.clone());
                }
            }
        }
    }

    // Overrides are implicitly conflicts
    conf.extend(arg.overrides.iter().cloned());

    conf
}

fn gather_group_direct_conflicts(group: &ArgGroup) -> Vec<Id> {
    group.conflicts.clone()
}

pub(cratefn get_possible_values_cli(a: &Arg) -> Vec<PossibleValue> {
    if !a.is_takes_value_set() {
        vec![]
    } else {
        a.get_value_parser()
            .possible_values()
            .map(|pvs| pvs.collect())
            .unwrap_or_default()
    }
}

Messung V0.5 in Prozent
C=98 H=100 G=98

¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet am  2026-06-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik