vrl/value/
value.rs

1//! Contains the main "Value" type for Vector and VRL, as well as helper methods.
2
3#[allow(clippy::module_name_repetitions)]
4pub use super::value::regex::ValueRegex;
5#[allow(clippy::module_name_repetitions)]
6pub use iter::{IterItem, ValueIter};
7
8use bytes::{Bytes, BytesMut};
9use chrono::{DateTime, SecondsFormat, Utc};
10use ordered_float::NotNan;
11use std::borrow::Cow;
12use std::{cmp::Ordering, collections::BTreeMap};
13
14use super::KeyString;
15use crate::path::ValuePath;
16
17mod convert;
18mod crud;
19mod display;
20mod iter;
21mod path;
22mod regex;
23
24#[cfg(any(test, feature = "arbitrary"))]
25mod arbitrary;
26#[cfg(any(test, feature = "lua"))]
27mod lua;
28mod serde;
29
30/// A boxed `std::error::Error`.
31pub type StdError = Box<dyn std::error::Error + Send + Sync + 'static>;
32
33/// The storage mapping for the `Object` variant.
34pub type ObjectMap = BTreeMap<KeyString, Value>;
35
36/// The main value type used in Vector events, and VRL.
37#[derive(Eq, PartialEq, Hash, Debug, Clone)]
38pub enum Value {
39    /// Bytes - usually representing a UTF8 String.
40    Bytes(Bytes),
41
42    /// Regex.
43    /// When used in the context of Vector this is treated identically to Bytes. It has
44    /// additional meaning in the context of VRL.
45    Regex(ValueRegex),
46
47    /// Integer.
48    Integer(i64),
49
50    /// Float - not NaN.
51    Float(NotNan<f64>),
52
53    /// Boolean.
54    Boolean(bool),
55
56    /// Timestamp (UTC).
57    Timestamp(DateTime<Utc>),
58
59    /// Object.
60    Object(ObjectMap),
61
62    /// Array.
63    Array(Vec<Value>),
64
65    /// Null.
66    Null,
67}
68
69impl Value {
70    /// Returns a string description of the value type
71    pub const fn kind_str(&self) -> &str {
72        match self {
73            Self::Bytes(_) | Self::Regex(_) => "string",
74            Self::Timestamp(_) => "timestamp",
75            Self::Integer(_) => "integer",
76            Self::Float(_) => "float",
77            Self::Boolean(_) => "boolean",
78            Self::Object(_) => "map",
79            Self::Array(_) => "array",
80            Self::Null => "null",
81        }
82    }
83
84    /// Merges `incoming` value into self.
85    ///
86    /// Will concatenate `Bytes` and overwrite the rest value kinds.
87    pub fn merge(&mut self, incoming: Self) {
88        match (self, incoming) {
89            (Self::Bytes(self_bytes), Self::Bytes(ref incoming)) => {
90                let mut bytes = BytesMut::with_capacity(self_bytes.len() + incoming.len());
91                bytes.extend_from_slice(&self_bytes[..]);
92                bytes.extend_from_slice(&incoming[..]);
93                *self_bytes = bytes.freeze();
94            }
95            (current, incoming) => *current = incoming,
96        }
97    }
98
99    /// Return if the node is empty, that is, it is an array or map with no items.
100    ///
101    /// ```rust
102    /// use vrl::value::Value;
103    /// use std::collections::BTreeMap;
104    /// use vrl::path;
105    ///
106    /// let val = Value::from(1);
107    /// assert_eq!(val.is_empty(), false);
108    ///
109    /// let mut val = Value::from(Vec::<Value>::default());
110    /// assert_eq!(val.is_empty(), true);
111    /// val.insert(path!(0), 1);
112    /// assert_eq!(val.is_empty(), false);
113    /// val.insert(path!(3), 1);
114    /// assert_eq!(val.is_empty(), false);
115    ///
116    /// let mut val = Value::from(BTreeMap::default());
117    /// assert_eq!(val.is_empty(), true);
118    /// val.insert(path!("foo"), 1);
119    /// assert_eq!(val.is_empty(), false);
120    /// val.insert(path!("bar"), 2);
121    /// assert_eq!(val.is_empty(), false);
122    /// ```
123    pub fn is_empty(&self) -> bool {
124        match &self {
125            Self::Boolean(_)
126            | Self::Bytes(_)
127            | Self::Regex(_)
128            | Self::Timestamp(_)
129            | Self::Float(_)
130            | Self::Integer(_) => false,
131            Self::Null => true,
132            Self::Object(v) => v.is_empty(),
133            Self::Array(v) => v.is_empty(),
134        }
135    }
136
137    /// Returns a reference to a field value specified by a path iter.
138    #[allow(clippy::needless_pass_by_value)]
139    pub fn insert<'a>(
140        &mut self,
141        path: impl ValuePath<'a>,
142        insert_value: impl Into<Self>,
143    ) -> Option<Self> {
144        let insert_value = insert_value.into();
145        let path_iter = path.segment_iter().peekable();
146
147        crud::insert(self, (), path_iter, insert_value)
148    }
149
150    /// Removes field value specified by the given path and return its value.
151    ///
152    /// A special case worth mentioning: if there is a nested array and an item is removed
153    /// from the middle of this array, then it is just replaced by `Value::Null`.
154    #[allow(clippy::needless_pass_by_value)]
155    pub fn remove<'a>(&mut self, path: impl ValuePath<'a>, prune: bool) -> Option<Self> {
156        crud::remove(self, &(), path.segment_iter(), prune)
157            .map(|(prev_value, _is_empty)| prev_value)
158    }
159
160    /// Returns a reference to a field value specified by a path iter.
161    #[allow(clippy::needless_pass_by_value)]
162    pub fn get<'a>(&self, path: impl ValuePath<'a>) -> Option<&Self> {
163        crud::get(self, path.segment_iter())
164    }
165
166    /// Get a mutable borrow of the value by path
167    #[allow(clippy::needless_pass_by_value)]
168    pub fn get_mut<'a>(&mut self, path: impl ValuePath<'a>) -> Option<&mut Self> {
169        crud::get_mut(self, path.segment_iter())
170    }
171
172    /// Determine if the lookup is contained within the value.
173    pub fn contains<'a>(&self, path: impl ValuePath<'a>) -> bool {
174        self.get(path).is_some()
175    }
176}
177
178impl PartialOrd for Value {
179    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
180        if std::mem::discriminant(self) != std::mem::discriminant(other) {
181            return None;
182        }
183        match (self, other) {
184            (Self::Bytes(a), Self::Bytes(b)) => a.partial_cmp(b),
185            (Self::Regex(a), Self::Regex(b)) => a.partial_cmp(b),
186            (Self::Integer(a), Self::Integer(b)) => a.partial_cmp(b),
187            (Self::Float(a), Self::Float(b)) => a.partial_cmp(b),
188            (Self::Boolean(a), Self::Boolean(b)) => a.partial_cmp(b),
189            (Self::Timestamp(a), Self::Timestamp(b)) => a.partial_cmp(b),
190            (Self::Object(a), Self::Object(b)) => a.partial_cmp(b),
191            (Self::Array(a), Self::Array(b)) => a.partial_cmp(b),
192            _ => None,
193        }
194    }
195}
196
197/// Converts a slice of bytes to a string, including invalid characters.
198#[must_use]
199pub fn simdutf_bytes_utf8_lossy(v: &[u8]) -> Cow<'_, str> {
200    simdutf8::basic::from_utf8(v).map_or_else(
201        |_| {
202            const REPLACEMENT: &str = "\u{FFFD}";
203
204            let mut res = String::with_capacity(v.len());
205            for chunk in v.utf8_chunks() {
206                res.push_str(chunk.valid());
207                if !chunk.invalid().is_empty() {
208                    res.push_str(REPLACEMENT);
209                }
210            }
211            Cow::Owned(res)
212        },
213        Cow::Borrowed,
214    )
215}
216
217/// Converts a timestamp to a `String`.
218#[must_use]
219pub fn timestamp_to_string(timestamp: &DateTime<Utc>) -> String {
220    timestamp.to_rfc3339_opts(SecondsFormat::AutoSi, true)
221}
222
223#[cfg(test)]
224mod test {
225    use quickcheck::{QuickCheck, TestResult};
226
227    use crate::path;
228    use crate::path::BorrowedSegment;
229
230    use super::*;
231
232    mod corner_cases {
233        use super::*;
234
235        #[test]
236        fn remove_prune_map_with_map() {
237            let mut value = Value::from(BTreeMap::default());
238            let key = "foo.bar";
239            let marker = Value::from(true);
240            assert_eq!(value.insert(key, marker.clone()), None);
241            // Since the `foo` map is now empty, this should get cleaned.
242            assert_eq!(value.remove(key, true), Some(marker));
243            assert!(!value.contains("foo"));
244        }
245
246        #[test]
247        fn remove_prune_map_with_array() {
248            let mut value = Value::from(BTreeMap::default());
249            let key = "foo[0]";
250            let marker = Value::from(true);
251            assert_eq!(value.insert(key, marker.clone()), None);
252            // Since the `foo` map is now empty, this should get cleaned.
253            assert_eq!(value.remove(key, true), Some(marker));
254            assert!(!value.contains("foo"));
255        }
256
257        #[test]
258        fn remove_prune_array_with_map() {
259            let mut value = Value::from(Vec::<Value>::default());
260            let key = "[0].bar";
261            let marker = Value::from(true);
262            assert_eq!(value.insert(key, marker.clone()), None);
263            // Since the `foo` map is now empty, this should get cleaned.
264            assert_eq!(value.remove(key, true), Some(marker));
265            assert!(!value.contains(path!(0)));
266        }
267
268        #[test]
269        fn remove_prune_array_with_array() {
270            let mut value = Value::from(Vec::<Value>::default());
271            let key = "[0][0]";
272            let marker = Value::from(true);
273            assert_eq!(value.insert(key, marker.clone()), None);
274            // Since the `foo` map is now empty, this should get cleaned.
275            assert_eq!(value.remove(key, true), Some(marker));
276            assert!(!value.contains(path!(0)));
277        }
278    }
279
280    #[test]
281    fn quickcheck_value() {
282        fn inner(mut path: Vec<BorrowedSegment<'static>>) -> TestResult {
283            let mut value = Value::from(BTreeMap::default());
284            let mut marker = Value::from(true);
285
286            // Push a field at the start of the path so the top level is a map.
287            path.insert(0, BorrowedSegment::from("field"));
288
289            assert_eq!(value.insert(&path, marker.clone()), None, "inserting value");
290            assert_eq!(value.get(&path), Some(&marker), "retrieving value");
291            assert_eq!(
292                value.get_mut(&path),
293                Some(&mut marker),
294                "retrieving mutable value"
295            );
296
297            assert_eq!(value.remove(&path, true), Some(marker), "removing value");
298
299            TestResult::passed()
300        }
301
302        QuickCheck::new()
303            .tests(100)
304            .max_tests(200)
305            .quickcheck(inner as fn(Vec<BorrowedSegment<'static>>) -> TestResult);
306    }
307
308    #[test]
309    fn partial_ord_value() {
310        assert_eq!(
311            Value::from(50).partial_cmp(&Value::from(77)),
312            Some(Ordering::Less)
313        );
314        assert_eq!(
315            Value::from("zzz").partial_cmp(&Value::from("aaa")),
316            Some(Ordering::Greater)
317        );
318        assert_eq!(
319            Value::from(10.5).partial_cmp(&Value::from(10.5)),
320            Some(Ordering::Equal)
321        );
322        assert_eq!(Value::from(10.5).partial_cmp(&Value::from(10)), None);
323    }
324}