fmf_core\query/
matchers.rs1use super::compile::{CTerm, Matcher};
2use super::memo::PathMemos;
3use crate::index::{EntryId, VolumeIndex};
4
5#[derive(Default)]
10pub(super) struct EvalCtx {
11 lower_path: Vec<u8>,
12 orig_path: Vec<u8>,
13 lower_built: bool,
14 orig_built: bool,
15}
16
17impl EvalCtx {
18 #[inline]
19 const fn reset(&mut self) {
20 self.lower_built = false;
21 self.orig_built = false;
22 }
23
24 #[inline]
25 fn lower_path<'a>(&'a mut self, idx: &VolumeIndex, memo: &PathMemos, id: EntryId) -> &'a [u8] {
26 if !self.lower_built {
27 self.lower_path.clear();
28 if id != VolumeIndex::ROOT {
29 self.lower_path
30 .extend_from_slice(memo.lower_prefix(idx.parent(id)));
31 }
32 self.lower_path.extend_from_slice(idx.lower_name(id));
33 self.lower_built = true;
34 }
35 &self.lower_path
36 }
37
38 #[inline]
39 fn orig_path<'a>(&'a mut self, idx: &VolumeIndex, memo: &PathMemos, id: EntryId) -> &'a [u8] {
40 if !self.orig_built {
41 self.orig_path.clear();
42 if id != VolumeIndex::ROOT {
43 self.orig_path
44 .extend_from_slice(memo.orig_prefix(idx.parent(id)));
45 }
46 self.orig_path.extend_from_slice(idx.name(id));
47 self.orig_built = true;
48 }
49 &self.orig_path
50 }
51}
52
53#[inline]
59fn exact_hay<'a>(idx: &'a VolumeIndex, t: &CTerm, id: EntryId) -> Option<&'a [u8]> {
60 if idx.is_fold_identical(id) {
61 if t.exact_needle_unstable {
62 None
63 } else {
64 Some(idx.lower_name(id))
65 }
66 } else {
67 Some(idx.name(id))
68 }
69}
70
71#[inline]
72fn eval(idx: &VolumeIndex, memo: &PathMemos, ctx: &mut EvalCtx, t: &CTerm, id: EntryId) -> bool {
73 match &t.matcher {
74 Matcher::True => true,
75 Matcher::Size { min, max } => !idx.is_dir(id) && (*min..=*max).contains(&idx.size(id)),
76 Matcher::Mtime { min, max } => (*min..=*max).contains(&idx.mtime(id)),
77 Matcher::IsDir(d) => idx.is_dir(id) == *d,
78 Matcher::Ext { exts } => {
79 let lower = idx.lower_name(id);
80 match memchr::memrchr(b'.', lower) {
81 Some(p) if !idx.is_dir(id) => {
82 let ext = &lower[p + 1..];
83 exts.iter().any(|e| e.as_slice() == ext)
84 }
85 _ => false,
86 }
87 }
88 Matcher::NameSub { finder, folded } => {
89 let hay = if *folded {
90 idx.lower_name(id)
91 } else {
92 match exact_hay(idx, t, id) {
93 Some(h) => h,
94 None => return false,
95 }
96 };
97 finder.find(hay).is_some()
98 }
99 Matcher::NamePrefix { bytes, folded } => {
100 let hay = if *folded {
101 idx.lower_name(id)
102 } else {
103 match exact_hay(idx, t, id) {
104 Some(h) => h,
105 None => return false,
106 }
107 };
108 hay.starts_with(bytes)
109 }
110 Matcher::NameSuffix { bytes, folded } => {
111 let hay = if *folded {
112 idx.lower_name(id)
113 } else {
114 match exact_hay(idx, t, id) {
115 Some(h) => h,
116 None => return false,
117 }
118 };
119 hay.ends_with(bytes)
120 }
121 Matcher::NameRegex { re } => re.is_match(idx.name(id)),
122 Matcher::PathSub { finder, folded } => {
123 let hay = if *folded {
124 ctx.lower_path(idx, memo, id)
125 } else {
126 ctx.orig_path(idx, memo, id)
127 };
128 finder.find(hay).is_some()
129 }
130 Matcher::PathRegex { re } => re.is_match(ctx.orig_path(idx, memo, id)),
131 }
132}
133
134#[inline]
135pub(super) fn terms_match(
136 idx: &VolumeIndex,
137 memo: &PathMemos,
138 ctx: &mut EvalCtx,
139 terms: &[CTerm],
140 id: EntryId,
141) -> bool {
142 terms_match_iter(idx, memo, ctx, terms.iter(), id)
143}
144
145#[inline]
148pub(super) fn terms_match_iter<'a>(
149 idx: &VolumeIndex,
150 memo: &PathMemos,
151 ctx: &mut EvalCtx,
152 terms: impl Iterator<Item = &'a CTerm>,
153 id: EntryId,
154) -> bool {
155 ctx.reset();
156 for t in terms {
157 if eval(idx, memo, ctx, t, id) == t.negated {
158 return false;
159 }
160 }
161 true
162}