// Copyright (c) 2018 The predicates-rs Project Developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::fmt; use crate::reflection; use crate::utils; use crate::Predicate; /// An error that occurred during parsing or compiling a regular expression. pub type RegexError = regex::Error; /// Predicate that uses regex matching /// /// This is created by the `predicate::str::is_match`. #[derive(Debug, Clone)] pub struct RegexPredicate { re: regex::Regex, } impl RegexPredicate { /// Require a specific count of matches. /// /// # Examples /// /// ``` /// use predicates::prelude::*; /// /// let predicate_fn = predicate::str::is_match("T[a-z]*").unwrap().count(3); /// assert_eq!(true, predicate_fn.eval("One Two Three Two One")); /// assert_eq!(false, predicate_fn.eval("One Two Three")); /// ``` pub fn count(self, count: usize) -> RegexMatchesPredicate { RegexMatchesPredicate { re: self.re, count } } } impl Predicate for RegexPredicate { fn eval(&self, variable: &str) -> bool { self.re.is_match(variable) } fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { utils::default_find_case(self, expected, variable) .map(|case| case.add_product(reflection::Product::new("var", variable.to_owned()))) } } impl reflection::PredicateReflection for RegexPredicate {} impl fmt::Display for RegexPredicate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let palette = crate::Palette::new(f.alternate()); write!( f, "{}.{}({})", palette.var("var"), palette.description("is_match"), palette.expected(&self.re), ) } } /// Predicate that checks for repeated patterns. /// /// This is created by `predicates::str::is_match(...).count`. #[derive(Debug, Clone)] pub struct RegexMatchesPredicate { re: regex::Regex, count: usize, } impl Predicate for RegexMatchesPredicate { fn eval(&self, variable: &str) -> bool { self.re.find_iter(variable).count() == self.count } fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { let actual_count = self.re.find_iter(variable).count(); let result = self.count == actual_count; if result == expected { Some( reflection::Case::new(Some(self), result) .add_product(reflection::Product::new("var", variable.to_owned())) .add_product(reflection::Product::new("actual count", actual_count)), ) } else { None } } } impl reflection::PredicateReflection for RegexMatchesPredicate { fn parameters<'a>(&'a self) -> Box> + 'a> { let params = vec![reflection::Parameter::new("count", &self.count)]; Box::new(params.into_iter()) } } impl fmt::Display for RegexMatchesPredicate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let palette = crate::Palette::new(f.alternate()); write!( f, "{}.{}({})", palette.var("var"), palette.description("is_match"), palette.expected(&self.re), ) } } /// Creates a new `Predicate` that uses a regular expression to match the string. /// /// # Examples /// /// ``` /// use predicates::prelude::*; /// /// let predicate_fn = predicate::str::is_match("^Hello.*$").unwrap(); /// assert_eq!(true, predicate_fn.eval("Hello World")); /// assert_eq!(false, predicate_fn.eval("Food World")); /// ``` pub fn is_match(pattern: S) -> Result where S: AsRef, { regex::Regex::new(pattern.as_ref()).map(|re| RegexPredicate { re }) }