litcheck_filecheck/pattern/search/
substring_set.rs1use aho_corasick::{AhoCorasick, AhoCorasickBuilder, MatchKind, StartKind};
2
3use crate::common::*;
4
5use super::AhoCorasickSearcher;
6
7pub struct SubstringSetSearcher<'a, 'patterns, 'input> {
8 buffer: &'input [u8],
9 crlf: bool,
10 patterns: Cow<'patterns, Vec<Span<Cow<'a, str>>>>,
13 pattern: AhoCorasick,
15 searcher: AhoCorasickSearcher<'input>,
17}
18impl<'a, 'patterns, 'input> SubstringSetSearcher<'a, 'patterns, 'input> {
19 pub fn new(
20 input: Input<'input>,
21 patterns: Cow<'patterns, Vec<Span<Cow<'a, str>>>>,
22 ) -> DiagResult<Self> {
23 let buffer = input.buffer();
24 let crlf = input.is_crlf();
25 let mut builder = AhoCorasickBuilder::new();
26 builder
27 .ascii_case_insensitive(false)
28 .match_kind(MatchKind::LeftmostLongest)
29 .start_kind(StartKind::Unanchored);
30 let pattern = builder
31 .build(patterns.iter().map(|p| p.as_bytes()))
32 .map_err(|err| {
33 let labels = patterns
34 .iter()
35 .map(|s| Label::new(s.span(), err.to_string()).into());
36 let diag = Diag::new("failed to build substring set searcher")
37 .and_labels(labels)
38 .with_help(format!(
39 "search configuration: {:?}, {:?}",
40 MatchKind::LeftmostLongest,
41 StartKind::Unanchored
42 ));
43 Report::from(diag)
44 })?;
45
46 let searcher = AhoCorasickSearcher::new(input.into());
47
48 Ok(Self {
49 buffer,
50 crlf,
51 patterns,
52 pattern,
53 searcher,
54 })
55 }
56
57 pub fn input(&self) -> Input<'input> {
58 let input = self.searcher.input();
59 Input::new(self.buffer, self.crlf)
60 .span(input.get_range())
61 .anchored(input.get_anchored().is_anchored())
62 }
63
64 pub fn num_patterns(&self) -> usize {
65 self.patterns.len()
66 }
67
68 pub fn pattern_span(&self, index: usize) -> SourceSpan {
69 self.patterns[index].span()
70 }
71}
72impl<'a, 'patterns, 'input> fmt::Debug for SubstringSetSearcher<'a, 'patterns, 'input> {
73 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74 f.debug_struct("SubstringSetSearcher")
75 .field("patterns", &self.patterns)
76 .field("kind", &self.pattern.kind())
77 .field("start_kind", &self.pattern.start_kind())
78 .field("match_kind", &self.pattern.match_kind())
79 .finish()
80 }
81}
82
83impl<'a, 'patterns, 'input> Spanned for SubstringSetSearcher<'a, 'patterns, 'input> {
84 fn span(&self) -> SourceSpan {
85 let start = self.patterns.iter().map(|p| p.start()).min().unwrap();
86 let end = self.patterns.iter().map(|p| p.end()).max().unwrap();
87 SourceSpan::from(start..end)
88 }
89}
90impl<'a, 'patterns, 'input> PatternSearcher<'input>
91 for SubstringSetSearcher<'a, 'patterns, 'input>
92{
93 type Input = aho_corasick::Input<'input>;
94 type PatternID = aho_corasick::PatternID;
95
96 fn input(&self) -> &Self::Input {
97 self.searcher.input()
98 }
99 fn last_match_end(&self) -> Option<usize> {
100 self.searcher.last_match_end()
101 }
102 fn set_last_match_end(&mut self, end: usize) {
103 self.searcher.set_last_match_end(end);
104 }
105 fn patterns_len(&self) -> usize {
106 self.num_patterns()
107 }
108 fn pattern_span(&self, id: Self::PatternID) -> SourceSpan {
109 SubstringSetSearcher::pattern_span(self, id.as_usize())
110 }
111 fn try_match_next<'context, C>(&mut self, context: &mut C) -> DiagResult<MatchResult<'input>>
112 where
113 C: Context<'input, 'context> + ?Sized,
114 {
115 use super::Input as SearchInput;
116 let result = self
117 .searcher
118 .advance(|input| self.pattern.try_find(input.as_input()));
119 if let Some(matched) = result {
120 let pattern_id = matched.pattern().as_usize();
121 let pattern_span = self.patterns[pattern_id].span();
122 Ok(MatchResult::ok(MatchInfo::new_with_pattern(
123 matched.range(),
124 pattern_span,
125 pattern_id,
126 )))
127 } else {
128 Ok(MatchResult::failed(
129 CheckFailedError::MatchNoneButExpected {
130 span: self.span(),
131 match_file: context.match_file(),
132 note: None,
133 },
134 ))
135 }
136 }
137}