1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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());
}
}
|