1#[cfg(any(test, feature = "proptest"))]
2use proptest::prelude::*;
3use regex::Regex;
4use serde::{Deserialize, Serialize};
5use std::fmt::{self, Debug, Display, Formatter, Write};
6use std::str::FromStr;
7
8use super::PathPrefix;
9use super::{BorrowedSegment, PathParseError, ValuePath, parse_target_path, parse_value_path};
10use crate::value::KeyString;
11use std::sync::LazyLock;
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
15#[serde(try_from = "String", into = "String")]
16pub struct OwnedValuePath {
17 pub segments: Vec<OwnedSegment>,
18}
19
20impl OwnedValuePath {
21 pub fn is_root(&self) -> bool {
22 self.segments.is_empty()
23 }
24
25 pub fn root() -> Self {
26 vec![].into()
27 }
28
29 pub fn push_field(&mut self, field: &str) {
30 self.segments.push(OwnedSegment::field(field));
31 }
32
33 pub fn push_segment(&mut self, segment: OwnedSegment) {
34 self.segments.push(segment);
35 }
36
37 pub fn push_front_field(&mut self, field: &str) {
38 self.segments.insert(0, OwnedSegment::field(field));
39 }
40
41 pub fn push_front_segment(&mut self, segment: OwnedSegment) {
42 self.segments.insert(0, segment);
43 }
44
45 pub fn with_field_appended(&self, field: &str) -> Self {
46 let mut new_path = self.clone();
47 new_path.push_field(field);
48 new_path
49 }
50
51 pub fn with_field_prefix(&self, field: &str) -> Self {
52 self.with_segment_prefix(OwnedSegment::field(field))
53 }
54
55 pub fn with_segment_prefix(&self, segment: OwnedSegment) -> Self {
56 let mut new_path = self.clone();
57 new_path.push_front_segment(segment);
58 new_path
59 }
60
61 pub fn push_index(&mut self, index: isize) {
62 self.segments.push(OwnedSegment::index(index));
63 }
64
65 pub fn with_index_appended(&self, index: isize) -> Self {
66 let mut new_path = self.clone();
67 new_path.push_index(index);
68 new_path
69 }
70
71 pub fn single_field(field: &str) -> Self {
72 vec![OwnedSegment::field(field)].into()
73 }
74
75 pub fn to_alternative_components(&self, limit: usize) -> Option<Vec<&str>> {
84 let mut components = vec![];
85 for segment in self.segments.iter().take(limit) {
86 match segment {
87 OwnedSegment::Field(field) => {
88 components.push(field.as_str());
89 }
90
91 OwnedSegment::Index(_) => {
92 return None;
93 }
94 }
95 }
96
97 Some(components)
98 }
99
100 pub fn push(&mut self, segment: OwnedSegment) {
101 self.segments.push(segment);
102 }
103}
104
105#[cfg(any(test, feature = "proptest"))]
107impl Arbitrary for OwnedValuePath {
108 type Parameters = ();
109 type Strategy = BoxedStrategy<Self>;
110
111 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
112 prop::collection::vec(any::<OwnedSegment>(), 1..10)
113 .prop_map(|segments| OwnedValuePath { segments })
114 .boxed()
115 }
116}
117
118#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
120#[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))]
121#[serde(try_from = "String", into = "String")]
122pub struct OwnedTargetPath {
123 pub prefix: PathPrefix,
124 pub path: OwnedValuePath,
125}
126
127impl OwnedTargetPath {
128 pub fn event_root() -> Self {
129 Self::root(PathPrefix::Event)
130 }
131 pub fn metadata_root() -> Self {
132 Self::root(PathPrefix::Metadata)
133 }
134
135 pub fn root(prefix: PathPrefix) -> Self {
136 Self {
137 prefix,
138 path: OwnedValuePath::root(),
139 }
140 }
141
142 pub fn event(path: OwnedValuePath) -> Self {
143 Self {
144 prefix: PathPrefix::Event,
145 path,
146 }
147 }
148
149 pub fn metadata(path: OwnedValuePath) -> Self {
150 Self {
151 prefix: PathPrefix::Metadata,
152 path,
153 }
154 }
155
156 pub fn can_start_with(&self, prefix: &Self) -> bool {
157 if self.prefix != prefix.prefix {
158 return false;
159 }
160 (&self.path).can_start_with(&prefix.path)
161 }
162
163 pub fn with_field_appended(&self, field: &str) -> Self {
164 let mut new_path = self.path.clone();
165 new_path.push_field(field);
166 Self {
167 prefix: self.prefix,
168 path: new_path,
169 }
170 }
171
172 pub fn with_index_appended(&self, index: isize) -> Self {
173 let mut new_path = self.path.clone();
174 new_path.push_index(index);
175 Self {
176 prefix: self.prefix,
177 path: new_path,
178 }
179 }
180}
181
182impl Display for OwnedTargetPath {
183 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
184 match self.prefix {
185 PathPrefix::Event => write!(f, ".")?,
186 PathPrefix::Metadata => write!(f, "%")?,
187 }
188 Display::fmt(&self.path, f)
189 }
190}
191
192impl Debug for OwnedTargetPath {
193 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
194 Display::fmt(self, f)
195 }
196}
197
198impl From<OwnedTargetPath> for String {
199 fn from(target_path: OwnedTargetPath) -> Self {
200 Self::from(&target_path)
201 }
202}
203
204impl From<&OwnedTargetPath> for String {
205 fn from(target_path: &OwnedTargetPath) -> Self {
206 target_path.to_string()
207 }
208}
209
210impl Display for OwnedValuePath {
211 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
212 write!(f, "{}", String::from(self))
213 }
214}
215
216impl FromStr for OwnedValuePath {
217 type Err = PathParseError;
218
219 fn from_str(src: &str) -> Result<Self, Self::Err> {
220 parse_value_path(src).map_err(|_| PathParseError::InvalidPathSyntax {
221 path: src.to_owned(),
222 })
223 }
224}
225
226impl TryFrom<String> for OwnedValuePath {
227 type Error = PathParseError;
228
229 fn try_from(src: String) -> Result<Self, Self::Error> {
230 src.parse()
231 }
232}
233
234impl FromStr for OwnedTargetPath {
235 type Err = PathParseError;
236
237 fn from_str(src: &str) -> Result<Self, Self::Err> {
238 parse_target_path(src).map_err(|_| PathParseError::InvalidPathSyntax {
239 path: src.to_owned(),
240 })
241 }
242}
243
244impl TryFrom<String> for OwnedTargetPath {
245 type Error = PathParseError;
246
247 fn try_from(src: String) -> Result<Self, Self::Error> {
248 src.parse()
249 }
250}
251
252impl TryFrom<KeyString> for OwnedValuePath {
253 type Error = PathParseError;
254
255 fn try_from(src: KeyString) -> Result<Self, Self::Error> {
256 src.parse()
257 }
258}
259
260impl TryFrom<KeyString> for OwnedTargetPath {
261 type Error = PathParseError;
262
263 fn try_from(src: KeyString) -> Result<Self, Self::Error> {
264 src.parse()
265 }
266}
267
268impl From<OwnedValuePath> for String {
269 fn from(owned: OwnedValuePath) -> Self {
270 Self::from(&owned)
271 }
272}
273
274impl From<&OwnedValuePath> for String {
275 fn from(owned: &OwnedValuePath) -> Self {
276 let mut output = String::new();
277 for (i, segment) in owned.segments.iter().enumerate() {
278 match segment {
279 OwnedSegment::Field(field) => {
280 serialize_field(&mut output, field.as_ref(), (i != 0).then_some("."))
281 }
282 OwnedSegment::Index(index) => {
283 write!(output, "[{index}]").expect("Could not write to string")
284 }
285 }
286 }
287 output
288 }
289}
290
291fn serialize_field(string: &mut String, field: &str, separator: Option<&str>) {
292 let needs_quotes = field.is_empty()
294 || field
295 .chars()
296 .any(|c| !matches!(c, 'A'..='Z' | 'a'..='z' | '_' | '0'..='9' | '@'));
297
298 let separator_len = separator.map_or(0, |x| x.len());
301 string.reserve(field.len() + 2 + separator_len);
302 if let Some(separator) = separator {
303 string.push_str(separator);
304 }
305 if needs_quotes {
306 string.push('"');
307 for c in field.chars() {
308 if matches!(c, '"' | '\\') {
309 string.push('\\');
310 }
311 string.push(c);
312 }
313 string.push('"');
314 } else {
315 string.push_str(field);
316 }
317}
318
319impl From<Vec<OwnedSegment>> for OwnedValuePath {
320 fn from(segments: Vec<OwnedSegment>) -> Self {
321 Self { segments }
322 }
323}
324
325#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
326#[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))]
327pub enum OwnedSegment {
328 Field(KeyString),
329 Index(isize),
330}
331
332impl OwnedSegment {
333 pub fn field(value: &str) -> OwnedSegment {
334 OwnedSegment::Field(value.into())
335 }
336 pub fn index(value: isize) -> OwnedSegment {
337 OwnedSegment::Index(value)
338 }
339
340 pub fn is_field(&self) -> bool {
341 matches!(self, OwnedSegment::Field(_))
342 }
343 pub fn is_index(&self) -> bool {
344 matches!(self, OwnedSegment::Index(_))
345 }
346
347 pub fn can_start_with(&self, prefix: &OwnedSegment) -> bool {
348 match (self, prefix) {
349 (OwnedSegment::Index(a), OwnedSegment::Index(b)) => a == b,
350 (OwnedSegment::Index(_), _) | (_, OwnedSegment::Index(_)) => false,
351 (OwnedSegment::Field(a), OwnedSegment::Field(b)) => a == b,
352 }
353 }
354}
355
356impl<'a> From<&'a str> for OwnedSegment {
357 fn from(field: &'a str) -> Self {
358 OwnedSegment::field(field)
359 }
360}
361
362impl<'a> From<&'a String> for OwnedSegment {
363 fn from(field: &'a String) -> Self {
364 OwnedSegment::field(field.as_str())
365 }
366}
367
368impl From<isize> for OwnedSegment {
369 fn from(index: isize) -> Self {
370 OwnedSegment::index(index)
371 }
372}
373
374impl<'a> ValuePath<'a> for &'a Vec<OwnedSegment> {
375 type Iter = OwnedSegmentSliceIter<'a>;
376
377 fn segment_iter(&self) -> Self::Iter {
378 OwnedSegmentSliceIter(self.iter())
379 }
380}
381
382impl<'a> ValuePath<'a> for &'a [OwnedSegment] {
383 type Iter = OwnedSegmentSliceIter<'a>;
384
385 fn segment_iter(&self) -> Self::Iter {
386 OwnedSegmentSliceIter(self.iter())
387 }
388}
389
390impl<'a> ValuePath<'a> for &'a OwnedValuePath {
391 type Iter = OwnedSegmentSliceIter<'a>;
392
393 fn segment_iter(&self) -> Self::Iter {
394 (&self.segments).segment_iter()
395 }
396}
397
398impl<'a> TryFrom<BorrowedSegment<'a>> for OwnedSegment {
399 type Error = ();
400
401 fn try_from(segment: BorrowedSegment<'a>) -> Result<Self, Self::Error> {
402 match segment {
403 BorrowedSegment::Invalid => Err(()),
404 BorrowedSegment::Index(i) => Ok(OwnedSegment::Index(i)),
405 BorrowedSegment::Field(field) => Ok(OwnedSegment::Field(field.into())),
406 }
407 }
408}
409
410static VALID_FIELD: LazyLock<Regex> =
411 LazyLock::new(|| Regex::new("^[0-9]*[a-zA-Z_@][0-9a-zA-Z_@]*$").unwrap());
412
413fn format_field(f: &mut Formatter<'_>, field: &str) -> fmt::Result {
414 let needs_quotes = !VALID_FIELD.is_match(field);
418 if needs_quotes {
419 write!(f, "\"{field}\"")
420 } else {
421 write!(f, "{field}")
422 }
423}
424
425impl Display for OwnedSegment {
426 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
427 match self {
428 OwnedSegment::Index(i) => write!(f, "[{i}]"),
429 OwnedSegment::Field(field) => format_field(f, field),
430 }
431 }
432}
433
434#[derive(Clone)]
435pub struct OwnedSegmentSliceIter<'a>(std::slice::Iter<'a, OwnedSegment>);
436
437impl<'a> Iterator for OwnedSegmentSliceIter<'a> {
438 type Item = BorrowedSegment<'a>;
439
440 fn next(&mut self) -> Option<Self::Item> {
441 self.0.next().map(BorrowedSegment::from)
442 }
443}
444
445#[cfg(test)]
446mod test {
447 use super::*;
448 use crate::path::parse_value_path;
449
450 #[test]
451 fn to_alternative_components() -> Result<(), PathParseError> {
452 let limit = 3;
453
454 let path = parse_value_path(".")?;
455 assert_eq!(Some(vec![]), path.to_alternative_components(limit));
456
457 let path = parse_value_path(".first.second")?;
458 assert_eq!(
459 Some(vec!["first", "second"]),
460 path.to_alternative_components(limit)
461 );
462
463 let path = parse_value_path("a.b[1].c.d")?;
464 assert_eq!(None, path.to_alternative_components(limit));
465
466 let path = parse_value_path("a.b.c[1].d")?; assert_eq!(
468 Some(vec!["a", "b", "c"]),
469 path.to_alternative_components(limit)
470 );
471
472 let path = parse_value_path("a.b.c.d.e.f.g.h")?;
473 assert_eq!(
474 Some(vec!["a", "b", "c"]),
475 path.to_alternative_components(limit)
476 );
477
478 Ok(())
479 }
480
481 #[test]
482 fn owned_path_serialize() {
483 let test_cases = [
484 (".", Some("")),
485 ("", None),
486 ("]", None),
487 ("]foo", None),
488 ("..", None),
489 ("...", None),
490 ("f", Some("f")),
491 ("foo", Some("foo")),
492 (
493 r#"ec2.metadata."availability-zone""#,
494 Some(r#"ec2.metadata."availability-zone""#),
495 ),
496 ("@timestamp", Some("@timestamp")),
497 ("foo[", None),
498 ("foo$", None),
499 (r#""$peci@l chars""#, Some(r#""$peci@l chars""#)),
500 ("foo.foo bar", None),
501 (r#"foo."foo bar".bar"#, Some(r#"foo."foo bar".bar"#)),
502 ("[1]", Some("[1]")),
503 ("[42]", Some("[42]")),
504 ("foo.[42]", None),
505 ("[42].foo", Some("[42].foo")),
506 ("[-1]", Some("[-1]")),
507 ("[-42]", Some("[-42]")),
508 ("[-42].foo", Some("[-42].foo")),
509 ("[-42]foo", Some("[-42].foo")),
510 (r#""[42]. {}-_""#, Some(r#""[42]. {}-_""#)),
511 (r#""a\"a""#, Some(r#""a\"a""#)),
512 (r#"foo."a\"a"."b\\b".bar"#, Some(r#"foo."a\"a"."b\\b".bar"#)),
513 ("<invalid>", None),
514 (r#""🤖""#, Some(r#""🤖""#)),
515 ];
516
517 for (path, expected) in test_cases {
518 let path = parse_value_path(path).map(String::from).ok();
519
520 assert_eq!(path, expected.map(|x| x.to_owned()));
521 }
522 }
523
524 fn reparse_thing<T: std::fmt::Debug + std::fmt::Display + Eq + FromStr>(thing: T)
525 where
526 <T as FromStr>::Err: std::fmt::Debug,
527 {
528 let text = thing.to_string();
529 let thing2: T = text.parse().unwrap();
530 assert_eq!(thing, thing2);
531 }
532
533 proptest::proptest! {
534 #[test]
535 fn reparses_valid_value_path(path: OwnedValuePath) {
536 reparse_thing(path);
537 }
538
539 #[test]
540 fn reparses_valid_target_path(path: OwnedTargetPath) {
541 reparse_thing(path);
542 }
543 }
544}