rush_pat 0.1.0

Core pattern matching engine for rush shell
Documentation
use std::ops::{BitAnd, BitAndAssign};
use std::clone::Clone;
use regex::Regex;

#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum Subsumption {
    Disjoint,
    Contains,
    Same,
    Overlaps,
    ContainedBy,
}

impl Subsumption {
    pub fn reverse(self) -> Subsumption {
        use self::Subsumption::*;

        match self {
            Disjoint => Disjoint,
            Contains => ContainedBy,
            Same => Same,
            Overlaps => Overlaps,
            ContainedBy => Contains,
        }
    }

    pub fn same_or_contains(self) -> bool {
        use self::Subsumption::*;

        return self == Same || self == Contains
    }

    pub fn same_or_contained_by(self) -> bool {
        use self::Subsumption::*;

        return self == Same || self == ContainedBy
    }
}

impl BitAndAssign for Subsumption {
    fn bitand_assign(&mut self, rhs: Self) {
        use self::Subsumption::*;
        *self = match (*self, rhs) {
            (Disjoint, _) | (_, Disjoint) => Disjoint,
            (Same, Same) => Same,
            (Same, x) | (x, Same) => x,
            (Overlaps, _) | (_, Overlaps) | (Contains, ContainedBy) | (ContainedBy, Contains) => Overlaps,
            (Contains, Contains) => Contains,
            (ContainedBy, ContainedBy) => ContainedBy,
        }
    }
}

impl BitAnd for Subsumption {
    type Output = Subsumption;

    fn bitand(self, rhs: Self) -> Self::Output {
        let mut ret = self.clone();
        ret.bitand_assign(rhs);
        ret
    }
}

pub trait Subsumes<RHS = Self> where RHS: ?Sized {
    fn subsumes(&self, other: &RHS) -> Subsumption;
}

pub trait IterOps<T, I>: IntoIterator<Item = T>
    where I: IntoIterator<Item = T>,
          T: PartialEq {
    fn intersect(self, other: I) -> Vec<T>;
    fn difference(self, other: I) -> Vec<T>;
}

impl Subsumes<str> for str {
    fn subsumes(&self, ss: &str) -> Subsumption {
        use self::Subsumption::*;

        if self == ss {
            Same
        } else {
            Disjoint
        }
    }
}

impl Subsumes<str> for Vec<String> {
    fn subsumes(&self, ss: &str) -> Subsumption {
        use self::Subsumption::*;

        if self.iter().any(|os| ss == os) {
            Contains
        } else {
            Disjoint
        }
    }
}

impl Subsumes<Vec<String>> for str {
    fn subsumes(&self, v: &Vec<String>) -> Subsumption {
        v.subsumes(self).reverse()
    }
}

fn intersect<T, I>(this: I, other: I) -> Vec<T> where I: IntoIterator<Item = T>, T: PartialEq {
    let mut common = Vec::new();
    let mut v_other: Vec<_> = other.into_iter().collect();

    for e1 in this.into_iter() {
        if let Some(pos) = v_other.iter().position(|e2| e1 == *e2) {
            common.push(e1);
            v_other.remove(pos);
        }
    }

    common
}

impl Subsumes for Vec<String> {
    fn subsumes(&self, o: &Self) -> Subsumption {
        use self::Subsumption::*;

        let inter = intersect(self.iter(), o.iter());
        if inter.len() == 0 {
            Disjoint
        } else {
            match (inter.len() == self.len(), inter.len() == o.len()) {
                (true, true) => Same,
                (false, false) => Overlaps,
                (true, false) => ContainedBy,
                (false, true) => ContainedBy,
            }
        }
    }
}

impl Subsumes<Regex> for str {
    fn subsumes(&self, rex: &Regex) -> Subsumption {
        use self::Subsumption::*;

        if rex.is_match(self) {
            ContainedBy
        } else {
            Disjoint
        }
    }
}

impl Subsumes<str> for Regex {
    fn subsumes(&self, s: &str) -> Subsumption {
        s.subsumes(self).reverse()
    }
}

impl Subsumes<Regex> for Vec<String> {
    fn subsumes(&self, rex: &Regex) -> Subsumption {
        use self::Subsumption::*;

        let count = self.iter().fold(0, |acc, x| acc + if rex.is_match(x) { 1 } else { 0 });
        if count == 0 {
            Disjoint
        } else if count == self.len() {
            ContainedBy
        } else {
            Overlaps
        }
    }
}

impl Subsumes<Vec<String>> for Regex {
    fn subsumes(&self, v: &Vec<String>) -> Subsumption {
        v.subsumes(self).reverse()
    }
}