1use std::ffi::OsStr;
13use std::num::NonZeroUsize;
14use std::path::Path;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct Accept;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct NoSymlink;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub struct MaxDepth(pub(crate) NonZeroUsize);
29
30impl MaxDepth {
31 const SINGLE_DEPTH: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(1) };
32
33 pub fn new(max_depth: NonZeroUsize) -> Self {
44 Self(max_depth)
45 }
46
47 pub fn single_depth() -> Self {
58 Self(Self::SINGLE_DEPTH)
59 }
60}
61
62#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
72pub struct Closure<F> {
73 closure: F,
74}
75
76#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
82pub struct Extension<S>(pub(crate) S);
83
84impl<S> Extension<S> {
85 pub fn new(extension: S) -> Self {
86 Self(extension)
87 }
88}
89
90pub trait Filter {
91 fn filter(&self, base_path: &Path, path_to_visit: &Path) -> bool;
101
102 fn should_emit(&self, _: &Path) -> bool {
104 true
105 }
106
107 fn and<B>(self, other: B) -> And<Self, B>
109 where
110 Self: Sized,
111 B: Filter,
112 {
113 And { a: self, b: other }
114 }
115
116 fn or<B>(self, other: B) -> Or<Self, B>
118 where
119 Self: Sized,
120 B: Filter,
121 {
122 Or { a: self, b: other }
123 }
124
125 fn not(self) -> Not<Self>
127 where
128 Self: Sized,
129 {
130 Not { a: self }
131 }
132}
133
134impl Filter for Accept {
135 fn filter(&self, _: &Path, _: &Path) -> bool {
136 true
137 }
138}
139
140impl Filter for NoSymlink {
141 fn filter(&self, _: &Path, p: &Path) -> bool {
142 !p.is_dir() || !p.is_symlink()
143 }
144}
145
146impl Filter for MaxDepth {
147 fn filter(&self, base_path: &Path, p: &Path) -> bool {
148 let depth = base_path.ancestors().count();
149 let MaxDepth(max_depth) = *self;
150 let ancestors = p.ancestors().count();
151 if ancestors >= depth {
152 ancestors - depth < max_depth.get()
153 } else {
154 false
155 }
156 }
157}
158
159impl<S> Filter for Extension<S>
160where
161 S: AsRef<OsStr>,
162{
163 fn filter(&self, _: &Path, _: &Path) -> bool {
164 true
165 }
166
167 fn should_emit(&self, dir: &Path) -> bool {
168 dir.extension() == Some(self.0.as_ref())
169 }
170}
171
172impl<F> Closure<F> {
173 pub fn new(closure: F) -> Self {
174 Self { closure }
175 }
176
177 pub fn from_fn(closure: F) -> Self
178 where
179 F: Fn(&Path) -> bool,
180 {
181 Self { closure }
182 }
183}
184
185impl<F> From<F> for Closure<F> {
186 fn from(f: F) -> Self {
187 Self { closure: f }
188 }
189}
190
191impl<F> Filter for Closure<F>
192where
193 F: Fn(&Path, &Path) -> bool,
194{
195 fn filter(&self, base_dir: &Path, directory: &Path) -> bool {
196 (self.closure)(base_dir, directory)
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
201pub struct And<A, B> {
202 a: A,
203 b: B,
204}
205
206#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
207pub struct Or<A, B> {
208 a: A,
209 b: B,
210}
211
212#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
213pub struct Not<A> {
214 a: A,
215}
216
217#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
218pub struct This<A> {
219 a: A,
220}
221
222impl<A> This<A> {
223 pub fn new(a: A) -> Self {
224 Self { a }
225 }
226}
227
228impl<A, B> Filter for And<A, B>
229where
230 A: Filter,
231 B: Filter,
232{
233 fn filter(&self, base_path: &Path, directory: &Path) -> bool {
234 self.a.filter(base_path, directory) && self.b.filter(base_path, directory)
235 }
236
237 fn should_emit(&self, dir: &Path) -> bool {
238 self.a.should_emit(dir) && self.b.should_emit(dir)
239 }
240}
241
242impl<A, B> Filter for Or<A, B>
243where
244 A: Filter,
245 B: Filter,
246{
247 fn filter(&self, base_path: &Path, directory: &Path) -> bool {
248 self.a.filter(base_path, directory) || self.b.filter(base_path, directory)
249 }
250
251 fn should_emit(&self, dir: &Path) -> bool {
252 self.a.should_emit(dir) || self.b.should_emit(dir)
253 }
254}
255
256impl<A> Filter for Not<A>
257where
258 A: Filter,
259{
260 fn filter(&self, base_path: &Path, directory: &Path) -> bool {
261 !self.a.filter(base_path, directory)
262 }
263
264 fn should_emit(&self, dir: &Path) -> bool {
265 self.a.should_emit(dir)
266 }
267}
268
269impl<A> Filter for This<A>
270where
271 A: Filter,
272{
273 fn filter(&self, base_path: &Path, directory: &Path) -> bool {
274 self.a.filter(base_path, directory)
275 }
276
277 fn should_emit(&self, dir: &Path) -> bool {
278 self.a.should_emit(dir)
279 }
280}