use predicates::{
reflection::{Case, PredicateReflection, Product},
Predicate,
};
use std::{any::type_name, borrow::Borrow, fmt, marker::PhantomData};
use crate::{Captured, CapturedEvent};
use tracing_tunnel::{FromTracedValue, TracedValue};
pub trait IntoFieldPredicate {
type Predicate: Predicate<TracedValue>;
fn into_predicate(self) -> Self::Predicate;
}
impl<P: Predicate<TracedValue>> IntoFieldPredicate for [P; 1] {
type Predicate = P;
fn into_predicate(self) -> Self::Predicate {
self.into_iter().next().unwrap()
}
}
macro_rules! impl_into_field_predicate {
($($ty:ty),+) => {
$(
impl IntoFieldPredicate for $ty {
type Predicate = EquivPredicate<Self>;
fn into_predicate(self) -> Self::Predicate {
EquivPredicate { value: self }
}
}
)+
};
}
impl_into_field_predicate!(bool, i64, i128, u64, u128, f64, &str);
pub fn field<P: IntoFieldPredicate>(
name: &'static str,
matches: P,
) -> FieldPredicate<P::Predicate> {
FieldPredicate {
name,
matches: matches.into_predicate(),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FieldPredicate<P> {
name: &'static str,
matches: P,
}
impl_bool_ops!(FieldPredicate<P>);
impl<P: Predicate<TracedValue>> fmt::Display for FieldPredicate<P> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "fields.{}({})", self.name, self.matches)
}
}
impl<P: Predicate<TracedValue>> PredicateReflection for FieldPredicate<P> {}
impl<'a, P: Predicate<TracedValue>, T: Captured<'a>> Predicate<T> for FieldPredicate<P> {
fn eval(&self, variable: &T) -> bool {
variable
.value(self.name)
.map_or(false, |value| self.matches.eval(value))
}
fn find_case(&self, expected: bool, variable: &T) -> Option<Case<'_>> {
let value = if let Some(value) = variable.value(self.name) {
value
} else {
return if expected {
None } else {
let product = Product::new(format!("fields.{}", self.name), "None");
Some(Case::new(Some(self), expected).add_product(product))
};
};
let child = self.matches.find_case(expected, value)?;
Some(Case::new(Some(self), expected).add_child(child))
}
}
#[doc(hidden)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EquivPredicate<V> {
value: V,
}
impl<V: fmt::Debug> fmt::Display for EquivPredicate<V> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "var == {:?}", self.value)
}
}
impl<V: fmt::Debug> PredicateReflection for EquivPredicate<V> {}
impl<V: fmt::Debug + PartialEq<TracedValue>> Predicate<TracedValue> for EquivPredicate<V> {
fn eval(&self, variable: &TracedValue) -> bool {
self.value == *variable
}
fn find_case(&self, expected: bool, variable: &TracedValue) -> Option<Case<'_>> {
if self.eval(variable) == expected {
let product = Product::new("var", format!("{variable:?}"));
Some(Case::new(Some(self), expected).add_product(product))
} else {
None
}
}
}
pub fn value<T, P>(matches: P) -> ValuePredicate<T, P>
where
T: for<'a> FromTracedValue<'a> + ?Sized,
P: Predicate<T>,
{
ValuePredicate {
matches,
_ty: PhantomData,
}
}
#[derive(Debug)]
pub struct ValuePredicate<T: ?Sized, P> {
matches: P,
_ty: PhantomData<fn(T)>,
}
impl<T: ?Sized, P: Clone> Clone for ValuePredicate<T, P> {
fn clone(&self) -> Self {
Self {
matches: self.matches.clone(),
_ty: PhantomData,
}
}
}
impl<T: ?Sized, P: Copy> Copy for ValuePredicate<T, P> {}
impl<T: ?Sized, P: PartialEq> PartialEq for ValuePredicate<T, P> {
fn eq(&self, other: &Self) -> bool {
self.matches == other.matches
}
}
impl<T, P> fmt::Display for ValuePredicate<T, P>
where
T: for<'a> FromTracedValue<'a> + ?Sized,
P: Predicate<T>,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "is<{}>({})", type_name::<T>(), self.matches)
}
}
impl<T, P> PredicateReflection for ValuePredicate<T, P>
where
T: for<'a> FromTracedValue<'a> + ?Sized,
P: Predicate<T>,
{
}
impl<T, P> Predicate<TracedValue> for ValuePredicate<T, P>
where
T: for<'a> FromTracedValue<'a> + ?Sized,
P: Predicate<T>,
{
fn eval(&self, variable: &TracedValue) -> bool {
T::from_value(variable).map_or(false, |value| self.matches.eval(value.borrow()))
}
fn find_case(&self, expected: bool, variable: &TracedValue) -> Option<Case<'_>> {
let value = T::from_value(variable);
let value = if let Some(value) = &value {
value.borrow()
} else {
return if expected {
None } else {
let product = Product::new(format!("var.as<{}>", type_name::<T>()), "None");
Some(Case::new(Some(self), expected).add_product(product))
};
};
let child = self.matches.find_case(expected, value)?;
Some(Case::new(Some(self), expected).add_child(child))
}
}
impl<T, P> IntoFieldPredicate for ValuePredicate<T, P>
where
T: for<'a> FromTracedValue<'a> + ?Sized,
P: Predicate<T>,
{
type Predicate = Self;
fn into_predicate(self) -> Self::Predicate {
self
}
}
pub fn message<P: Predicate<str>>(matches: P) -> MessagePredicate<P> {
MessagePredicate { matches }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MessagePredicate<P> {
matches: P,
}
impl_bool_ops!(MessagePredicate<P>);
impl<P: Predicate<str>> fmt::Display for MessagePredicate<P> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "message({})", self.matches)
}
}
impl<P: Predicate<str>> PredicateReflection for MessagePredicate<P> {}
impl<P: Predicate<str>> Predicate<CapturedEvent<'_>> for MessagePredicate<P> {
fn eval(&self, variable: &CapturedEvent<'_>) -> bool {
variable
.message()
.map_or(false, |value| self.matches.eval(value))
}
fn find_case(&self, expected: bool, variable: &CapturedEvent<'_>) -> Option<Case<'_>> {
let message = if let Some(message) = variable.message() {
message
} else {
return if expected {
None } else {
let product = Product::new("message", "None");
Some(Case::new(Some(self), expected).add_product(product))
};
};
let child = self.matches.find_case(expected, message)?;
Some(Case::new(Some(self), expected).add_child(child))
}
}