vrl/stdlib/
includes.rs

1use crate::compiler::prelude::*;
2
3fn includes(list: Value, item: &Value) -> Resolved {
4    let list = list.try_array()?;
5    let included = list.iter().any(|i| i == item);
6    Ok(included.into())
7}
8
9#[derive(Clone, Copy, Debug)]
10pub struct Includes;
11
12impl Function for Includes {
13    fn identifier(&self) -> &'static str {
14        "includes"
15    }
16
17    fn usage(&self) -> &'static str {
18        "Determines whether the `value` array includes the specified `item`."
19    }
20
21    fn category(&self) -> &'static str {
22        Category::Enumerate.as_ref()
23    }
24
25    fn return_kind(&self) -> u16 {
26        kind::BOOLEAN
27    }
28
29    fn parameters(&self) -> &'static [Parameter] {
30        const PARAMETERS: &[Parameter] = &[
31            Parameter::required("value", kind::ARRAY, "The array."),
32            Parameter::required("item", kind::ANY, "The item to check."),
33        ];
34        PARAMETERS
35    }
36
37    fn examples(&self) -> &'static [Example] {
38        &[
39            example! {
40                title: "Array includes",
41                source: r#"includes(["apple", "orange", "banana"], "banana")"#,
42                result: Ok("true"),
43            },
44            example! {
45                title: "Includes boolean",
46                source: "includes([1, true], true)",
47                result: Ok("true"),
48            },
49            example! {
50                title: "Doesn't include",
51                source: r#"includes(["foo", "bar"], "baz")"#,
52                result: Ok("false"),
53            },
54        ]
55    }
56
57    fn compile(
58        &self,
59        _state: &state::TypeState,
60        _ctx: &mut FunctionCompileContext,
61        arguments: ArgumentList,
62    ) -> Compiled {
63        let value = arguments.required("value");
64        let item = arguments.required("item");
65
66        Ok(IncludesFn { value, item }.as_expr())
67    }
68}
69
70#[derive(Debug, Clone)]
71struct IncludesFn {
72    value: Box<dyn Expression>,
73    item: Box<dyn Expression>,
74}
75
76impl FunctionExpression for IncludesFn {
77    fn resolve(&self, ctx: &mut Context) -> Resolved {
78        let list = self.value.resolve(ctx)?;
79        let item = self.item.resolve(ctx)?;
80
81        includes(list, &item)
82    }
83
84    fn type_def(&self, _: &state::TypeState) -> TypeDef {
85        TypeDef::boolean().infallible()
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use crate::value;
93
94    test_function![
95        includes => Includes;
96
97        empty_not_included {
98            args: func_args![value: value!([]), item: value!("foo")],
99            want: Ok(value!(false)),
100            tdef: TypeDef::boolean().infallible(),
101        }
102
103        string_included {
104            args: func_args![value: value!(["foo", "bar"]), item: value!("foo")],
105            want: Ok(value!(true)),
106            tdef: TypeDef::boolean().infallible(),
107        }
108
109        string_not_included {
110            args: func_args![value: value!(["foo", "bar"]), item: value!("baz")],
111            want: Ok(value!(false)),
112            tdef: TypeDef::boolean().infallible(),
113        }
114
115        bool_included {
116            args: func_args![value: value!([true, false]), item: value!(true)],
117            want: Ok(value!(true)),
118            tdef: TypeDef::boolean().infallible(),
119        }
120
121        bool_not_included {
122            args: func_args![value: value!([true, true]), item: value!(false)],
123            want: Ok(value!(false)),
124            tdef: TypeDef::boolean().infallible(),
125        }
126
127        integer_included {
128            args: func_args![value: value!([1, 2, 3, 4, 5]), item: value!(5)],
129            want: Ok(value!(true)),
130            tdef: TypeDef::boolean().infallible(),
131        }
132
133        integer_not_included {
134            args: func_args![value: value!([1, 2, 3, 4, 6]), item: value!(5)],
135            want: Ok(value!(false)),
136            tdef: TypeDef::boolean().infallible(),
137        }
138
139        float_included {
140            args: func_args![value: value!([0.5, 12.1, 13.075]), item: value!(13.075)],
141            want: Ok(value!(true)),
142            tdef: TypeDef::boolean().infallible(),
143        }
144
145        float_not_included {
146            args: func_args![value: value!([0.5, 12.1, 13.075]), item: value!(471.0)],
147            want: Ok(value!(false)),
148            tdef: TypeDef::boolean().infallible(),
149        }
150
151        array_included {
152            args: func_args![value: value!([[1,2,3], [4,5,6]]), item: value!([1,2,3])],
153            want: Ok(value!(true)),
154            tdef: TypeDef::boolean().infallible(),
155        }
156
157        array_not_included {
158            args: func_args![value: value!([[1,2,3], [4,5,6]]), item: value!([1,2,4])],
159            want: Ok(value!(false)),
160            tdef: TypeDef::boolean().infallible(),
161        }
162
163        mixed_included_string {
164            args: func_args![value: value!(["foo", 1, true, [1,2,3]]), item: value!("foo")],
165            want: Ok(value!(true)),
166            tdef: TypeDef::boolean().infallible(),
167        }
168
169        mixed_not_included_string {
170            args: func_args![value: value!(["bar", 1, true, [1,2,3]]), item: value!("foo")],
171            want: Ok(value!(false)),
172            tdef: TypeDef::boolean().infallible(),
173        }
174
175        mixed_included_bool {
176            args: func_args![value: value!(["foo", 1, true, [1,2,3]]), item: value!(true)],
177            want: Ok(value!(true)),
178            tdef: TypeDef::boolean().infallible(),
179        }
180
181        mixed_not_included_bool {
182            args: func_args![value: value!(["foo", 1, true, [1,2,3]]), item: value!(false)],
183            want: Ok(value!(false)),
184            tdef: TypeDef::boolean().infallible(),
185        }
186    ];
187}