morel/
finder.rs

1use crate::algorithm::{ahocorasick::AhoCorasick, Algorithm, Kind, Wrapper};
2
3/// Provides methods to search text.
4#[derive(Clone)]
5pub struct Finder {
6    algorithm: Wrapper,
7}
8
9impl Finder {
10    /// Create a new instance of Finder with the provided Syntax and
11    /// Algorithm.
12    ///
13    /// The Finder instance will use the algorithm described by Kind
14    /// to search for patterns.
15    pub fn new(syntax: Syntax, a: Kind) -> Self {
16        match a {
17            Kind::AhoCorasick => Finder {
18                algorithm: Wrapper::AhoCorasick(AhoCorasick::new(syntax.patterns)),
19            },
20        }
21    }
22
23    /// Search the given text starting at 'n' for a match on any pattern
24    /// of the given syntax.
25    ///
26    /// # Examples
27    ///
28    /// ```
29    /// use morel::{Syntax, Finder, Kind};
30    ///
31    /// // We want to search for these patterns.
32    /// // Each pattern has a unique (within the vec) identifier and a literal value.
33    /// let patterns = vec![(0, "abc".into()), (1, "def".into())];
34    ///
35    /// // Patterns are cloned to allow for an assertion further down.
36    /// let syntax = Syntax::new(patterns.clone());
37    /// let mut finder = Finder::new(syntax, Kind::AhoCorasick);
38    ///
39    /// // The text to be searched.
40    /// let text = "123abc";
41    ///
42    /// let result = finder.next(text, 0);
43    ///
44    /// // A match is found.
45    /// assert_eq!(result, Some((0, 3, 6)));
46    ///
47    /// let unwrapped = result.unwrap();
48    /// let id = unwrapped.0;
49    /// let start = unwrapped.1;
50    /// let end = unwrapped.2;
51    ///
52    /// // The text between the indices is equal to the pattern literal.
53    /// assert_eq!(
54    ///    &text[start..end],
55    ///    patterns.into_iter().find(|e| e.0 == id).unwrap().1
56    /// );
57    /// ```
58    pub fn next(&self, text: &str, at: usize) -> Option<(usize, usize, usize)> {
59        self.algorithm.find(text, at).map(|mat| {
60            let kind = mat.pattern_id();
61            (kind, mat.begin(), mat.end())
62        })
63    }
64
65    /// Determine if the given text starting at 'n' begins with a match
66    /// on any pattern of the given syntax.
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// use morel::{Syntax, Finder, Kind};
72    ///
73    /// // We want to search for these patterns.
74    /// // Each pattern has a unique (within the vec) identifier and a literal value.
75    /// let patterns = vec![(0, "abc".into()), (1, "def".into())];
76    ///
77    /// // Patterns are cloned to allow for an assertion further down.
78    /// let syntax = Syntax::new(patterns.clone());
79    /// let finder = Finder::new(syntax, Kind::AhoCorasick);
80    ///
81    /// // The text to be searched.
82    /// let text = "abc123";
83    /// let result = finder.starts(text, 0);
84    ///
85    /// // A match is found.
86    /// assert_eq!(result, Some((0, 3)));
87    ///
88    /// let unwrapped = result.unwrap();
89    /// let id = unwrapped.0;
90    /// let length = unwrapped.1;
91    ///
92    /// // The text between the indices is equal to the pattern literal.
93    /// assert_eq!(
94    ///    &text[0..length],
95    ///    patterns.into_iter().find(|e| e.0 == id).unwrap().1
96    /// );
97    /// ```
98    pub fn starts(&self, text: &str, at: usize) -> Option<(usize, usize)> {
99        let (kind, i, j) = self.next(text, at)?;
100        if at == i {
101            Some((kind, j))
102        } else {
103            None
104        }
105    }
106}
107
108/// Represents a set of unique patterns that can be searched for.
109pub struct Syntax {
110    pub(crate) patterns: Vec<(usize, String)>,
111}
112
113impl Syntax {
114    /// Create a new instance of Syntax from the given patterns.
115    pub fn new(patterns: Vec<(usize, String)>) -> Self {
116        Syntax { patterns }
117    }
118}