Skip to content

Commit acc2eea

Browse files
committed
finish regex implementation
1 parent c53b4a8 commit acc2eea

File tree

2 files changed

+162
-78
lines changed

2 files changed

+162
-78
lines changed

src/finder.rs

Lines changed: 159 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ use crate::sys::SysReadDirEntry;
55
use crate::{error::*, NonFatalErrorHandler};
66
#[cfg(feature = "regex")]
77
use regex::Regex;
8+
#[cfg(feature = "regex")]
9+
use std::borrow::Borrow;
810
use std::borrow::Cow;
911
use std::ffi::OsStr;
12+
#[cfg(feature = "regex")]
13+
use std::io;
1014
use std::path::{Component, Path, PathBuf};
1115
use std::vec;
1216

@@ -77,7 +81,7 @@ impl<TSys: Sys> Finder<TSys> {
7781
WhichFindIterator::new_cwd(path, cwd.as_ref(), self.sys, nonfatal_error_handler)
7882
}
7983
_ => {
80-
#[cfg(feature = "tracing")]
84+
#[cfg(feature = "tracing")]
8185
tracing::trace!("{} has no path seperators, so only paths in PATH environment variable will be searched.", path.display());
8286
// Search binary in PATHs(defined in environment variable).
8387
let paths = paths.ok_or(Error::CannotGetCurrentDirAndPathListEmpty)?;
@@ -100,30 +104,12 @@ impl<TSys: Sys> Finder<TSys> {
100104
self,
101105
binary_regex: impl std::borrow::Borrow<Regex>,
102106
paths: Option<T>,
103-
mut nonfatal_error_handler: F,
107+
nonfatal_error_handler: F,
104108
) -> Result<impl Iterator<Item = PathBuf>>
105109
where
106110
T: AsRef<OsStr>,
107111
{
108-
let p = paths.ok_or(Error::CannotGetCurrentDirAndPathListEmpty)?;
109-
let paths = self.sys.env_split_paths(p.as_ref());
110-
111-
let matching_re = paths
112-
.into_iter()
113-
.flat_map(move |p| self.sys.read_dir(&p))
114-
.flatten()
115-
.flatten()
116-
.map(|e| e.path())
117-
.filter(move |p| {
118-
if let Some(unicode_file_name) = p.file_name().unwrap().to_str() {
119-
binary_regex.borrow().is_match(unicode_file_name)
120-
} else {
121-
false
122-
}
123-
})
124-
.filter(move |p| is_valid(self.sys, p, &mut nonfatal_error_handler));
125-
126-
Ok(matching_re)
112+
WhichFindRegexIter::new(self.sys, paths, binary_regex, nonfatal_error_handler)
127113
}
128114
}
129115

@@ -142,21 +128,37 @@ impl<TSys: Sys, F: NonFatalErrorHandler> WhichFindIterator<TSys, F> {
142128
};
143129
Self {
144130
sys,
145-
paths: PathsIter { paths: vec![binary_name.to_absolute(cwd)].into_iter(), current_path_with_index: None, path_extensions },
131+
paths: PathsIter {
132+
paths: vec![binary_name.to_absolute(cwd)].into_iter(),
133+
current_path_with_index: None,
134+
path_extensions,
135+
},
146136
nonfatal_error_handler,
147137
}
148138
}
149139

150-
pub fn new_paths(binary_name: PathBuf, paths: Vec<PathBuf>, sys: TSys, nonfatal_error_handler: F) -> Self {
140+
pub fn new_paths(
141+
binary_name: PathBuf,
142+
paths: Vec<PathBuf>,
143+
sys: TSys,
144+
nonfatal_error_handler: F,
145+
) -> Self {
151146
let path_extensions = if sys.is_windows() {
152147
sys.env_windows_path_ext()
153148
} else {
154149
Cow::Borrowed(Default::default())
155150
};
156-
let paths = paths.iter().map(|p| tilde_expansion(&sys, p).join(&binary_name)).collect::<Vec<_>>();
151+
let paths = paths
152+
.iter()
153+
.map(|p| tilde_expansion(&sys, p).join(&binary_name))
154+
.collect::<Vec<_>>();
157155
Self {
158156
sys,
159-
paths: PathsIter { paths: paths.into_iter(), current_path_with_index: None, path_extensions },
157+
paths: PathsIter {
158+
paths: paths.into_iter(),
159+
current_path_with_index: None,
160+
path_extensions,
161+
},
160162
nonfatal_error_handler,
161163
}
162164
}
@@ -168,70 +170,74 @@ impl<TSys: Sys, F: NonFatalErrorHandler> Iterator for WhichFindIterator<TSys, F>
168170
fn next(&mut self) -> Option<Self::Item> {
169171
for path in &mut self.paths {
170172
if is_valid(&self.sys, &path, &mut self.nonfatal_error_handler) {
171-
return Some(correct_casing(&self.sys, path, &mut self.nonfatal_error_handler))
173+
return Some(correct_casing(
174+
&self.sys,
175+
path,
176+
&mut self.nonfatal_error_handler,
177+
));
172178
}
173179
}
174180
None
175181
}
176182
}
177183

