tracing_capture/predicates/
field.rs1use std::{any::type_name, borrow::Borrow, fmt, marker::PhantomData};
4
5use predicates::{
6 reflection::{Case, PredicateReflection, Product},
7 Predicate,
8};
9use tracing_tunnel::{FromTracedValue, TracedValue};
10
11use crate::{Captured, CapturedEvent};
12
13pub trait IntoFieldPredicate {
15 type Predicate: Predicate<TracedValue>;
18 fn into_predicate(self) -> Self::Predicate;
20}
21
22impl<P: Predicate<TracedValue>> IntoFieldPredicate for [P; 1] {
23 type Predicate = P;
24
25 fn into_predicate(self) -> Self::Predicate {
26 self.into_iter().next().unwrap()
27 }
28}
29
30macro_rules! impl_into_field_predicate {
31 ($($ty:ty),+) => {
32 $(
33 impl IntoFieldPredicate for $ty {
34 type Predicate = EquivPredicate<Self>;
35
36 fn into_predicate(self) -> Self::Predicate {
37 EquivPredicate { value: self }
38 }
39 }
40 )+
41 };
42}
43
44impl_into_field_predicate!(bool, i64, i128, u64, u128, f64, &str);
45
46pub fn field<P: IntoFieldPredicate>(
83 name: &'static str,
84 matches: P,
85) -> FieldPredicate<P::Predicate> {
86 FieldPredicate {
87 name,
88 matches: matches.into_predicate(),
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub struct FieldPredicate<P> {
98 name: &'static str,
99 matches: P,
100}
101
102impl_bool_ops!(FieldPredicate<P>);
103
104impl<P: Predicate<TracedValue>> fmt::Display for FieldPredicate<P> {
105 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
106 write!(formatter, "fields.{}({})", self.name, self.matches)
107 }
108}
109
110impl<P: Predicate<TracedValue>> PredicateReflection for FieldPredicate<P> {}
111
112impl<'a, P: Predicate<TracedValue>, T: Captured<'a>> Predicate<T> for FieldPredicate<P> {
113 fn eval(&self, variable: &T) -> bool {
114 variable
115 .value(self.name)
116 .is_some_and(|value| self.matches.eval(value))
117 }
118
119 fn find_case(&self, expected: bool, variable: &T) -> Option<Case<'_>> {
120 let Some(value) = variable.value(self.name) else {
121 return if expected {
122 None } else {
124 let product = Product::new(format!("fields.{}", self.name), "None");
125 Some(Case::new(Some(self), expected).add_product(product))
126 };
127 };
128
129 let child = self.matches.find_case(expected, value)?;
130 Some(Case::new(Some(self), expected).add_child(child))
131 }
132}
133
134#[doc(hidden)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub struct EquivPredicate<V> {
137 value: V,
138}
139
140impl<V: fmt::Debug> fmt::Display for EquivPredicate<V> {
141 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
142 write!(formatter, "var == {:?}", self.value)
143 }
144}
145
146impl<V: fmt::Debug> PredicateReflection for EquivPredicate<V> {}
147
148impl<V: fmt::Debug + PartialEq<TracedValue>> Predicate<TracedValue> for EquivPredicate<V> {
149 fn eval(&self, variable: &TracedValue) -> bool {
150 self.value == *variable
151 }
152
153 fn find_case(&self, expected: bool, variable: &TracedValue) -> Option<Case<'_>> {
154 if self.eval(variable) == expected {
155 let product = Product::new("var", format!("{variable:?}"));
156 Some(Case::new(Some(self), expected).add_product(product))
157 } else {
158 None
159 }
160 }
161}
162
163pub fn value<T, P>(matches: P) -> ValuePredicate<T, P>
186where
187 T: for<'a> FromTracedValue<'a> + ?Sized,
188 P: Predicate<T>,
189{
190 ValuePredicate {
191 matches,
192 _ty: PhantomData,
193 }
194}
195
196#[derive(Debug)]
198pub struct ValuePredicate<T: ?Sized, P> {
199 matches: P,
200 _ty: PhantomData<fn(T)>,
201}
202
203impl<T: ?Sized, P: Clone> Clone for ValuePredicate<T, P> {
204 fn clone(&self) -> Self {
205 Self {
206 matches: self.matches.clone(),
207 _ty: PhantomData,
208 }
209 }
210}
211
212impl<T: ?Sized, P: Copy> Copy for ValuePredicate<T, P> {}
213
214impl<T: ?Sized, P: PartialEq> PartialEq for ValuePredicate<T, P> {
215 fn eq(&self, other: &Self) -> bool {
216 self.matches == other.matches
217 }
218}
219
220impl<T, P> fmt::Display for ValuePredicate<T, P>
221where
222 T: for<'a> FromTracedValue<'a> + ?Sized,
223 P: Predicate<T>,
224{
225 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
226 write!(formatter, "is<{}>({})", type_name::<T>(), self.matches)
227 }
228}
229
230impl<T, P> PredicateReflection for ValuePredicate<T, P>
231where
232 T: for<'a> FromTracedValue<'a> + ?Sized,
233 P: Predicate<T>,
234{
235}
236
237impl<T, P> Predicate<TracedValue> for ValuePredicate<T, P>
238where
239 T: for<'a> FromTracedValue<'a> + ?Sized,
240 P: Predicate<T>,
241{
242 fn eval(&self, variable: &TracedValue) -> bool {
243 T::from_value(variable).is_some_and(|value| self.matches.eval(value.borrow()))
244 }
245
246 fn find_case(&self, expected: bool, variable: &TracedValue) -> Option<Case<'_>> {
247 let value = T::from_value(variable);
248 let value = if let Some(value) = &value {
249 value.borrow()
250 } else {
251 return if expected {
252 None } else {
254 let product = Product::new(format!("var.as<{}>", type_name::<T>()), "None");
255 Some(Case::new(Some(self), expected).add_product(product))
256 };
257 };
258
259 let child = self.matches.find_case(expected, value)?;
260 Some(Case::new(Some(self), expected).add_child(child))
261 }
262}
263
264impl<T, P> IntoFieldPredicate for ValuePredicate<T, P>
265where
266 T: for<'a> FromTracedValue<'a> + ?Sized,
267 P: Predicate<T>,
268{
269 type Predicate = Self;
270
271 fn into_predicate(self) -> Self::Predicate {
272 self
273 }
274}
275
276pub fn message<P: Predicate<str>>(matches: P) -> MessagePredicate<P> {
303 MessagePredicate { matches }
304}
305
306#[derive(Debug, Clone, Copy, PartialEq, Eq)]
308pub struct MessagePredicate<P> {
309 matches: P,
310}
311
312impl_bool_ops!(MessagePredicate<P>);
313
314impl<P: Predicate<str>> fmt::Display for MessagePredicate<P> {
315 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
316 write!(formatter, "message({})", self.matches)
317 }
318}
319
320impl<P: Predicate<str>> PredicateReflection for MessagePredicate<P> {}
321
322impl<P: Predicate<str>> Predicate<CapturedEvent<'_>> for MessagePredicate<P> {
323 fn eval(&self, variable: &CapturedEvent<'_>) -> bool {
324 variable
325 .message()
326 .is_some_and(|value| self.matches.eval(value))
327 }
328
329 fn find_case(&self, expected: bool, variable: &CapturedEvent<'_>) -> Option<Case<'_>> {
330 let Some(message) = variable.message() else {
331 return if expected {
332 None } else {
334 let product = Product::new("message", "None");
335 Some(Case::new(Some(self), expected).add_product(product))
336 };
337 };
338
339 let child = self.matches.find_case(expected, message)?;
340 Some(Case::new(Some(self), expected).add_child(child))
341 }
342}