use std::error::Error;
use std::path::Path;
use super::{Matcher, MatcherIO, WalkEntry};
pub struct AndMatcher {
submatchers: Vec<Box<dyn Matcher>>,
}
impl AndMatcher {
pub fn new(submatchers: Vec<Box<dyn Matcher>>) -> Self {
Self { submatchers }
}
}
impl Matcher for AndMatcher {
fn matches(&self, dir_entry: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
for matcher in &self.submatchers {
if !matcher.matches(dir_entry, matcher_io) {
return false;
}
if matcher_io.should_quit() {
break;
}
}
true
}
fn has_side_effects(&self) -> bool {
self.submatchers
.iter()
.any(super::Matcher::has_side_effects)
}
fn finished_dir(&self, dir: &Path, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished_dir(dir, matcher_io);
}
}
fn finished(&self, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished(matcher_io);
}
}
}
pub struct AndMatcherBuilder {
submatchers: Vec<Box<dyn Matcher>>,
}
impl AndMatcherBuilder {
pub fn new() -> Self {
Self {
submatchers: Vec::new(),
}
}
pub fn new_and_condition(&mut self, matcher: impl Matcher) {
self.submatchers.push(matcher.into_box());
}
pub fn build(mut self) -> Box<dyn Matcher> {
if self.submatchers.len() == 1 {
return self.submatchers.pop().unwrap();
}
AndMatcher::new(self.submatchers).into_box()
}
}
pub struct OrMatcher {
submatchers: Vec<Box<dyn Matcher>>,
}
impl OrMatcher {
pub fn new(submatchers: Vec<Box<dyn Matcher>>) -> Self {
Self { submatchers }
}
}
impl Matcher for OrMatcher {
fn matches(&self, dir_entry: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
for matcher in &self.submatchers {
if matcher.matches(dir_entry, matcher_io) {
return true;
}
if matcher_io.should_quit() {
break;
}
}
false
}
fn has_side_effects(&self) -> bool {
self.submatchers
.iter()
.any(super::Matcher::has_side_effects)
}
fn finished_dir(&self, dir: &Path, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished_dir(dir, matcher_io);
}
}
fn finished(&self, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished(matcher_io);
}
}
}
pub struct OrMatcherBuilder {
submatchers: Vec<AndMatcherBuilder>,
}
impl OrMatcherBuilder {
pub fn new_and_condition(&mut self, matcher: impl Matcher) {
self.submatchers
.last_mut()
.unwrap()
.new_and_condition(matcher);
}
pub fn new_or_condition(&mut self, arg: &str) -> Result<(), Box<dyn Error>> {
if self.submatchers.last().unwrap().submatchers.is_empty() {
return Err(From::from(format!(
"invalid expression; you have used a binary operator \
'{arg}' with nothing before it."
)));
}
self.submatchers.push(AndMatcherBuilder::new());
Ok(())
}
pub fn new() -> Self {
let mut o = Self {
submatchers: Vec::new(),
};
o.submatchers.push(AndMatcherBuilder::new());
o
}
pub fn build(mut self) -> Box<dyn Matcher> {
if self.submatchers.len() == 1 {
return self.submatchers.pop().unwrap().build();
}
let mut submatchers = vec![];
for x in self.submatchers {
submatchers.push(x.build());
}
OrMatcher::new(submatchers).into_box()
}
}
pub struct ListMatcher {
submatchers: Vec<Box<dyn Matcher>>,
}
impl ListMatcher {
pub fn new(submatchers: Vec<Box<dyn Matcher>>) -> Self {
Self { submatchers }
}
}
impl Matcher for ListMatcher {
fn matches(&self, dir_entry: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
let mut rc = false;
for matcher in &self.submatchers {
rc = matcher.matches(dir_entry, matcher_io);
if matcher_io.should_quit() {
break;
}
}
rc
}
fn has_side_effects(&self) -> bool {
self.submatchers
.iter()
.any(super::Matcher::has_side_effects)
}
fn finished_dir(&self, dir: &Path, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished_dir(dir, matcher_io);
}
}
fn finished(&self, matcher_io: &mut MatcherIO) {
for m in &self.submatchers {
m.finished(matcher_io);
}
}
}
pub struct ListMatcherBuilder {
submatchers: Vec<OrMatcherBuilder>,
}
impl ListMatcherBuilder {
pub fn new_and_condition(&mut self, matcher: impl Matcher) {
self.submatchers
.last_mut()
.unwrap()
.new_and_condition(matcher);
}
pub fn new_or_condition(&mut self, arg: &str) -> Result<(), Box<dyn Error>> {
self.submatchers.last_mut().unwrap().new_or_condition(arg)
}
pub fn check_new_and_condition(&mut self) -> Result<(), Box<dyn Error>> {
{
let child_or_matcher = &self.submatchers.last().unwrap();
let grandchild_and_matcher = &child_or_matcher.submatchers.last().unwrap();
if grandchild_and_matcher.submatchers.is_empty() {
return Err(From::from(
"invalid expression; you have used a binary operator '-a' \
with nothing before it.",
));
}
}
Ok(())
}
pub fn new_list_condition(&mut self) -> Result<(), Box<dyn Error>> {
{
let child_or_matcher = &self.submatchers.last().unwrap();
let grandchild_and_matcher = &child_or_matcher.submatchers.last().unwrap();
if grandchild_and_matcher.submatchers.is_empty() {
return Err(From::from(
"invalid expression; you have used a binary operator ',' \
with nothing before it.",
));
}
}
self.submatchers.push(OrMatcherBuilder::new());
Ok(())
}
pub fn new() -> Self {
let mut o = Self {
submatchers: Vec::new(),
};
o.submatchers.push(OrMatcherBuilder::new());
o
}
pub fn build(mut self) -> Box<dyn Matcher> {
if self.submatchers.len() == 1 {
return self.submatchers.pop().unwrap().build();
}
let mut submatchers = vec![];
for x in self.submatchers {
submatchers.push(x.build());
}
Box::new(ListMatcher::new(submatchers))
}
}
pub struct TrueMatcher;
impl Matcher for TrueMatcher {
fn matches(&self, _dir_entry: &WalkEntry, _: &mut MatcherIO) -> bool {
true
}
}
pub struct FalseMatcher;
impl Matcher for FalseMatcher {
fn matches(&self, _dir_entry: &WalkEntry, _: &mut MatcherIO) -> bool {
false
}
}
pub struct NotMatcher {
submatcher: Box<dyn Matcher>,
}
impl NotMatcher {
pub fn new(submatcher: impl Matcher) -> Self {
Self {
submatcher: submatcher.into_box(),
}
}
}
impl Matcher for NotMatcher {
fn matches(&self, dir_entry: &WalkEntry, matcher_io: &mut MatcherIO) -> bool {
!self.submatcher.matches(dir_entry, matcher_io)
}
fn has_side_effects(&self) -> bool {
self.submatcher.has_side_effects()
}
fn finished_dir(&self, dir: &Path, matcher_io: &mut MatcherIO) {
self.submatcher.finished_dir(dir, matcher_io);
}
fn finished(&self, matcher_io: &mut MatcherIO) {
self.submatcher.finished(matcher_io);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::find::matchers::quit::QuitMatcher;
use crate::find::matchers::tests::get_dir_entry_for;
use crate::find::tests::FakeDependencies;
use std::cell::RefCell;
use std::rc::Rc;
pub struct HasSideEffects;
impl Matcher for HasSideEffects {
fn matches(&self, _: &WalkEntry, _: &mut MatcherIO) -> bool {
false
}
fn has_side_effects(&self) -> bool {
true
}
}
struct Counter(Rc<RefCell<u32>>);
impl Matcher for Counter {
fn matches(&self, _: &WalkEntry, _: &mut MatcherIO) -> bool {
*self.0.borrow_mut() += 1;
true
}
}
#[test]
fn and_matches_works() {
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
let mut builder = AndMatcherBuilder::new();
let deps = FakeDependencies::new();
builder.new_and_condition(TrueMatcher);
assert!(builder.build().matches(&abbbc, &mut deps.new_matcher_io()));
builder = AndMatcherBuilder::new();
builder.new_and_condition(TrueMatcher);
builder.new_and_condition(FalseMatcher);
assert!(!builder.build().matches(&abbbc, &mut deps.new_matcher_io()));
}
#[test]
fn or_matches_works() {
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
let mut builder = OrMatcherBuilder::new();
let deps = FakeDependencies::new();
builder.new_and_condition(FalseMatcher);
assert!(!builder.build().matches(&abbbc, &mut deps.new_matcher_io()));
let mut builder = OrMatcherBuilder::new();
builder.new_and_condition(FalseMatcher);
builder.new_or_condition("-o").unwrap();
builder.new_and_condition(TrueMatcher);
assert!(builder.build().matches(&abbbc, &mut deps.new_matcher_io()));
}
#[test]
fn list_matches_works() {
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
let mut builder = ListMatcherBuilder::new();
let deps = FakeDependencies::new();
builder.new_and_condition(FalseMatcher);
assert!(!builder.build().matches(&abbbc, &mut deps.new_matcher_io()));
builder = ListMatcherBuilder::new();
builder.new_and_condition(FalseMatcher);
builder.new_list_condition().unwrap();
builder.new_and_condition(TrueMatcher);
assert!(builder.build().matches(&abbbc, &mut deps.new_matcher_io()));
builder = ListMatcherBuilder::new();
builder.new_and_condition(FalseMatcher);
builder.new_list_condition().unwrap();
builder.new_and_condition(TrueMatcher);
builder.new_list_condition().unwrap();
builder.new_and_condition(FalseMatcher);
assert!(!builder.build().matches(&abbbc, &mut deps.new_matcher_io()));
}
#[test]
fn true_matches_works() {
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
let matcher = TrueMatcher {};
let deps = FakeDependencies::new();
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
}
#[test]
fn false_matches_works() {
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
let matcher = FalseMatcher {};
let deps = FakeDependencies::new();
assert!(!matcher.matches(&abbbc, &mut deps.new_matcher_io()));
}
#[test]
fn and_has_side_effects_works() {
let mut builder = AndMatcherBuilder::new();
builder.new_and_condition(TrueMatcher);
assert!(!builder.build().has_side_effects());
builder = AndMatcherBuilder::new();
builder.new_and_condition(TrueMatcher);
builder.new_and_condition(HasSideEffects);
assert!(builder.build().has_side_effects());
}
#[test]
fn or_has_side_effects_works() {
let mut builder = OrMatcherBuilder::new();
builder.new_and_condition(TrueMatcher);
assert!(!builder.build().has_side_effects());
builder = OrMatcherBuilder::new();
builder.new_and_condition(TrueMatcher);
builder.new_and_condition(HasSideEffects);
assert!(builder.build().has_side_effects());
}
#[test]
fn list_has_side_effects_works() {
let mut builder = ListMatcherBuilder::new();
builder.new_and_condition(TrueMatcher);
assert!(!builder.build().has_side_effects());
builder = ListMatcherBuilder::new();
builder.new_and_condition(TrueMatcher);
builder.new_and_condition(HasSideEffects);
assert!(builder.build().has_side_effects());
}
#[test]
fn true_has_side_effects_works() {
let matcher = TrueMatcher {};
assert!(!matcher.has_side_effects());
}
#[test]
fn false_has_side_effects_works() {
let matcher = FalseMatcher {};
assert!(!matcher.has_side_effects());
}
#[test]
fn not_matches_works() {
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
let not_true = NotMatcher::new(TrueMatcher);
let not_false = NotMatcher::new(FalseMatcher);
let deps = FakeDependencies::new();
assert!(!not_true.matches(&abbbc, &mut deps.new_matcher_io()));
assert!(not_false.matches(&abbbc, &mut deps.new_matcher_io()));
}
#[test]
fn not_has_side_effects_works() {
let has_fx = NotMatcher::new(HasSideEffects);
let has_no_fx = NotMatcher::new(FalseMatcher);
assert!(has_fx.has_side_effects());
assert!(!has_no_fx.has_side_effects());
}
#[test]
fn and_quit_works() {
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
let mut builder = AndMatcherBuilder::new();
let deps = FakeDependencies::new();
let before = Rc::new(RefCell::new(0));
let after = Rc::new(RefCell::new(0));
builder.new_and_condition(Counter(before.clone()));
builder.new_and_condition(QuitMatcher);
builder.new_and_condition(Counter(after.clone()));
builder.build().matches(&abbbc, &mut deps.new_matcher_io());
assert_eq!(*before.borrow(), 1);
assert_eq!(*after.borrow(), 0);
}
#[test]
fn or_quit_works() {
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
let mut builder = OrMatcherBuilder::new();
let deps = FakeDependencies::new();
let before = Rc::new(RefCell::new(0));
let after = Rc::new(RefCell::new(0));
builder.new_and_condition(Counter(before.clone()));
builder.new_or_condition("-o").unwrap();
builder.new_and_condition(QuitMatcher);
builder.new_or_condition("-o").unwrap();
builder.new_and_condition(Counter(after.clone()));
builder.build().matches(&abbbc, &mut deps.new_matcher_io());
assert_eq!(*before.borrow(), 1);
assert_eq!(*after.borrow(), 0);
}
#[test]
fn list_quit_works() {
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
let mut builder = ListMatcherBuilder::new();
let deps = FakeDependencies::new();
let before = Rc::new(RefCell::new(0));
let after = Rc::new(RefCell::new(0));
builder.new_and_condition(Counter(before.clone()));
builder.new_list_condition().unwrap();
builder.new_and_condition(QuitMatcher);
builder.new_list_condition().unwrap();
builder.new_and_condition(Counter(after.clone()));
builder.build().matches(&abbbc, &mut deps.new_matcher_io());
assert_eq!(*before.borrow(), 1);
assert_eq!(*after.borrow(), 0);
}
}