simple_text_pattern/
lib.rs

1/*!
2This crate provides a library for compiling and matching simple text patterns.
3
4# Example
5Passed pattern `some*text` will be compiled into equivalent regexp `^some.*text$`.
6
7# Syntax
8* `*` - one or more any symbol.
9* `any other text` - interpreted as simple text.
10
11# Usage
12```rust
13use simple_text_pattern::Pattern;
14let pattern = Pattern::new("some*text").expect("Unable to compile pattern");
15assert_eq!(true, pattern.is_match("sometext"));
16assert_eq!(true, pattern.is_match("some text"));
17assert_eq!(false, pattern.is_match("not some text"));
18```
19
20*/
21mod error;
22mod parser;
23mod token;
24pub use self::error::Error;
25use self::token::Token;
26use self::parser::Parser;
27
28#[derive(Debug, Clone)]
29/// Structure represents pattern.
30pub struct Pattern {
31    tokens: Vec<Token>,
32}
33
34impl Pattern {
35    /// Parses input pattern.
36    pub fn new(input: &str) -> Result<Pattern, Error> {
37        let parser = Parser::new();
38        let result = parser.parse(input)?;
39        return Ok(result);
40    }
41
42    /// Checks is input string matches current pattern.
43    pub fn is_match(&self, input: &str) -> bool {
44        let mut inputs = vec![input];
45        let mut may_skip_chars = false;
46        for token in self.tokens.iter() {
47            match token {
48                &Token::Text(ref text) => {
49                    inputs = Self::handle_text(inputs, text, may_skip_chars);
50                    may_skip_chars = false;
51                },
52                &Token::Any => {
53                    may_skip_chars = true;
54                },
55            }
56        }
57        if may_skip_chars && !inputs.is_empty() {
58            return true;
59        }
60        for input in inputs.iter() {
61            if input.is_empty() {
62                return true;
63            }
64        }
65        return false;
66    }
67
68    fn handle_text<'a>(inputs: Vec<&'a str>, need: &str, may_skip_chars: bool) -> Vec<&'a str> {
69        let mut result = Vec::with_capacity(inputs.len());
70        for input in inputs {
71            let end_index = input.len();
72            for (found_index, _) in input.match_indices(need) {
73                if !may_skip_chars && found_index != 0 {
74                    continue;
75                }
76                let out_index = found_index + need.len();
77                if out_index >= end_index {
78                    result.push("");
79                } else {
80                    result.push(&input[out_index..end_index]);
81                }
82            }
83        }
84        result.sort();
85        result.dedup_by(|a, b| {
86            return a == b;
87        });
88        return result;
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use crate::Pattern;
95
96    #[test]
97    fn test_empty_pattern() {
98        assert_eq!(true, Pattern::new("").is_err());
99    }
100
101    #[test]
102    fn test_text_pattern() {
103        let pattern = Pattern::new("abcdef").expect("Unable to build text pattern");
104        assert_eq!(false, pattern.is_match(""));
105        assert_eq!(true, pattern.is_match("abcdef"));
106        assert_eq!(false, pattern.is_match("1abcdef"));
107        assert_eq!(false, pattern.is_match("abcdef1"));
108        assert_eq!(false, pattern.is_match("abc"));
109    }
110
111    #[test]
112    fn test_any_pattern() {
113        let pattern = Pattern::new("*").expect("Unable to build any pattern");
114        assert_eq!(true, pattern.is_match(""));
115        assert_eq!(true, pattern.is_match("abcdef"));
116        assert_eq!(true, pattern.is_match("1abcdef"));
117        assert_eq!(true, pattern.is_match("abcdef1"));
118        assert_eq!(true, pattern.is_match("abc"));
119    }
120
121    #[test]
122    fn test_text_n_any_pattern() {
123        let pattern = Pattern::new("abc*").expect("Unable to build any pattern with text pattern");
124        assert_eq!(false, pattern.is_match(""));
125        assert_eq!(true, pattern.is_match("abcdef"));
126        assert_eq!(false, pattern.is_match("1abcdef"));
127        assert_eq!(true, pattern.is_match("abcdef1"));
128        assert_eq!(true, pattern.is_match("abc"));
129        assert_eq!(false, pattern.is_match("q"));
130        assert_eq!(false, pattern.is_match("qwe"));
131
132        let pattern = Pattern::new("*abc").expect("Unable to build any pattern with text pattern");
133        assert_eq!(false, pattern.is_match(""));
134        assert_eq!(false, pattern.is_match("abcdef"));
135        assert_eq!(false, pattern.is_match("1abcdef"));
136        assert_eq!(false, pattern.is_match("abcdef1"));
137        assert_eq!(true, pattern.is_match("abc"));
138        assert_eq!(true, pattern.is_match("svsdfvsdfabc"));
139
140        let pattern = Pattern::new("abc*def").expect("Unable to build any pattern with text pattern");
141        assert_eq!(false, pattern.is_match(""));
142        assert_eq!(true, pattern.is_match("abcdef"));
143        assert_eq!(false, pattern.is_match("1abcdef"));
144        assert_eq!(false, pattern.is_match("abcdef1"));
145        assert_eq!(false, pattern.is_match("abc"));
146        assert_eq!(true, pattern.is_match("abcabcdefdef"));
147        assert_eq!(true, pattern.is_match("abc1def"));
148        assert_eq!(true, pattern.is_match("abc1sdfvsdvdef"));
149    }
150}