1use crate::compiler::prelude::*;
2
3fn is_empty(value: Value) -> Resolved {
4 let empty = match value {
5 Value::Object(v) => v.is_empty(),
6 Value::Array(v) => v.is_empty(),
7 Value::Bytes(v) => v.is_empty(),
8 value => {
9 return Err(ValueError::Expected {
10 got: value.kind(),
11 expected: Kind::array(Collection::any())
12 | Kind::object(Collection::any())
13 | Kind::bytes(),
14 }
15 .into());
16 }
17 };
18
19 Ok(empty.into())
20}
21
22#[derive(Clone, Copy, Debug)]
23pub struct IsEmpty;
24
25impl Function for IsEmpty {
26 fn identifier(&self) -> &'static str {
27 "is_empty"
28 }
29
30 fn usage(&self) -> &'static str {
31 "Check if the object, array, or string has a length of `0`."
32 }
33
34 fn category(&self) -> &'static str {
35 Category::Type.as_ref()
36 }
37
38 fn return_kind(&self) -> u16 {
39 kind::BOOLEAN
40 }
41
42 fn return_rules(&self) -> &'static [&'static str] {
43 &[
44 "Returns `true` if `value` is empty.",
45 "Returns `false` if `value` is non-empty.",
46 ]
47 }
48
49 fn parameters(&self) -> &'static [Parameter] {
50 const PARAMETERS: &[Parameter] = &[Parameter::required(
51 "value",
52 kind::OBJECT | kind::ARRAY | kind::BYTES,
53 "The value to check.",
54 )];
55 PARAMETERS
56 }
57
58 fn examples(&self) -> &'static [Example] {
59 &[
60 example! {
61 title: "Empty array",
62 source: "is_empty([])",
63 result: Ok("true"),
64 },
65 example! {
66 title: "Non-empty string",
67 source: r#"is_empty("a string")"#,
68 result: Ok("false"),
69 },
70 example! {
71 title: "Non-empty object",
72 source: r#"is_empty({"foo": "bar"})"#,
73 result: Ok("false"),
74 },
75 example! {
76 title: "Empty string",
77 source: r#"is_empty("")"#,
78 result: Ok("true"),
79 },
80 example! {
81 title: "Empty object",
82 source: "is_empty({})",
83 result: Ok("true"),
84 },
85 example! {
86 title: "Non-empty array",
87 source: "is_empty([1,2,3])",
88 result: Ok("false"),
89 },
90 ]
91 }
92
93 fn compile(
94 &self,
95 _state: &state::TypeState,
96 _ctx: &mut FunctionCompileContext,
97 arguments: ArgumentList,
98 ) -> Compiled {
99 let value = arguments.required("value");
100
101 Ok(IsEmptyFn { value }.as_expr())
102 }
103}
104
105#[derive(Debug, Clone)]
106struct IsEmptyFn {
107 value: Box<dyn Expression>,
108}
109
110impl FunctionExpression for IsEmptyFn {
111 fn resolve(&self, ctx: &mut Context) -> Resolved {
112 let value = self.value.resolve(ctx)?;
113 is_empty(value)
114 }
115
116 fn type_def(&self, _: &state::TypeState) -> TypeDef {
117 TypeDef::boolean().infallible()
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use crate::value;
125
126 test_function![
127 is_empty => IsEmpty;
128
129 empty_array {
130 args: func_args![value: value!([])],
131 want: Ok(value!(true)),
132 tdef: TypeDef::boolean().infallible(),
133 }
134
135 non_empty_array {
136 args: func_args![value: value!(["foo"])],
137 want: Ok(value!(false)),
138 tdef: TypeDef::boolean().infallible(),
139 }
140
141 empty_object {
142 args: func_args![value: value!({})],
143 want: Ok(value!(true)),
144 tdef: TypeDef::boolean().infallible(),
145 }
146
147 non_empty_object {
148 args: func_args![value: value!({"foo": "bar"})],
149 want: Ok(value!(false)),
150 tdef: TypeDef::boolean().infallible(),
151 }
152
153 empty_string {
154 args: func_args![value: ""],
155 want: Ok(value!(true)),
156 tdef: TypeDef::boolean().infallible(),
157 }
158
159 non_empty_string {
160 args: func_args![value: "foo"],
161 want: Ok(value!(false)),
162 tdef: TypeDef::boolean().infallible(),
163 }
164 ];
165}