1use super::util;
2use crate::compiler::prelude::*;
3
4fn is_nullish(value: &Value) -> bool {
5 util::is_nullish(value)
6}
7
8#[derive(Clone, Copy, Debug)]
9pub struct IsNullish;
10
11impl Function for IsNullish {
12 fn identifier(&self) -> &'static str {
13 "is_nullish"
14 }
15
16 fn usage(&self) -> &'static str {
17 r#"Determines whether `value` is nullish. Returns `true` if the specified `value` is `null`, an empty string, a string containing only whitespace, or the string `"-"`. Returns `false` otherwise."#
18 }
19
20 fn category(&self) -> &'static str {
21 Category::Type.as_ref()
22 }
23
24 fn return_kind(&self) -> u16 {
25 kind::BOOLEAN
26 }
27
28 fn return_rules(&self) -> &'static [&'static str] {
29 &[
30 "Returns `true` if `value` is `null`.",
31 "Returns `true` if `value` is `\"-\"`.",
32 "Returns `true` if `value` is whitespace as defined by [Unicode `White_Space` property](https://en.wikipedia.org/wiki/Unicode_character_property#Whitespace).",
33 "Returns `false` if `value` is anything else.",
34 ]
35 }
36
37 fn notices(&self) -> &'static [&'static str] {
38 &[indoc! {r#"
39 This function behaves inconsistently: it returns `false` for empty arrays (`[]`) and
40 objects (`{}`), but `true` for empty strings (`""`) and `null`.
41 "#}]
42 }
43
44 fn parameters(&self) -> &'static [Parameter] {
45 const PARAMETERS: &[Parameter] = &[Parameter::required(
46 "value",
47 kind::ANY,
48 "The value to check for nullishness, for example, a useless value.",
49 )];
50 PARAMETERS
51 }
52
53 fn examples(&self) -> &'static [Example] {
54 &[
55 example! {
56 title: "Null detection (blank string)",
57 source: r#"is_nullish("")"#,
58 result: Ok("true"),
59 },
60 example! {
61 title: "Null detection (dash string)",
62 source: r#"is_nullish("-")"#,
63 result: Ok("true"),
64 },
65 example! {
66 title: "Null detection (whitespace)",
67 source: "is_nullish(\"\n \n\")",
68 result: Ok("true"),
69 },
70 example! {
71 title: "Null",
72 source: "is_nullish(null)",
73 result: Ok("true"),
74 },
75 ]
76 }
77
78 fn compile(
79 &self,
80 _state: &state::TypeState,
81 _ctx: &mut FunctionCompileContext,
82 arguments: ArgumentList,
83 ) -> Compiled {
84 let value = arguments.required("value");
85 Ok(IsNullishFn { value }.as_expr())
86 }
87}
88
89#[derive(Clone, Debug)]
90struct IsNullishFn {
91 value: Box<dyn Expression>,
92}
93
94impl FunctionExpression for IsNullishFn {
95 fn resolve(&self, ctx: &mut Context) -> Resolved {
96 let value = self.value.resolve(ctx)?;
97 Ok(is_nullish(&value).into())
98 }
99
100 fn type_def(&self, _: &state::TypeState) -> TypeDef {
101 TypeDef::boolean().infallible()
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::value;
109 test_function![
110 is_nullish => IsNullish;
111
112 empty_string {
113 args: func_args![value: value!("")],
114 want: Ok(value!(true)),
115 tdef: TypeDef::boolean().infallible(),
116 }
117
118 single_space_string {
119 args: func_args![value: value!(" ")],
120 want: Ok(value!(true)),
121 tdef: TypeDef::boolean().infallible(),
122 }
123
124 multi_space_string {
125 args: func_args![value: value!(" ")],
126 want: Ok(value!(true)),
127 tdef: TypeDef::boolean().infallible(),
128 }
129
130 newline_string {
131 args: func_args![value: value!("\n")],
132 want: Ok(value!(true)),
133 tdef: TypeDef::boolean().infallible(),
134 }
135
136 carriage_return_string {
137 args: func_args![value: value!("\r")],
138 want: Ok(value!(true)),
139 tdef: TypeDef::boolean().infallible(),
140 }
141
142 dash_string {
143 args: func_args![value: value!("-")],
144 want: Ok(value!(true)),
145 tdef: TypeDef::boolean().infallible(),
146 }
147
148 null {
149 args: func_args![value: value!(null)],
150 want: Ok(value!(true)),
151 tdef: TypeDef::boolean().infallible(),
152 }
153
154 non_empty_string {
155 args: func_args![value: value!("hello world")],
156 want: Ok(value!(false)),
157 tdef: TypeDef::boolean().infallible(),
158 }
159
160 integer {
162 args: func_args![value: value!(427)],
163 want: Ok(value!(false)),
164 tdef: TypeDef::boolean().infallible(),
165 }
166
167 array {
169 args: func_args![value: value!([1, 2, 3])],
170 want: Ok(value!(false)),
171 tdef: TypeDef::boolean().infallible(),
172 }
173 ];
174}