gmec 0.0.3

Gabe's Monolithic Everything Crate - A library for all your (my) general-purpose needs
Documentation
pub struct PatternMatch<T> {
    pub index: usize,
    pub length: usize,
    pub slice: T,
}

impl<T> PatternMatch<T> {
    pub fn start(&self) -> usize {
        return self.index;
    }

    pub fn end(&self) -> usize {
        return self.index+self.length;
    }

    pub fn range(&self) -> std::ops::Range<usize> {
        return self.index..self.index+self.length;
    }
}

pub trait PatternMatcher<'a, P> {
    fn find_first_from(&'a self, pattern: &P, byte_offset: usize) -> Option<PatternMatch<&'a Self>>;

    #[inline(always)]
    fn find_first(&'a self, pattern: &P) -> Option<PatternMatch<&'a Self>>{
        return self.find_first_from(pattern, 0);
    }

    fn find_every_from(&'a self, pattern: &P, byte_offset: usize) -> Option<Vec<PatternMatch<&'a Self>>> {
        let mut total_offset: usize = byte_offset;
        let mut matches = Vec::new();
        loop {
            match self.find_first_from(pattern, total_offset) {
                Some(found_match) => {
                    total_offset = found_match.end();
                    matches.push(found_match)
                },
                None => break
            }
        }
        if matches.is_empty() {
            return None;
        }
        return Some(matches);
    }

    #[inline(always)]
    fn find_every(&'a self, pattern: &P) -> Option<Vec<PatternMatch<&'a Self>>> {
        return self.find_every_from(pattern, 0);
    }

    fn find_any_from<IIP: IntoIterator<Item = P>>(&'a self, patterns: IIP, byte_offset: usize) -> Option<PatternMatch<&'a Self>> {
        let mut matches = Vec::new();
        for pattern in patterns.into_iter() {
            if let Some(found_match) = self.find_first_from(&pattern, byte_offset) {
                matches.push(found_match);
            }
        }
        let mut earliest_match: Option<PatternMatch<&'a Self>> = None;
        let mut earliest_index: usize = usize::MAX;
        for current_match in matches {
            if earliest_match.is_none() || current_match.index < earliest_index {
                earliest_index = current_match.index;
                earliest_match = Some(current_match);
            }
        }
        return earliest_match;
    }

    #[inline(always)]
    fn find_any<IIP: IntoIterator<Item = P>>(&'a self, patterns: IIP) -> Option<PatternMatch<&'a Self>> {
        self.find_any_from(patterns, 0)
    }

    fn find_all_from<IIP: IntoIterator<Item = P>>(&'a self, patterns: IIP, byte_offset: usize) -> Option<Vec<PatternMatch<&'a Self>>> {
        let mut matches = Vec::new();
        for pattern in patterns.into_iter() {
            if let Some(mut found_matches) = self.find_every_from(&pattern, byte_offset) {
                matches.append(&mut found_matches);
            }
        }
        if matches.is_empty() {
            return None;
        }
        return Some(matches);
    }

    #[inline(always)]
    fn find_all<IIP: IntoIterator<Item = P>>(&'a self, patterns: IIP) -> Option<Vec<PatternMatch<&'a Self>>> {
        return self.find_all_from(patterns, 0)
    }
}

impl<'a, P> PatternMatcher<'a, P> for str
where P: AsRef<str> {
    fn find_first_from(&'a self, pattern: &P, byte_offset: usize) -> Option<PatternMatch<&'a str>> {
        let pattern_str = pattern.as_ref();
        if let Some(index) = self[byte_offset..].find(pattern_str) {
            let byte_len = pattern_str.len();
            let slice = &self[byte_offset + index..byte_offset + index + byte_len];
            Some(PatternMatch { index: byte_offset + index, length: byte_len, slice })
        } else {
            None
        }
    }
}


impl<'a, P, T> PatternMatcher<'a, P> for [T]
where P: AsRef<[T]>,
T: PartialEq {
    fn find_first_from(&'a self, pattern: &P, byte_offset: usize) -> Option<PatternMatch<&'a Self>> {
        let pattern_slice = pattern.as_ref();
        let pattern_len = pattern_slice.len();
        let offset_slice = &self[byte_offset..];
        for compare_start in 0..offset_slice.len() {
            let compare_end = compare_start + pattern_len;
            if compare_end > offset_slice.len() {
                return None;
            }
            let compare_slice = &offset_slice[compare_start..compare_end];
            if compare_slice == pattern_slice {
                let true_index = compare_start+byte_offset;
                return Some(PatternMatch { index: true_index, length: pattern_len, slice: &self[true_index..true_index+pattern_len] })
            }
        }
        return None
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pattern_match_start() {
        let pm = PatternMatch { index: 2, length: 3, slice: "hello" };
        assert_eq!(pm.start(), 2);
    }

    #[test]
    fn test_pattern_match_end() {
        let pm = PatternMatch { index: 2, length: 3, slice: "hello" };
        assert_eq!(pm.end(), 5);
    }

    #[test]
    fn test_pattern_match_range() {
        let pm = PatternMatch { index: 2, length: 3, slice: "hello" };
        assert_eq!(pm.range(), 2..5);
    }

    #[test]
    fn test_find_first() {
        let s = "hello world";
        let pm = s.find_first(&"world").unwrap();
        assert_eq!(pm.start(), 6);
        assert_eq!(pm.end(), 11);
        assert_eq!(pm.slice, "world");
    }

    #[test]
    fn test_find_every() {
        let s = "hello world";
        let pms = s.find_every(&"l").unwrap();
        assert_eq!(pms.len(), 3);
        assert_eq!(pms[0].start(), 2);
        assert_eq!(pms[0].end(), 3);
        assert_eq!(pms[1].start(), 3);
        assert_eq!(pms[1].end(), 4);
        assert_eq!(pms[2].start(), 9);
        assert_eq!(pms[2].end(), 10);
    }

    #[test]
    fn test_find_any() {
        let s = "hello world";
        let pm = s.find_any(&["world", "foo", "bar"]).unwrap();
        assert_eq!(pm.start(), 6);
        assert_eq!(pm.end(), 11);
        assert_eq!(pm.slice, "world");
    }

    #[test]
    fn test_find_all() {
        let s = "hello world";
        let pms = s.find_all(&["l", "o"]).unwrap();
        assert_eq!(pms.len(), 5);
        assert_eq!(pms[0].start(), 2);
        assert_eq!(pms[0].end(), 3);
        assert_eq!(pms[1].start(), 3);
        assert_eq!(pms[1].end(), 4);
        assert_eq!(pms[2].start(), 9);
        assert_eq!(pms[2].end(), 10);
        assert_eq!(pms[3].start(), 4);
        assert_eq!(pms[3].end(), 5);
        assert_eq!(pms[4].start(), 7);
        assert_eq!(pms[4].end(), 8);
    }
}