//! `force`: overriding checks that otherwise refuse. Conflicts are errors by default; //! each check that can be overridden has a name, and `force` lists the names to //! override, or is `true` for all of them (`false`, or nothing, for none). //! //! The same format everywhere: the `?force=` query parameter of the API and the //! `--force` option. Names are camelCase, like the API's fields (`switchDelaySecs`), //! on the command line too. An unknown name is an error, so a typo can't silently //! override nothing. /// The checks a `force` value overrides. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub(crate) struct Force { all: bool, names: Vec<&'static str>, } impl Force { /// Parses `values` (each `true`, `false`, or a comma-separated list of names), /// allowing only the names in `known`. pub(crate) fn parse<'a>( values: impl IntoIterator, known: &[&'static str], ) -> Result { let mut force = Force::default(); for value in values { for name in value.split(',').map(str::trim) { match name { "" | "false" => {} "true" => force.all = true, name => match known.iter().find(|known| **known == name) { Some(known) => force.names.push(known), None => { return Err(format!( "unknown force {name:?}: use true, or a list of {}", known.join(", ") )); } }, } } } Ok(force) } /// Whether the check `name` is overridden. pub(crate) fn allows(&self, name: &str) -> bool { self.all || self.names.contains(&name) } } #[cfg(test)] mod tests { use super::*; const KNOWN: &[&str] = &["switchDelaySecs", "supersede"]; #[test] fn all_some_or_none() { let all = Force::parse(["true"], KNOWN).unwrap(); assert!(all.allows("supersede") && all.allows("switchDelaySecs")); let some = Force::parse(["supersede"], KNOWN).unwrap(); assert!(some.allows("supersede") && !some.allows("switchDelaySecs")); let list = Force::parse(["switchDelaySecs, supersede"], KNOWN).unwrap(); assert!(list.allows("supersede") && list.allows("switchDelaySecs")); for none in [vec![], vec!["false"], vec![""]] { assert!(!Force::parse(none, KNOWN).unwrap().allows("supersede")); } assert!(Force::parse(["swarm-id"], KNOWN).is_err()); } }