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 parameters(&self) -> &'static [Parameter] {
38 &[Parameter {
39 keyword: "value",
40 kind: kind::ARRAY | kind::OBJECT | kind::BYTES,
41 required: true,
42 }]
43 }
44
45 fn examples(&self) -> &'static [Example] {
46 &[
47 example! {
48 title: "Length (object)",
49 source: r#"length({ "portland": "Trail Blazers", "seattle": "Supersonics" })"#,
50 result: Ok("2"),
51 },
52 example! {
53 title: "Length (nested object)",
54 source: r#"length({ "home": { "city": "Portland", "state": "Oregon" }, "name": "Trail Blazers", "mascot": { "name": "Blaze the Trail Cat" } })"#,
55 result: Ok("3"),
56 },
57 example! {
58 title: "Length (array)",
59 source: r#"length(["Trail Blazers", "Supersonics", "Grizzlies"])"#,
60 result: Ok("3"),
61 },
62 example! {
63 title: "Length (string)",
64 source: r#"length("The Planet of the Apes Musical")"#,
65 result: Ok("30"),
66 },
67 ]
68 }
69
70 fn compile(
71 &self,
72 _state: &state::TypeState,
73 _ctx: &mut FunctionCompileContext,
74 arguments: ArgumentList,
75 ) -> Compiled {
76 let value = arguments.required("value");
77
78 Ok(LengthFn { value }.as_expr())
79 }
80}
81
82#[derive(Debug, Clone)]
83struct LengthFn {
84 value: Box<dyn Expression>,
85}
86
87impl FunctionExpression for LengthFn {
88 fn resolve(&self, ctx: &mut Context) -> Resolved {
89 let value = self.value.resolve(ctx)?;
90
91 length(value)
92 }
93
94 fn type_def(&self, _: &state::TypeState) -> TypeDef {
95 TypeDef::integer().infallible()
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use crate::value;
103
104 test_function![
105 length => Length;
106
107 non_empty_object_value {
108 args: func_args![value: value!({"foo": "bar", "baz": true, "baq": [1, 2, 3]})],
109 want: Ok(value!(3)),
110 tdef: TypeDef::integer().infallible(),
111 }
112
113 empty_object_value {
114 args: func_args![value: value!({})],
115 want: Ok(value!(0)),
116 tdef: TypeDef::integer().infallible(),
117 }
118
119 nested_object_value {
120 args: func_args![value: value!({"nested": {"foo": "bar"}})],
121 want: Ok(value!(1)),
122 tdef: TypeDef::integer().infallible(),
123 }
124
125 non_empty_array_value {
126 args: func_args![value: value!([1, 2, 3, 4, true, "hello"])],
127 want: Ok(value!(6)),
128 tdef: TypeDef::integer().infallible(),
129 }
130
131 empty_array_value {
132 args: func_args![value: value!([])],
133 want: Ok(value!(0)),
134 tdef: TypeDef::integer().infallible(),
135 }
136
137 string_value {
138 args: func_args![value: value!("hello world")],
139 want: Ok(value!(11)),
140 tdef: TypeDef::integer().infallible(),
141 }
142 ];
143}