diff options
Diffstat (limited to 'src/force.rs')
| -rw-r--r-- | src/force.rs | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/src/force.rs b/src/force.rs new file mode 100644 index 0000000..40f0ffc --- /dev/null +++ b/src/force.rs @@ -0,0 +1,72 @@ +//! `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<Item = &'a str>, + known: &[&'static str], + ) -> Result<Self, String> { + 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()); + } +} |
