1use crate::compiler::prelude::*;
2
3fn strlen(value: &Value) -> Resolved {
4 let v = value.try_bytes_utf8_lossy()?;
5
6 Ok(v.chars().count().into())
7}
8
9#[derive(Clone, Copy, Debug)]
10pub struct Strlen;
11
12impl Function for Strlen {
13 fn identifier(&self) -> &'static str {
14 "strlen"
15 }
16
17 fn usage(&self) -> &'static str {
18 indoc! {"
19 Returns the number of UTF-8 characters in `value`. This differs from
20 `length` which counts the number of bytes of a string.
21
22 **Note**: This is the count of [Unicode scalar values](https://www.unicode.org/glossary/#unicode_scalar_value)
23 which can sometimes differ from [Unicode code points](https://www.unicode.org/glossary/#code_point).
24 "}
25 }
26
27 fn category(&self) -> &'static str {
28 Category::Enumerate.as_ref()
29 }
30
31 fn return_kind(&self) -> u16 {
32 kind::INTEGER
33 }
34
35 fn parameters(&self) -> &'static [Parameter] {
36 const PARAMETERS: &[Parameter] =
37 &[Parameter::required("value", kind::BYTES, "The string.")];
38 PARAMETERS
39 }
40
41 fn examples(&self) -> &'static [Example] {
42 &[example! {
43 title: "Count Unicode scalar values",
44 source: r#"strlen("ñandú")"#,
45 result: Ok("5"),
46 }]
47 }
48
49 fn compile(
50 &self,
51 _state: &state::TypeState,
52 _ctx: &mut FunctionCompileContext,
53 arguments: ArgumentList,
54 ) -> Compiled {
55 let value = arguments.required("value");
56
57 Ok(StrlenFn { value }.as_expr())
58 }
59}
60
61#[derive(Debug, Clone)]
62struct StrlenFn {
63 value: Box<dyn Expression>,
64}
65
66impl FunctionExpression for StrlenFn {
67 fn resolve(&self, ctx: &mut Context) -> Resolved {
68 let value = self.value.resolve(ctx)?;
69
70 strlen(&value)
71 }
72
73 fn type_def(&self, _state: &state::TypeState) -> TypeDef {
74 TypeDef::integer().infallible()
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::value;
82
83 test_function![
84 strlen => Strlen;
85
86 string_value {
87 args: func_args![value: value!("ñandú")],
88 want: Ok(value!(5)),
89 tdef: TypeDef::integer().infallible(),
90 }
91 ];
92}