vrl/datadog/filter/filter.rs
1use std::fmt::Debug;
2
3use super::{Matcher, Run};
4use crate::datadog::search::{Comparison, ComparisonValue, Field};
5use crate::path::PathParseError;
6use dyn_clone::{DynClone, clone_trait_object};
7
8/// A `Filter` is a generic type that contains methods that are invoked by the `build_filter`
9/// function. Each method returns a heap-allocated `Matcher<V>` (typically a closure) containing
10/// logic to determine whether the value matches the filter. A filter is intended to be side-effect
11/// free and idempotent, and so only receives an immutable reference to self.
12pub trait Filter<V: Debug + Send + Sync + Clone + 'static>: DynClone {
13 /// Determine whether a field value exists.
14 ///
15 /// # Errors
16 ///
17 /// Will return `Err` if the query contains an invalid path.
18 fn exists(&self, field: Field) -> Result<Box<dyn Matcher<V>>, PathParseError>;
19
20 /// Determine whether a field value equals `to_match`.
21 ///
22 /// # Errors
23 ///
24 /// Will return `Err` if the query contains an invalid path.
25 fn equals(&self, field: Field, to_match: &str) -> Result<Box<dyn Matcher<V>>, PathParseError>;
26
27 /// Determine whether a value starts with a prefix.
28 ///
29 /// # Errors
30 /// Will return `Err` if the query contains an invalid path.
31 fn prefix(&self, field: Field, prefix: &str) -> Result<Box<dyn Matcher<V>>, PathParseError>;
32
33 /// Determine whether a value matches a wildcard.
34 ///
35 /// # Errors
36 ///
37 /// Will return `Err` if the query contains an invalid path.
38 fn wildcard(&self, field: Field, wildcard: &str)
39 -> Result<Box<dyn Matcher<V>>, PathParseError>;
40
41 /// Compare a field value against `comparison_value`, using one of the `comparator` operators.
42 ///
43 /// # Errors
44 ///
45 /// Will return `Err` if the query contains an invalid path.
46 fn compare(
47 &self,
48 field: Field,
49 comparator: Comparison,
50 comparison_value: ComparisonValue,
51 ) -> Result<Box<dyn Matcher<V>>, PathParseError>;
52
53 /// Determine whether a field value falls within a range. By default, this will use
54 /// `self.compare` on both the lower and upper bound.
55 ///
56 /// # Errors
57 ///
58 /// Will return `Err` if the query contains an invalid path.
59 fn range(
60 &self,
61 field: Field,
62 lower: ComparisonValue,
63 lower_inclusive: bool,
64 upper: ComparisonValue,
65 upper_inclusive: bool,
66 ) -> Result<Box<dyn Matcher<V>>, PathParseError> {
67 match (&lower, &upper) {
68 // If both bounds are wildcards, just check that the field exists to catch the
69 // special case for "tags".
70 (ComparisonValue::Unbounded, ComparisonValue::Unbounded) => self.exists(field),
71 // Unbounded lower.
72 (ComparisonValue::Unbounded, _) => {
73 let op = if upper_inclusive {
74 Comparison::Lte
75 } else {
76 Comparison::Lt
77 };
78
79 self.compare(field, op, upper)
80 }
81 // Unbounded upper.
82 (_, ComparisonValue::Unbounded) => {
83 let op = if lower_inclusive {
84 Comparison::Gte
85 } else {
86 Comparison::Gt
87 };
88
89 self.compare(field, op, lower)
90 }
91 // Definitive range.
92 _ => {
93 let lower_op = if lower_inclusive {
94 Comparison::Gte
95 } else {
96 Comparison::Gt
97 };
98
99 let upper_op = if upper_inclusive {
100 Comparison::Lte
101 } else {
102 Comparison::Lt
103 };
104
105 let lower_func = self.compare(field.clone(), lower_op, lower)?;
106 let upper_func = self.compare(field, upper_op, upper)?;
107
108 Ok(Run::boxed(move |value| {
109 lower_func.run(value) && upper_func.run(value)
110 }))
111 }
112 }
113 }
114}
115
116clone_trait_object!(<V>Filter<V>);