use predicates::{
reflection::{Case, PredicateReflection, Product},
Predicate,
};
use std::fmt;
use crate::Captured;
pub trait IntoTargetPredicate {
type Predicate: Predicate<str>;
fn into_predicate(self) -> Self::Predicate;
}
impl<P: Predicate<str>> IntoTargetPredicate for [P; 1] {
type Predicate = P;
fn into_predicate(self) -> Self::Predicate {
self.into_iter().next().unwrap()
}
}
impl<'a> IntoTargetPredicate for &'a str {
type Predicate = TargetStrPredicate<'a>;
fn into_predicate(self) -> Self::Predicate {
TargetStrPredicate { prefix: self }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TargetStrPredicate<'a> {
prefix: &'a str,
}
impl fmt::Display for TargetStrPredicate<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "target ^= {}", self.prefix)
}
}
impl PredicateReflection for TargetStrPredicate<'_> {}
impl Predicate<str> for TargetStrPredicate<'_> {
fn eval(&self, variable: &str) -> bool {
variable
.strip_prefix(self.prefix)
.map_or(false, |stripped| {
stripped.is_empty() || stripped.starts_with("::")
})
}
fn find_case(&self, expected: bool, variable: &str) -> Option<Case<'_>> {
if self.eval(variable) == expected {
let product = Product::new("target", variable.to_owned());
Some(Case::new(Some(self), expected).add_product(product))
} else {
None
}
}
}
pub fn target<P: IntoTargetPredicate>(matches: P) -> TargetPredicate<P::Predicate> {
TargetPredicate {
matches: matches.into_predicate(),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TargetPredicate<P> {
matches: P,
}
impl_bool_ops!(TargetPredicate<P>);
impl<P: Predicate<str>> fmt::Display for TargetPredicate<P> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "target({})", self.matches)
}
}
impl<P: Predicate<str>> PredicateReflection for TargetPredicate<P> {}
impl<'a, P: Predicate<str>, T: Captured<'a>> Predicate<T> for TargetPredicate<P> {
fn eval(&self, variable: &T) -> bool {
self.matches.eval(variable.metadata().target())
}
fn find_case(&self, expected: bool, variable: &T) -> Option<Case<'_>> {
let child = self
.matches
.find_case(expected, variable.metadata().target())?;
Some(Case::new(Some(self), expected).add_child(child))
}
}