1use crate::compiler::prelude::*;
2
3fn length(value: Value) -> Resolved {
4 match value {
5 Value::Array(v) => Ok(v.len().into()),
6 Value::Object(v) => Ok(v.len().into()),
7 Value::Bytes(v) => Ok(v.len().into()),
8 value => Err(ValueError::Expected {
9 got: value.kind(),
10 expected: Kind::array(Collection::any())
11 | Kind::object(Collection::any())
12 | Kind::bytes(),
13 }
14 .into()),
15 }
16}
17
18#[derive(Clone, Copy, Debug)]
19pub struct Length;
20
21impl Function for Length {
22 fn identifier(&self) -> &'static str {
23 "length"
24 }
25
26 fn usage(&self) -> &'static str {
27 indoc! {"
28 Returns the length of the `value`.
29
30 * If `value` is an array, returns the number of elements.
31 * If `value` is an object, returns the number of top-level keys.
32 * If `value` is a string, returns the number of bytes in the string. If
33 you want the number of characters, see `strlen`.
34 "}
35 }
36
37 fn category(&self) -> &'static str {
38 Category::Enumerate.as_ref()
39 }
40
41 fn return_kind(&self) -> u16 {
42 kind::INTEGER
43 }
44
45 fn return_rules(&self) -> &'static [&'static str] {
46 &[
47 "If `value` is an array, returns the number of elements.",
48 "If `value` is an object, returns the number of top-level keys.",
49 "If `value` is a string, returns the number of bytes in the string.",
50 ]
51 }
52
53 fn parameters(&self) -> &'static [Parameter] {
54 const PARAMETERS: &[Parameter] = &[Parameter::required(
55 "value",
56 kind::ARRAY | kind::OBJECT | kind::BYTES,
57 "The array or object.",
58 )];
59 PARAMETERS
60 }
61
62 fn examples(&self) -> &'static [Example] {
63 &[
64 example! {
65 title: "Length (object)",
66 source: indoc! {r#"
67 length({
68 "portland": "Trail Blazers",
69 "seattle": "Supersonics"
70 })
71 "#},
72 result: Ok("2"),
73 },
74 example! {
75 title: "Length (nested object)",
76 source: indoc! {r#"
77 length({
78 "home": {
79 "city": "Portland",
80 "state": "Oregon"
81 },
82 "name": "Trail Blazers",
83 "mascot": {
84 "name": "Blaze the Trail Cat"
85 }
86 })
87 "#},
88 result: Ok("3"),
89 },
90 example! {
91 title: "Length (array)",
92 source: r#"length(["Trail Blazers", "Supersonics", "Grizzlies"])"#,
93 result: Ok("3"),
94 },
95 example! {
96 title: "Length (string)",
97 source: r#"length("The Planet of the Apes Musical")"#,
98 result: Ok("30"),
99 },
100 ]
101 }
102
103 fn compile(
104 &self,
105 _state: &state::TypeState,
106 _ctx: &mut FunctionCompileContext,
107 arguments: ArgumentList,
108 ) -> Compiled {
109 let value = arguments.required("value");
110
111 Ok(LengthFn { value }.as_expr())
112 }
113}
114
115#[derive(Debug, Clone)]
116struct LengthFn {
117 value: Box<dyn Expression>,
118}
119
120impl FunctionExpression for LengthFn {
121 fn resolve(&self, ctx: &mut Context) -> Resolved {
122 let value = self.value.resolve(ctx)?;
123
124 length(value)
125 }
126
127 fn type_def(&self, _: &state::TypeState) -> TypeDef {
128 TypeDef::integer().infallible()
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::value;
136
137 test_function![
138 length => Length;
139
140 non_empty_object_value {
141 args: func_args![value: value!({"foo": "bar", "baz": true, "baq": [1, 2, 3]})],
142 want: Ok(value!(3)),
143 tdef: TypeDef::integer().infallible(),
144 }
145
146 empty_object_value {
147 args: func_args![value: value!({})],
148 want: Ok(value!(0)),
149 tdef: TypeDef::integer().infallible(),
150 }
151
152 nested_object_value {
153 args: func_args![value: value!({"nested": {"foo": "bar"}})],
154 want: Ok(value!(1)),
155 tdef: TypeDef::integer().infallible(),
156 }
157
158 non_empty_array_value {
159 args: func_args![value: value!([1, 2, 3, 4, true, "hello"])],
160 want: Ok(value!(6)),
161 tdef: TypeDef::integer().infallible(),
162 }
163
164 empty_array_value {
165 args: func_args![value: value!([])],
166 want: Ok(value!(0)),
167 tdef: TypeDef::integer().infallible(),
168 }
169
170 string_value {
171 args: func_args![value: value!("hello world")],
172 want: Ok(value!(11)),
173 tdef: TypeDef::integer().infallible(),
174 }
175 ];
176}