Skip to content

Commit e130171

Browse files
fix(loader): update parse boolean to work with all accepted yaml values (#633)
1 parent 0302134 commit e130171

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

guard/src/rules/libyaml/loader.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ impl Loader {
8787
Ok(i) => MarkedValue::Int(i, location),
8888
Err(_) => match val.parse::<f64>() {
8989
Ok(f) => MarkedValue::Float(f, location),
90-
Err(_) => match val.parse::<bool>() {
91-
Ok(b) => MarkedValue::Bool(b, location),
92-
Err(_) => match val.to_lowercase().as_str() {
90+
Err(_) => match self.parse_bool(&val) {
91+
Some(b) => MarkedValue::Bool(b, location),
92+
None => match val.to_lowercase().as_str() {
9393
"~" | "null" => MarkedValue::Null(location),
9494
_ => MarkedValue::String(val, location),
9595
},
@@ -100,6 +100,23 @@ impl Loader {
100100

101101
self.stack.push(path_value);
102102
}
103+
fn is_bool_true(&self, s: &str) -> bool {
104+
matches!(s, "true" | "yes" | "on" | "y")
105+
}
106+
107+
fn is_bool_false(&self, s: &str) -> bool {
108+
matches!(s, "false" | "no" | "off" | "n")
109+
}
110+
111+
fn parse_bool(&self, val: &str) -> Option<bool> {
112+
if self.is_bool_true(val) {
113+
Some(true)
114+
} else if self.is_bool_false(val) {
115+
Some(false)
116+
} else {
117+
None
118+
}
119+
}
103120

104121
fn handle_sequence_end(&mut self) {
105122
let array_idx = self.last_container_index.pop().unwrap();

guard/src/rules/libyaml/loader_tests.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,48 @@ fn yaml_loader() -> Result<()> {
2828
Ok(())
2929
}
3030

31+
#[rstest::rstest]
32+
#[case::standard_lowercase_true("true", true)]
33+
#[case::standard_capitalized_true("True", true)]
34+
#[case::standard_uppercase_true("TRUE", true)]
35+
#[case::yaml_yes_lowercase("yes", true)]
36+
#[case::yaml_yes_capitalized("Yes", true)]
37+
#[case::yaml_yes_uppercase("YES", true)]
38+
#[case::yaml_on_lowercase("on", true)]
39+
#[case::yaml_on_capitalized("On", true)]
40+
#[case::yaml_on_uppercase("ON", true)]
41+
#[case::yaml_y_lowercase("y", true)]
42+
#[case::yaml_y_uppercase("Y", true)]
43+
#[case::standard_lowercase_false("false", false)]
44+
#[case::standard_capitalized_false("False", false)]
45+
#[case::standard_uppercase_false("FALSE", false)]
46+
#[case::yaml_no_lowercase("no", false)]
47+
#[case::yaml_no_capitalized("No", false)]
48+
#[case::yaml_no_uppercase("NO", false)]
49+
#[case::yaml_off_lowercase("off", false)]
50+
#[case::yaml_off_capitalized("Off", false)]
51+
#[case::yaml_off_uppercase("OFF", false)]
52+
#[case::yaml_n_lowercase("n", false)]
53+
#[case::yaml_n_uppercase("N", false)]
54+
fn test_handle_bool_happy_path(#[case] arg: &str, #[case] expected: bool) -> Result<()> {
55+
let docs = format!("check: {arg}");
56+
57+
let mut loader = Loader::new();
58+
match loader.load(String::from(docs))? {
59+
MarkedValue::Map(map, ..) => {
60+
assert!(map.len() == 1);
61+
let (.., result) = map.first().unwrap();
62+
63+
if let MarkedValue::Bool(result, ..) = *result {
64+
assert_eq!(result, expected);
65+
}
66+
}
67+
_ => unreachable!("this isn't possible"),
68+
}
69+
70+
Ok(())
71+
}
72+
3173
#[test]
3274
fn yaml_loader2() -> Result<()> {
3375
let docs = r###"

0 commit comments

Comments
 (0)