vrl/compiler/value/
kind.rs

1use crate::value::Value;
2use chrono::{TimeZone, Utc};
3use ordered_float::NotNan;
4use regex::Regex;
5
6use crate::value;
7
8pub const BYTES: u16 = 1 << 1;
9pub const INTEGER: u16 = 1 << 2;
10pub const FLOAT: u16 = 1 << 3;
11pub const BOOLEAN: u16 = 1 << 4;
12pub const OBJECT: u16 = 1 << 5;
13pub const ARRAY: u16 = 1 << 6;
14pub const TIMESTAMP: u16 = 1 << 7;
15pub const REGEX: u16 = 1 << 8;
16pub const NULL: u16 = 1 << 9;
17pub const UNDEFINED: u16 = 1 << 10;
18
19pub const ANY: u16 =
20    BYTES | INTEGER | FLOAT | BOOLEAN | OBJECT | ARRAY | TIMESTAMP | REGEX | NULL | UNDEFINED;
21pub const SCALAR: u16 = BYTES | INTEGER | FLOAT | BOOLEAN | TIMESTAMP | REGEX | NULL;
22pub const CONTAINER: u16 = OBJECT | ARRAY;
23
24pub use crate::value::{
25    Kind,
26    kind::{Collection, Field, Index, get, insert, merge, remove},
27};
28
29pub trait DefaultValue {
30    /// Returns the default [`Value`] for a given [`Kind`].
31    ///
32    /// If the kind is unknown (or inexact), `null` is returned as the default
33    /// value.
34    ///
35    /// These are (somewhat) arbitrary values that mostly shouldn't be used, but
36    /// are particularly useful for the "infallible assignment" expression,
37    /// where the `ok` value is set to the default value kind if the expression
38    /// results in an error.
39    fn default_value(&self) -> Value;
40}
41
42impl DefaultValue for Kind {
43    fn default_value(&self) -> Value {
44        if self.is_bytes() {
45            return value!("");
46        }
47
48        if self.is_integer() {
49            return value!(0_i64);
50        }
51
52        if self.is_float() {
53            return value!(NotNan::new(0.0).unwrap());
54        }
55
56        if self.is_boolean() {
57            return value!(false);
58        }
59
60        if self.is_timestamp() {
61            return Utc
62                .timestamp_opt(0, 0)
63                .single()
64                .expect("invalid timestamp")
65                .into();
66        }
67
68        if self.is_regex() {
69            #[allow(clippy::trivial_regex)]
70            return Regex::new("").unwrap().into();
71        }
72
73        if self.is_array() {
74            return value!([]);
75        }
76
77        if self.is_object() {
78            return value!({});
79        }
80
81        Value::Null
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use std::collections::{BTreeMap, HashMap};
88
89    use super::*;
90
91    #[test]
92    fn test_from_value() {
93        struct TestCase {
94            value: Value,
95            want: Kind,
96        }
97
98        for (title, TestCase { value, want }) in HashMap::from([
99            (
100                "bytes",
101                TestCase {
102                    value: value!("foo"),
103                    want: Kind::bytes(),
104                },
105            ),
106            (
107                "integer",
108                TestCase {
109                    value: value!(3_i64),
110                    want: Kind::integer(),
111                },
112            ),
113            (
114                "float",
115                TestCase {
116                    value: value!(NotNan::new(3.3).unwrap()),
117                    want: Kind::float(),
118                },
119            ),
120            (
121                "boolean",
122                TestCase {
123                    value: value!(true),
124                    want: Kind::boolean(),
125                },
126            ),
127            (
128                "timestamp",
129                TestCase {
130                    value: Utc::now().into(),
131                    want: Kind::timestamp(),
132                },
133            ),
134            (
135                "regex",
136                TestCase {
137                    value: Regex::new("").unwrap().into(),
138                    want: Kind::regex(),
139                },
140            ),
141            (
142                "null",
143                TestCase {
144                    value: value!(null),
145                    want: Kind::null(),
146                },
147            ),
148            (
149                "object",
150                TestCase {
151                    value: value!({ "foo": { "bar": 12_i64 }, "baz": true }),
152                    want: Kind::object(BTreeMap::from([
153                        (
154                            "foo".into(),
155                            Kind::object(BTreeMap::from([("bar".into(), Kind::integer())])),
156                        ),
157                        ("baz".into(), Kind::boolean()),
158                    ])),
159                },
160            ),
161            (
162                "array",
163                TestCase {
164                    value: value!([12_i64, true, "foo", { "bar": null }]),
165                    want: Kind::array(BTreeMap::from([
166                        (0.into(), Kind::integer()),
167                        (1.into(), Kind::boolean()),
168                        (2.into(), Kind::bytes()),
169                        (
170                            3.into(),
171                            Kind::object(BTreeMap::from([("bar".into(), Kind::null())])),
172                        ),
173                    ])),
174                },
175            ),
176        ]) {
177            assert_eq!(Kind::from(value), want, "{title}");
178        }
179    }
180}