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}