vrl/path/mod.rs
1#![deny(warnings)]
2
3//! This module contains all of the logic for paths.
4//!
5//! Paths can be thought of as similar to file paths (in an operating system) pointing
6//! to specific files inside of a directory.
7//! A `Value` is a data structure that can contain recursively nested fields. Paths
8//! allow referring to a specific field inside of a `Value`.
9//!
10//! # Example
11//! Below is a sample `Value`. Different fields can be accessed with paths.
12//! ```json
13//! {
14//! "foo": {
15//! "bar": 1
16//! },
17//! "baz": ["a", "b", "c"]
18//! }
19//! ```
20//!
21//! | path | value it points to |
22//! |--------|---------------------------------------|
23//! | `.foo.bar` | `1`
24//! | `.foo` | `{ "bar": 1 }`
25//! | `.` | `{ "foo" : { "bar" : 1 }, "baz" : ["a", "b", "c"] }`
26//! | `.baz[0]` | `"a"`
27//! | `.baz` | `["a", "b", "c"]`
28//!
29//!
30//! # Traits
31//! There are 2 main traits that define a path. Most functions that use a path for querying
32//! will require one of these traits, rather than a concrete type.
33//!
34//! - [ValuePath] is a path that points to a field inside of a `Value`.
35//! - [TargetPath] is a path that points to a field inside of a `target`. A `target` in VRL refers to
36//! the external data being processed by a VRL script. A `target` has two main sections that can be
37//! pointed to, `event` and `metadata`. [TargetPath::prefix] identifies the section, and
38//! [TargetPath::value_path] is a [ValuePath] pointing into that section.
39//!
40//! Note that for performance reasons, since [ValuePath] and [TargetPath] require [Clone], these
41//! traits are only implemented on types that are cheap to clone (usually references). That means
42//! when passing in a value (e.g. [OwnedValuePath]) into a function that requires `impl ValuePath`,
43//! it will generally need to be passed in as a reference.
44//!
45//! # Owned Paths
46//! [OwnedValuePath] and [OwnedTargetPath] are pre-parsed paths. That means that accessing fields
47//! using an owned path is very fast. There is an upfront cost however, since owned paths are parsed
48//! when they are created, and the segments are heap allocated. Owned paths should be preferred
49//! if they can be created when performance isn't as much of a concern (e.g. startup time)
50//! and they can be stored for re-use.
51//! Owned paths tend to be easier to work with since you can directly access / manipulate the
52//! segments that make up the path.
53//!
54//! If a path is being created and will only be used once, it may make sense to use other types.
55//! For example here are two different ways to append a segment to a [OwnedValuePath] before querying
56//! a `Value`:
57//! - Use [OwnedValuePath::with_field_appended] to create a new [OwnedValuePath] and use that. This
58//! method is preferred if the new path will be used multiple times and the path adjustment can be
59//! done in a non performance-critical part of the code (e.g. at startup).
60//! - Use [ValuePath::concat] which con concatenate two [ValuePath]'s very efficiently without
61//! allocating on the heap.
62//!
63//! To convert a string into an owned path, use either [parse_value_path] or [parse_target_path].
64//!
65//! # String Paths
66//! [ValuePath] and [TargetPath] are implemented for [&str]. That means a raw / unparsed string can
67//! be used as a path. This use is discouraged, and may be removed in the future. It mostly
68//! exists for backwards compatibility in places where String paths are used instead of owned paths.
69//! Using string paths is slightly slower than using an owned path. It's still very fast
70//! but it is easy to introduce bugs since some compile-time type information is missing -
71//! such as whether it is a target vs value path, or if the entire string is meant
72//! to be treated as a single segment vs being parsed as a path.
73//!
74//! # Macros
75//! Several macros exist to make creating paths easier. These are used if the structure of the
76//! path being created is already known. <strong>The macros do not parse paths</strong>. Use
77//! [parse_value_path] or [parse_target_path] instead if the path needs to be parsed.
78//!
79//! You need to pass in each segment into the macro as separate arguments. A single argument is treated as
80//! a single segment. This is true for all of the path macros.
81//!
82//! For example, [owned_value_path!][crate::owned_value_path] can be used to easily created owned paths.
83//! - `owned_value_path!("foo.bar", "x")` will create a path with *two* segments. Equivalent to `."foo.bar".x`
84//!
85
86use std::fmt;
87use std::fmt::Debug;
88
89use snafu::Snafu;
90
91pub use borrowed::{BorrowedSegment, BorrowedTargetPath, BorrowedValuePath};
92pub use concat::PathConcat;
93pub use owned::{OwnedSegment, OwnedTargetPath, OwnedValuePath};
94
95use self::jit::JitValuePath;
96
97mod borrowed;
98mod concat;
99mod jit;
100mod owned;
101
102#[derive(Clone, Debug, Eq, PartialEq, Snafu)]
103pub enum PathParseError {
104 #[snafu(display("Invalid field path {:?}", path))]
105 InvalidPathSyntax { path: String },
106}
107
108/// Syntactic sugar for creating a pre-parsed path.
109///
110/// Example: `path!("foo", 4, "bar")` is the pre-parsed path of `foo[4].bar`
111#[macro_export]
112macro_rules! path {
113 ($($segment:expr_2021),*) => { $crate::path::BorrowedValuePath {
114 segments: &[$($crate::path::BorrowedSegment::from($segment),)*],
115 }};
116}
117
118/// Syntactic sugar for creating a pre-parsed path.
119/// This path points at an event (as opposed to metadata).
120#[macro_export]
121macro_rules! event_path {
122 ($($segment:expr_2021),*) => { $crate::path::BorrowedTargetPath {
123 prefix: $crate::path::PathPrefix::Event,
124 path: $crate::path!($($segment),*),
125 }};
126}
127
128/// Syntactic sugar for creating a pre-parsed path.
129/// This path points at metadata (as opposed to the event).
130#[macro_export]
131macro_rules! metadata_path {
132 ($($segment:expr_2021),*) => { $crate::path::BorrowedTargetPath {
133 prefix: $crate::path::PathPrefix::Metadata,
134 path: $crate::path!($($segment),*),
135 }};
136}
137
138/// Syntactic sugar for creating a pre-parsed owned path.
139///
140/// This allocates and will be slower than using `path!`. Prefer that when possible.
141/// The return value must be borrowed to get a value that implements `Path`.
142///
143/// Example: `owned_value_path!("foo", 4, "bar")` is the pre-parsed path of `foo[4].bar`
144#[macro_export]
145macro_rules! owned_value_path {
146 ($($segment:expr_2021),*) => {{
147 $crate::path::OwnedValuePath::from(vec![$($crate::path::OwnedSegment::from($segment),)*])
148 }};
149}
150
151/// Syntactic sugar for creating a pre-parsed owned event path.
152/// This path points at the event (as opposed to metadata).
153#[macro_export]
154macro_rules! owned_event_path {
155 ($($tokens:tt)*) => {
156 $crate::path::OwnedTargetPath::event($crate::owned_value_path!($($tokens)*))
157 }
158}
159
160/// Syntactic sugar for creating a pre-parsed owned metadata path.
161/// This path points at metadata (as opposed to the event).
162#[macro_export]
163macro_rules! owned_metadata_path {
164 ($($tokens:tt)*) => {
165 $crate::path::OwnedTargetPath::metadata($crate::owned_value_path!($($tokens)*))
166 }
167}
168
169/// Used to pre-parse a path.
170/// The return value (when borrowed) implements `Path` so it can be used directly.
171/// This parses a value path, which is a path without a target prefix.
172///
173/// See `parse_target_path` if the path contains a target prefix.
174pub fn parse_value_path(path: &str) -> Result<OwnedValuePath, PathParseError> {
175 JitValuePath::new(path)
176 .to_owned_value_path()
177 .map_err(|_| PathParseError::InvalidPathSyntax {
178 path: path.to_owned(),
179 })
180}
181
182/// Used to pre-parse a path.
183/// The return value (when borrowed) implements `Path` so it can be used directly.
184/// This parses a target path, which is a path that contains a target prefix.
185///
186/// See `parse_value_path` if the path doesn't contain a prefix.
187pub fn parse_target_path(path: &str) -> Result<OwnedTargetPath, PathParseError> {
188 let (prefix, value_path) = get_target_prefix(path);
189 let value_path = parse_value_path(value_path)?;
190
191 Ok(OwnedTargetPath {
192 prefix,
193 path: value_path,
194 })
195}
196
197pub trait TargetPath<'a>: Clone {
198 type ValuePath: ValuePath<'a>;
199
200 fn prefix(&self) -> PathPrefix;
201 fn value_path(&self) -> Self::ValuePath;
202}
203
204/// A path is simply the data describing how to look up a field from a value.
205/// This should only be implemented for types that are very cheap to clone, such as references.
206pub trait ValuePath<'a>: Clone {
207 type Iter: Iterator<Item = BorrowedSegment<'a>> + Clone;
208
209 /// Iterates over the raw "Borrowed" segments.
210 fn segment_iter(&self) -> Self::Iter;
211
212 fn concat<T: ValuePath<'a>>(&self, path: T) -> PathConcat<Self, T> {
213 PathConcat {
214 a: self.clone(),
215 b: path,
216 }
217 }
218
219 fn eq(&self, other: impl ValuePath<'a>) -> bool {
220 self.segment_iter().eq(other.segment_iter())
221 }
222
223 fn can_start_with(&self, prefix: impl ValuePath<'a>) -> bool {
224 let (self_path, prefix_path) = if let (Ok(self_path), Ok(prefix_path)) =
225 (self.to_owned_value_path(), prefix.to_owned_value_path())
226 {
227 (self_path, prefix_path)
228 } else {
229 return false;
230 };
231
232 let mut self_segments = self_path.segments.into_iter();
233 for prefix_segment in prefix_path.segments.iter() {
234 match self_segments.next() {
235 None => return false,
236 Some(self_segment) => {
237 if !self_segment.can_start_with(prefix_segment) {
238 return false;
239 }
240 }
241 }
242 }
243 true
244 }
245
246 #[allow(clippy::result_unit_err)]
247 fn to_owned_value_path(&self) -> Result<OwnedValuePath, ()> {
248 self.segment_iter()
249 .map(OwnedSegment::try_from)
250 .collect::<Result<Vec<OwnedSegment>, ()>>()
251 .map(OwnedValuePath::from)
252 }
253}
254
255#[cfg(any(feature = "string_path", test))]
256impl<'a> ValuePath<'a> for &'a str {
257 type Iter = jit::JitValuePathIter<'a>;
258
259 fn segment_iter(&self) -> Self::Iter {
260 JitValuePath::new(self).segment_iter()
261 }
262}
263
264#[cfg(any(feature = "string_path", test))]
265impl<'a> TargetPath<'a> for &'a str {
266 type ValuePath = &'a str;
267
268 fn prefix(&self) -> PathPrefix {
269 get_target_prefix(self).0
270 }
271
272 fn value_path(&self) -> Self::ValuePath {
273 get_target_prefix(self).1
274 }
275}
276
277impl<'a> TargetPath<'a> for &'a OwnedTargetPath {
278 type ValuePath = &'a OwnedValuePath;
279
280 fn prefix(&self) -> PathPrefix {
281 self.prefix
282 }
283
284 fn value_path(&self) -> Self::ValuePath {
285 &self.path
286 }
287}
288
289// This is deprecated but still used in Vector (results in 10 compile errors)
290impl<'a, T: ValuePath<'a>> TargetPath<'a> for (PathPrefix, T) {
291 type ValuePath = T;
292
293 fn prefix(&self) -> PathPrefix {
294 self.0
295 }
296
297 fn value_path(&self) -> Self::ValuePath {
298 self.1.clone()
299 }
300}
301
302/// Determines the prefix of a "TargetPath", and also returns the remaining
303/// "ValuePath" portion of the string.
304fn get_target_prefix(path: &str) -> (PathPrefix, &str) {
305 match path.chars().next() {
306 Some('.') => {
307 // For backwards compatibility, the "ValuePath" parser still allows an optional
308 // starting ".". To prevent ".." from being a valid path, it is _not_ removed
309 // here. This should be changed once "ValuePath" no longer allows a leading ".".
310 (PathPrefix::Event, path)
311 }
312 Some('%') => (PathPrefix::Metadata, &path[1..]),
313 _ => {
314 // This shouldn't be allowed in the future, but is currently
315 // used for backwards compatibility.
316 (PathPrefix::Event, path)
317 }
318 }
319}
320
321#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
322#[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))]
323pub enum PathPrefix {
324 Event,
325 Metadata,
326}
327
328impl fmt::Display for PathPrefix {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 match self {
331 PathPrefix::Event => write!(f, "."),
332 PathPrefix::Metadata => write!(f, "%"),
333 }
334 }
335}
336
337#[cfg(test)]
338mod test {
339 use crate::path::PathPrefix;
340 use crate::path::TargetPath;
341 use crate::path::ValuePath;
342 use crate::path::parse_target_path;
343
344 #[test]
345 fn test_parse_target_path() {
346 assert_eq!(parse_target_path("i"), Ok(owned_event_path!("i")));
347 }
348
349 #[test]
350 fn test_path_macro() {
351 assert!(ValuePath::eq(&path!("a", "b"), "a.b"))
352 }
353
354 #[test]
355 fn test_event_path_macro() {
356 let path = event_path!("a", "b");
357 let expected = "a.b";
358 assert!(ValuePath::eq(&path.value_path(), expected));
359 assert_eq!(path.prefix(), PathPrefix::Event);
360 }
361
362 #[test]
363 fn test_metadata_path_macro() {
364 let path = metadata_path!("a", "b");
365 let expected = "a.b";
366 assert!(ValuePath::eq(&path.value_path(), expected));
367 assert_eq!(path.prefix(), PathPrefix::Metadata);
368 }
369
370 #[test]
371 fn test_owned_value_path_macro() {
372 assert!(ValuePath::eq(&&owned_value_path!("a", "b"), "a.b"))
373 }
374}