vrl/value/
kind.rs

1//! The `kind` module has all relevant types related to progressive type checking.
2
3mod builder;
4mod collection;
5mod comparison;
6mod conversion;
7mod crud;
8mod debug;
9
10pub mod merge;
11
12pub use crud::*;
13
14use std::collections::BTreeMap;
15
16pub use collection::{Collection, Field, Index, Unknown};
17
18use super::Value;
19
20/// The type (kind) of a given value.
21///
22/// This struct tracks the known states a type can have. By allowing one type to have multiple
23/// states, the type definition can be progressively refined.
24#[derive(Debug, Clone, Eq, PartialOrd)]
25pub struct Kind {
26    // NOTE: The internal API uses `Option` over `bool` for primitive types, as it makes internal
27    // usage of the API easier to work with. There is no impact on the memory size of the type.
28    bytes: Option<()>,
29    integer: Option<()>,
30    float: Option<()>,
31    boolean: Option<()>,
32    timestamp: Option<()>,
33    regex: Option<()>,
34    null: Option<()>,
35    undefined: Option<()>,
36    array: Option<Collection<Index>>,
37    object: Option<Collection<Field>>,
38}
39
40impl std::fmt::Display for Kind {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        if self.is_any() {
43            return f.write_str("any");
44        }
45
46        // For collections, we expand to a more descriptive representation only
47        // if the type can only be this collection.
48        if self.is_exact() {
49            if let Some(object) = &self.object {
50                return object.fmt(f);
51            } else if let Some(array) = &self.array {
52                return array.fmt(f);
53            }
54        }
55
56        let mut kinds = vec![];
57
58        if self.contains_bytes() {
59            kinds.push("string");
60        }
61        if self.contains_integer() {
62            kinds.push("integer");
63        }
64        if self.contains_float() {
65            kinds.push("float");
66        }
67        if self.contains_boolean() {
68            kinds.push("boolean");
69        }
70        if self.contains_timestamp() {
71            kinds.push("timestamp");
72        }
73        if self.contains_regex() {
74            kinds.push("regex");
75        }
76        if self.contains_null() {
77            kinds.push("null");
78        }
79        if self.contains_undefined() {
80            kinds.push("undefined");
81        }
82        if self.contains_array() {
83            kinds.push("array");
84        }
85        if self.contains_object() {
86            kinds.push("object");
87        }
88
89        if kinds.is_empty() {
90            return f.write_str("never");
91        }
92
93        let len = kinds.len();
94        for (i, kind) in kinds.into_iter().enumerate() {
95            if i != 0 {
96                if i == len - 1 {
97                    f.write_str(" or ")?;
98                } else {
99                    f.write_str(", ")?;
100                }
101            }
102            kind.fmt(f)?;
103        }
104
105        Ok(())
106    }
107}
108
109impl PartialEq for Kind {
110    fn eq(&self, other: &Self) -> bool {
111        let a = self.canonicalize();
112        let b = other.canonicalize();
113
114        if a.bytes != b.bytes {
115            return false;
116        }
117        if a.integer != b.integer {
118            return false;
119        }
120        if a.float != b.float {
121            return false;
122        }
123        if a.boolean != b.boolean {
124            return false;
125        }
126        if a.timestamp != b.timestamp {
127            return false;
128        }
129        if a.regex != b.regex {
130            return false;
131        }
132        if a.null != b.null {
133            return false;
134        }
135        if a.undefined != b.undefined {
136            return false;
137        }
138        if a.array != b.array {
139            return false;
140        }
141        if a.object != b.object {
142            return false;
143        }
144        true
145    }
146}
147
148impl Kind {
149    /// Returns a Kind type in a standard / simple representation.
150    #[must_use]
151    pub fn canonicalize(&self) -> Self {
152        let mut output = self.clone();
153
154        if let Some(object) = &mut output.object {
155            *object = object.canonicalize();
156        }
157        if let Some(array) = &mut output.array {
158            *array = array.canonicalize();
159        }
160        output
161    }
162}
163
164impl From<&Value> for Kind {
165    fn from(value: &Value) -> Self {
166        match value {
167            Value::Bytes(_) => Self::bytes(),
168            Value::Integer(_) => Self::integer(),
169            Value::Float(_) => Self::float(),
170            Value::Boolean(_) => Self::boolean(),
171            Value::Timestamp(_) => Self::timestamp(),
172            Value::Regex(_) => Self::regex(),
173            Value::Null => Self::null(),
174
175            Value::Object(object) => Self::object(
176                object
177                    .iter()
178                    .map(|(k, v)| (k.clone().into(), v.into()))
179                    .collect::<BTreeMap<_, _>>(),
180            ),
181
182            Value::Array(array) => Self::array(
183                array
184                    .iter()
185                    .enumerate()
186                    .map(|(i, v)| (i.into(), v.into()))
187                    .collect::<BTreeMap<_, _>>(),
188            ),
189        }
190    }
191}
192
193impl From<Value> for Kind {
194    fn from(value: Value) -> Self {
195        (&value).into()
196    }
197}