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}