178184
struct PathsIter<P>
179-
where
180-
P: Iterator<Item = PathBuf>,
181-
{
182-
paths: P,
183-
current_path_with_index: Option<(PathBuf, usize)>,
184-
path_extensions: Cow<'static, [String]>,
185-
}
185+
where
186+
P: Iterator<Item = PathBuf>,
187+
{
188+
paths: P,
189+
current_path_with_index: Option<(PathBuf, usize)>,
190+
path_extensions: Cow<'static, [String]>,
191+
}
186192

187-
impl<P> Iterator for PathsIter<P>
188-
where
189-
P: Iterator<Item = PathBuf>,
190-
{
191-
type Item = PathBuf;
193+
impl<P> Iterator for PathsIter<P>
194+
where
195+
P: Iterator<Item = PathBuf>,
196+
{
197+
type Item = PathBuf;
192198

193-
fn next(&mut self) -> Option<Self::Item> {
194-
if self.path_extensions.is_empty() {
195-
self.paths.next()
196-
} else if let Some((p, index)) = self.current_path_with_index.take() {
197-
let next_index = index + 1;
198-
if next_index < self.path_extensions.len() {
199-
self.current_path_with_index = Some((p.clone(), next_index));
200-
}
201-
// Append the extension.
202-
let mut p = p.into_os_string();
203-
p.push(&self.path_extensions[index]);
204-
let ret = PathBuf::from(p);
199+
fn next(&mut self) -> Option<Self::Item> {
200+
if self.path_extensions.is_empty() {
201+
self.paths.next()
202+
} else if let Some((p, index)) = self.current_path_with_index.take() {
203+
let next_index = index + 1;
204+
if next_index < self.path_extensions.len() {
205+
self.current_path_with_index = Some((p.clone(), next_index));
206+
}
207+
// Append the extension.
208+
let mut p = p.into_os_string();
209+
p.push(&self.path_extensions[index]);
210+
let ret = PathBuf::from(p);
211+
#[cfg(feature = "tracing")]
212+
tracing::trace!("possible extension: {}", ret.display());
213+
Some(ret)
214+
} else {
215+
let p = self.paths.next()?;
216+
if has_executable_extension(&p, &self.path_extensions) {
205217
#[cfg(feature = "tracing")]
206-
tracing::trace!("possible extension: {}", ret.display());
207-
Some(ret)
218+
tracing::trace!(
219+
"{} already has an executable extension, not modifying it further",
220+
p.display()
221+
);
208222
} else {
209-
let p = self.paths.next()?;
210-
if has_executable_extension(&p, &self.path_extensions) {
211-
#[cfg(feature = "tracing")]
212-
tracing::trace!(
213-
"{} already has an executable extension, not modifying it further",
214-
p.display()
215-
);
216-
} else {
217-
#[cfg(feature = "tracing")]
218-
tracing::trace!(
219-
"{} has no extension, using PATHEXT environment variable to infer one",
220-
p.display()
221-
);
222-
// Appended paths with windows executable extensions.
223-
// e.g. path `c:/windows/bin[.ext]` will expand to:
224-
// [c:/windows/bin.ext]
225-
// c:/windows/bin[.ext].COM
226-
// c:/windows/bin[.ext].EXE
227-
// c:/windows/bin[.ext].CMD
228-
// ...
229-
self.current_path_with_index = Some((p.clone(), 0));
230-
}
231-
Some(p)
223+
#[cfg(feature = "tracing")]
224+
tracing::trace!(
225+
"{} has no extension, using PATHEXT environment variable to infer one",
226+
p.display()
227+
);
228+
// Appended paths with windows executable extensions.
229+
// e.g. path `c:/windows/bin[.ext]` will expand to:
230+
// [c:/windows/bin.ext]
231+
// c:/windows/bin[.ext].COM
232+
// c:/windows/bin[.ext].EXE
233+
// c:/windows/bin[.ext].CMD
234+
// ...
235+
self.current_path_with_index = Some((p.clone(), 0));
232236
}
237+
Some(p)
233238
}
234239
}
240+
}
235241

236242
fn tilde_expansion<TSys: Sys>(sys: TSys, p: &Path) -> Cow<'_, Path> {
237243
let mut component_iter = p.components();
@@ -282,3 +288,81 @@ fn correct_casing<TSys: Sys, F: NonFatalErrorHandler>(
282288
}
283289
p
284290
}
291+
292+
#[cfg(feature = "regex")]
293+
struct WhichFindRegexIter<TSys: Sys, B: Borrow<Regex>, F: NonFatalErrorHandler> {
294+
sys: TSys,
295+
re: B,
296+
paths: vec::IntoIter<PathBuf>,
297+
nonfatal_error_handler: F,
298+
current_read_dir_iter: Option<Box<dyn Iterator<Item = io::Result<TSys::ReadDirEntry>>>>,
299+
}
300+
301+
#[cfg(feature = "regex")]
302+
impl<TSys: Sys, B: Borrow<Regex>, F: NonFatalErrorHandler> WhichFindRegexIter<TSys, B, F> {
303+
pub fn new<T: AsRef<OsStr>>(
304+
sys: TSys,
305+
paths: Option<T>,
306+
re: B,
307+
nonfatal_error_handler: F,
308+
) -> Result<Self> {
309+
let p = paths.ok_or(Error::CannotGetCurrentDirAndPathListEmpty)?;
310+
let paths = sys.env_split_paths(p.as_ref());
311+
Ok(WhichFindRegexIter {
312+
sys,
313+
re,
314+
paths: paths.into_iter(),
315+
nonfatal_error_handler,
316+
current_read_dir_iter: None,
317+
})
318+
}
319+
}
320+
321+
#[cfg(feature = "regex")]
322+
impl<TSys: Sys, B: Borrow<Regex>, F: NonFatalErrorHandler> Iterator
323+
for WhichFindRegexIter<TSys, B, F>
324+
{
325+
type Item = PathBuf;
326+
327+
fn next(&mut self) -> Option<Self::Item> {
328+
loop {
329+
if let Some(iter) = &mut self.current_read_dir_iter {
330+
match iter.next() {
331+
Some(Ok(path)) => {
332+
if let Some(unicode_file_name) = path.file_name().to_str() {
333+
if self.re.borrow().is_match(unicode_file_name) {
334+
return Some(path.path());
335+
} else {
336+
#[cfg(feature = "tracing")]
337+
tracing::debug!("regex filtered out {}", unicode_file_name);
338+
}
339+
} else {
340+
#[cfg(feature = "tracing")]
341+
tracing::debug!("regex unable to evaluate filename as it's not valid unicode. Lossy filename conversion: {}", path.file_name().to_string_lossy());
342+
}
343+
}
344+
Some(Err(e)) => {
345+
self.nonfatal_error_handler.handle(NonFatalError::Io(e));
346+
}
347+
None => {
348+
self.current_read_dir_iter = None;
349+
}
350+
}
351+
} else {
352+
let path = self.paths.next();
353+
if let Some(path) = path {
354+
match self.sys.read_dir(&path) {
355+
Ok(new_read_dir_iter) => {
356+
self.current_read_dir_iter = Some(new_read_dir_iter);
357+
}
358+
Err(e) => {
359+
self.nonfatal_error_handler.handle(NonFatalError::Io(e));
360+
}
361+
}
362+
} else {
363+
return None;
364+
}
365+
}
366+
}
367+
}
368+
}

src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
//! # }
1717
//! ```
1818
19-
#![forbid(unsafe_code)]
20-
2119
mod checker;
2220
mod error;
2321
mod finder;
@@ -363,7 +361,9 @@ impl<'a, TSys: Sys + 'a, F: NonFatalErrorHandler + 'a> WhichConfig<TSys, F> {
363361
}
364362
#[cfg(feature = "regex")]
365363
{
366-
if matches!(self.cwd, CwdOption::UseSysCwd) || matches!(self.cwd, CwdOption::UseCustomCwd(_)) {
364+
if matches!(self.cwd, CwdOption::UseSysCwd)
365+
|| matches!(self.cwd, CwdOption::UseCustomCwd(_))
366+
{
367367
panic!("which can't use regex and cwd at the same time!")
368368
}
369369
if self.binary_name.is_some() {

0 commit comments

Comments
 (0)