vrl/stdlib/
to_bool.rs

1use crate::compiler::conversion::Conversion;
2use crate::compiler::prelude::*;
3
4fn to_bool(value: Value) -> Resolved {
5    use Value::{Boolean, Bytes, Float, Integer, Null};
6
7    match value {
8        Boolean(_) => Ok(value),
9        Integer(v) => Ok(Boolean(v != 0)),
10        Float(v) => Ok(Boolean(v != 0.0)),
11        Null => Ok(Boolean(false)),
12        Bytes(v) => Conversion::Boolean
13            .convert(v)
14            .map_err(|e| e.to_string().into()),
15        v => Err(format!("unable to coerce {} into boolean", v.kind()).into()),
16    }
17}
18
19#[derive(Clone, Copy, Debug)]
20pub struct ToBool;
21
22impl Function for ToBool {
23    fn identifier(&self) -> &'static str {
24        "to_bool"
25    }
26
27    fn usage(&self) -> &'static str {
28        "Coerces the `value` into a boolean."
29    }
30
31    fn category(&self) -> &'static str {
32        Category::Coerce.as_ref()
33    }
34
35    fn internal_failure_reasons(&self) -> &'static [&'static str] {
36        &["`value` is not a supported boolean representation."]
37    }
38
39    fn return_kind(&self) -> u16 {
40        kind::BOOLEAN
41    }
42
43    fn return_rules(&self) -> &'static [&'static str] {
44        &[
45            "If `value` is `\"true\"`, `\"t\"`, `\"yes\"`, or `\"y\"`, `true` is returned.",
46            "If `value` is `\"false\"`, `\"f\"`, `\"no\"`, `\"n\"`, or `\"0\"`, `false` is returned.",
47            "If `value` is `0.0`, `false` is returned, otherwise `true` is returned.",
48            "If `value` is `0`, `false` is returned, otherwise `true` is returned.",
49            "If `value` is `null`, `false` is returned.",
50            "If `value` is a Boolean, it's returned unchanged.",
51        ]
52    }
53
54    fn parameters(&self) -> &'static [Parameter] {
55        const PARAMETERS: &[Parameter] = &[Parameter::required(
56            "value",
57            kind::ANY,
58            "The value to convert to a Boolean.",
59        )];
60        PARAMETERS
61    }
62
63    #[allow(clippy::too_many_lines)]
64    fn examples(&self) -> &'static [Example] {
65        &[
66            example! {
67                title: "Coerce to a Boolean (string)",
68                source: "to_bool!(\"yes\")",
69                result: Ok("true"),
70            },
71            example! {
72                title: "Coerce to a Boolean (float)",
73                source: "to_bool(0.0)",
74                result: Ok("false"),
75            },
76            example! {
77                title: "Coerce to a Boolean (int)",
78                source: "to_bool(0)",
79                result: Ok("false"),
80            },
81            example! {
82                title: "Coerce to a Boolean (null)",
83                source: "to_bool(null)",
84                result: Ok("false"),
85            },
86            example! {
87                title: "Coerce to a Boolean (Boolean)",
88                source: "to_bool(true)",
89                result: Ok("true"),
90            },
91            example! {
92                title: "Integer (other)",
93                source: "to_bool(2)",
94                result: Ok("true"),
95            },
96            example! {
97                title: "Float (other)",
98                source: "to_bool(5.6)",
99                result: Ok("true"),
100            },
101            example! {
102                title: "False",
103                source: "to_bool(false)",
104                result: Ok("false"),
105            },
106            example! {
107                title: "True string",
108                source: "to_bool!(s'true')",
109                result: Ok("true"),
110            },
111            example! {
112                title: "Y string",
113                source: "to_bool!(s'y')",
114                result: Ok("true"),
115            },
116            example! {
117                title: "Non-zero integer string",
118                source: "to_bool!(s'1')",
119                result: Ok("true"),
120            },
121            example! {
122                title: "False string",
123                source: "to_bool!(s'false')",
124                result: Ok("false"),
125            },
126            example! {
127                title: "No string",
128                source: "to_bool!(s'no')",
129                result: Ok("false"),
130            },
131            example! {
132                title: "N string",
133                source: "to_bool!(s'n')",
134                result: Ok("false"),
135            },
136            example! {
137                title: "Invalid string",
138                source: "to_bool!(s'foobar')",
139                result: Err(
140                    r#"function call error for "to_bool" at (0:19): Invalid boolean value "foobar""#,
141                ),
142            },
143            example! {
144                title: "Timestamp",
145                source: "to_bool!(t'2020-01-01T00:00:00Z')",
146                result: Err(
147                    r#"function call error for "to_bool" at (0:33): unable to coerce timestamp into boolean"#,
148                ),
149            },
150            example! {
151                title: "Array",
152                source: "to_bool!([])",
153                result: Err(
154                    r#"function call error for "to_bool" at (0:12): unable to coerce array into boolean"#,
155                ),
156            },
157            example! {
158                title: "Object",
159                source: "to_bool!({})",
160                result: Err(
161                    r#"function call error for "to_bool" at (0:12): unable to coerce object into boolean"#,
162                ),
163            },
164            example! {
165                title: "Regex",
166                source: "to_bool!(r'foo')",
167                result: Err(
168                    r#"function call error for "to_bool" at (0:16): unable to coerce regex into boolean"#,
169                ),
170            },
171        ]
172    }
173
174    fn compile(
175        &self,
176        _state: &state::TypeState,
177        _ctx: &mut FunctionCompileContext,
178        arguments: ArgumentList,
179    ) -> Compiled {
180        let value = arguments.required("value");
181
182        Ok(ToBoolFn { value }.as_expr())
183    }
184}
185
186#[derive(Debug, Clone)]
187struct ToBoolFn {
188    value: Box<dyn Expression>,
189}
190
191impl FunctionExpression for ToBoolFn {
192    fn resolve(&self, ctx: &mut Context) -> Resolved {
193        let value = self.value.resolve(ctx)?;
194
195        to_bool(value)
196    }
197
198    fn type_def(&self, state: &state::TypeState) -> TypeDef {
199        let td = self.value.type_def(state);
200
201        TypeDef::boolean().maybe_fallible(
202            td.contains_bytes()
203                || td.contains_timestamp()
204                || td.contains_array()
205                || td.contains_object()
206                || td.contains_regex(),
207        )
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214
215    test_function![
216        to_bool => ToBool;
217
218        string_true {
219            args: func_args![value: "true"],
220            want: Ok(true),
221            tdef: TypeDef::boolean().fallible(),
222        }
223
224        string_false {
225            args: func_args![value: "no"],
226            want: Ok(false),
227            tdef: TypeDef::boolean().fallible(),
228        }
229
230        string_error {
231            args: func_args![value: "cabbage"],
232            want: Err(r#"Invalid boolean value "cabbage""#),
233            tdef: TypeDef::boolean().fallible(),
234        }
235
236        number_true {
237            args: func_args![value: 20],
238            want: Ok(true),
239            tdef: TypeDef::boolean().infallible(),
240        }
241
242        number_false {
243            args: func_args![value: 0],
244            want: Ok(false),
245            tdef: TypeDef::boolean().infallible(),
246        }
247    ];
248}