1pub use ncp_matcher::pattern::{Atom, AtomKind, CaseMatching, Normalization, Pattern};
3use ncp_matcher::{Matcher, Utf32String};
4
5#[cfg(test)]
6mod tests;
7
8#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Default)]
9pub(crate) enum Status {
10 #[default]
11 Unchanged,
12 Update,
13 Rescore,
14}
15
16#[derive(Debug)]
18pub struct MultiPattern {
19 cols: Vec<(Pattern, Status)>,
20}
21
22impl Clone for MultiPattern {
23 fn clone(&self) -> Self {
24 Self {
25 cols: self.cols.clone(),
26 }
27 }
28
29 fn clone_from(&mut self, source: &Self) {
30 self.cols.clone_from(&source.cols);
31 }
32}
33
34impl MultiPattern {
35 pub fn new(columns: usize) -> Self {
37 Self {
38 cols: vec![Default::default(); columns],
39 }
40 }
41
42 #[allow(clippy::unnecessary_map_or)]
47 pub fn reparse(
48 &mut self,
49 column: usize,
50 new_text: &str,
51 case_matching: CaseMatching,
52 normalization: Normalization,
53 append: bool,
54 ) {
55 let old_status = self.cols[column].1;
56 if append
57 && old_status != Status::Rescore
58 && self.cols[column].0.atoms.last().map_or(true, |last| {
61 !last.negative
62 && last
63 .needle_text()
64 .chars()
65 .rev()
66 .take_while(|c| *c == '\\')
67 .count()
68 % 2
69 == 0
70 })
71 {
72 self.cols[column].1 = Status::Update;
73 } else {
74 self.cols[column].1 = Status::Rescore;
75 }
76 self.cols[column]
77 .0
78 .reparse(new_text, case_matching, normalization);
79 }
80
81 pub fn column_pattern(&self, column: usize) -> &Pattern {
83 &self.cols[column].0
84 }
85
86 pub(crate) fn status(&self) -> Status {
87 self.cols
88 .iter()
89 .map(|&(_, status)| status)
90 .max()
91 .unwrap_or(Status::Unchanged)
92 }
93
94 pub(crate) fn reset_status(&mut self) {
95 for (_, status) in &mut self.cols {
96 *status = Status::Unchanged;
97 }
98 }
99
100 pub fn score(&self, haystack: &[Utf32String], matcher: &mut Matcher) -> Option<u32> {
102 let mut score = 0;
104 for ((pattern, _), haystack) in self.cols.iter().zip(haystack) {
105 score += pattern.score(haystack.slice(..), matcher)?;
106 }
107 Some(score)
108 }
109
110 pub fn is_empty(&self) -> bool {
112 self.cols.iter().all(|(pat, _)| pat.atoms.is_empty())
113 }
114}