1mod checker;
18mod error;
19mod finder;
20#[cfg(windows)]
21mod helper;
22
23#[cfg(feature = "regex")]
24use std::borrow::Borrow;
25use std::env;
26use std::fmt;
27use std::path;
28
29use std::ffi::{OsStr, OsString};
30
31use crate::checker::{CompositeChecker, ExecutableChecker, ExistedChecker};
32pub use crate::error::*;
33use crate::finder::Finder;
34
35pub fn which<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
57 which_all(binary_name).and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
58}
59
60pub fn which_global<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
81 which_all_global(binary_name).and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
82}
83
84pub fn which_all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = path::PathBuf>> {
86 let cwd = env::current_dir().ok();
87
88 let binary_checker = build_binary_checker();
89
90 let finder = Finder::new();
91
92 finder.find(binary_name, env::var_os("PATH"), cwd, binary_checker)
93}
94
95pub fn which_all_global<T: AsRef<OsStr>>(
97 binary_name: T,
98) -> Result<impl Iterator<Item = path::PathBuf>> {
99 let binary_checker = build_binary_checker();
100
101 let finder = Finder::new();
102
103 finder.find(
104 binary_name,
105 env::var_os("PATH"),
106 Option::<&Path>::None,
107 binary_checker,
108 )
109}
110
111#[cfg(feature = "regex")]
144pub fn which_re(regex: impl Borrow<Regex>) -> Result<impl Iterator<Item = path::PathBuf>> {
145 which_re_in(regex, env::var_os("PATH"))
146}
147
148pub fn which_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<path::PathBuf>
150where
151 T: AsRef<OsStr>,
152 U: AsRef<OsStr>,
153 V: AsRef<path::Path>,
154{
155 which_in_all(binary_name, paths, cwd)
156 .and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
157}
158
159#[cfg(feature = "regex")]
183pub fn which_re_in<T>(
184 regex: impl Borrow<Regex>,
185 paths: Option<T>,
186) -> Result<impl Iterator<Item = path::PathBuf>>
187where
188 T: AsRef<OsStr>,
189{
190 let binary_checker = build_binary_checker();
191
192 let finder = Finder::new();
193
194 finder.find_re(regex, paths, binary_checker)
195}
196
197pub fn which_in_all<T, U, V>(
199 binary_name: T,
200 paths: Option<U>,
201 cwd: V,
202) -> Result<impl Iterator<Item = path::PathBuf>>
203where
204 T: AsRef<OsStr>,
205 U: AsRef<OsStr>,
206 V: AsRef<path::Path>,
207{
208 let binary_checker = build_binary_checker();
209
210 let finder = Finder::new();
211
212 finder.find(binary_name, paths, Some(cwd), binary_checker)
213}
214
215pub fn which_in_global<T, U>(
217 binary_name: T,
218 paths: Option<U>,
219) -> Result<impl Iterator<Item = path::PathBuf>>
220where
221 T: AsRef<OsStr>,
222 U: AsRef<OsStr>,
223{
224 let binary_checker = build_binary_checker();
225
226 let finder = Finder::new();
227
228 finder.find(binary_name, paths, Option::<&Path>::None, binary_checker)
229}
230
231fn build_binary_checker() -> CompositeChecker {
232 CompositeChecker::new()
233 .add_checker(Box::new(ExistedChecker::new()))
234 .add_checker(Box::new(ExecutableChecker::new()))
235}
236
237pub struct WhichConfig {
239 cwd: Option<either::Either<bool, path::PathBuf>>,
240 custom_path_list: Option<OsString>,
241 binary_name: Option<OsString>,
242 #[cfg(feature = "regex")]
243 regex: Option<Regex>,
244}
245
246impl Default for WhichConfig {
247 fn default() -> Self {
248 Self {
249 cwd: Some(either::Either::Left(true)),
250 custom_path_list: None,
251 binary_name: None,
252 #[cfg(feature = "regex")]
253 regex: None,
254 }
255 }
256}
257
258#[cfg(feature = "regex")]
259type Regex = regex::Regex;
260
261#[cfg(not(feature = "regex"))]
262type Regex = ();
263
264impl WhichConfig {
265 pub fn new() -> Self {
266 Self::default()
267 }
268
269 pub fn system_cwd(mut self, use_cwd: bool) -> Self {
275 #[cfg(feature = "regex")]
276 if self.regex.is_some() && use_cwd {
277 panic!("which can't use regex and cwd at the same time!")
278 }
279 self.cwd = Some(either::Either::Left(use_cwd));
280 self
281 }
282
283 pub fn custom_cwd(mut self, cwd: path::PathBuf) -> Self {
289 #[cfg(feature = "regex")]
290 if self.regex.is_some() {
291 panic!("which can't use regex and cwd at the same time!")
292 }
293 self.cwd = Some(either::Either::Right(cwd));
294 self
295 }
296
297 #[allow(unused_variables)]
308 #[allow(unused_mut)]
309 pub fn regex(mut self, regex: Regex) -> Self {
310 #[cfg(not(feature = "regex"))]
311 {
312 panic!("which's regex feature was not enabled in your Cargo.toml!")
313 }
314 #[cfg(feature = "regex")]
315 {
316 if self.cwd != Some(either::Either::Left(false)) && self.cwd.is_some() {
317 panic!("which can't use regex and cwd at the same time!")
318 }
319 if self.binary_name.is_some() {
320 panic!("which can't use `binary_name` and `regex` at the same time!");
321 }
322 self.regex = Some(regex);
323 self
324 }
325 }
326
327 pub fn binary_name(mut self, name: OsString) -> Self {
333 #[cfg(feature = "regex")]
334 if self.regex.is_some() {
335 panic!("which can't use `binary_name` and `regex` at the same time!");
336 }
337 self.binary_name = Some(name);
338 self
339 }
340
341 pub fn custom_path_list(mut self, custom_path_list: OsString) -> Self {
343 self.custom_path_list = Some(custom_path_list);
344 self
345 }
346
347 pub fn system_path_list(mut self) -> Self {
349 self.custom_path_list = None;
350 self
351 }
352
353 pub fn first_result(self) -> Result<path::PathBuf> {
355 self.all_results()
356 .and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
357 }
358
359 pub fn all_results(self) -> Result<impl Iterator<Item = path::PathBuf>> {
361 let binary_checker = build_binary_checker();
362
363 let finder = Finder::new();
364
365 let paths = self.custom_path_list.or_else(|| env::var_os("PATH"));
366
367 #[cfg(feature = "regex")]
368 if let Some(regex) = self.regex {
369 return finder
370 .find_re(regex, paths, binary_checker)
371 .map(|i| Box::new(i) as Box<dyn Iterator<Item = path::PathBuf>>);
372 }
373
374 let cwd = match self.cwd {
375 Some(either::Either::Left(false)) => None,
376 Some(either::Either::Right(custom)) => Some(custom),
377 None | Some(either::Either::Left(true)) => env::current_dir().ok(),
378 };
379
380 finder
381 .find(
382 self.binary_name.expect(
383 "binary_name not set! You must set binary_name or regex before searching!",
384 ),
385 paths,
386 cwd,
387 binary_checker,
388 )
389 .map(|i| Box::new(i) as Box<dyn Iterator<Item = path::PathBuf>>)
390 }
391}
392
393#[derive(Clone, PartialEq, Eq)]
404pub struct Path {
405 inner: path::PathBuf,
406}
407
408impl Path {
409 pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<Path> {
413 which(binary_name).map(|inner| Path { inner })
414 }
415
416 pub fn all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = Path>> {
420 which_all(binary_name).map(|inner| inner.map(|inner| Path { inner }))
421 }
422
423 pub fn new_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<Path>
428 where
429 T: AsRef<OsStr>,
430 U: AsRef<OsStr>,
431 V: AsRef<path::Path>,
432 {
433 which_in(binary_name, paths, cwd).map(|inner| Path { inner })
434 }
435
436 pub fn all_in<T, U, V>(
441 binary_name: T,
442 paths: Option<U>,
443 cwd: V,
444 ) -> Result<impl Iterator<Item = Path>>
445 where
446 T: AsRef<OsStr>,
447 U: AsRef<OsStr>,
448 V: AsRef<path::Path>,
449 {
450 which_in_all(binary_name, paths, cwd).map(|inner| inner.map(|inner| Path { inner }))
451 }
452
453 pub fn as_path(&self) -> &path::Path {
455 self.inner.as_path()
456 }
457
458 pub fn into_path_buf(self) -> path::PathBuf {
460 self.inner
461 }
462}
463
464impl fmt::Debug for Path {
465 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466 fmt::Debug::fmt(&self.inner, f)
467 }
468}
469
470impl std::ops::Deref for Path {
471 type Target = path::Path;
472
473 fn deref(&self) -> &path::Path {
474 self.inner.deref()
475 }
476}
477
478impl AsRef<path::Path> for Path {
479 fn as_ref(&self) -> &path::Path {
480 self.as_path()
481 }
482}
483
484impl AsRef<OsStr> for Path {
485 fn as_ref(&self) -> &OsStr {
486 self.as_os_str()
487 }
488}
489
490impl PartialEq<path::PathBuf> for Path {
491 fn eq(&self, other: &path::PathBuf) -> bool {
492 self.inner == *other
493 }
494}
495
496impl PartialEq<Path> for path::PathBuf {
497 fn eq(&self, other: &Path) -> bool {
498 *self == other.inner
499 }
500}
501
502#[derive(Clone, PartialEq, Eq)]
516pub struct CanonicalPath {
517 inner: path::PathBuf,
518}
519
520impl CanonicalPath {
521 pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<CanonicalPath> {
525 which(binary_name)
526 .and_then(|p| p.canonicalize().map_err(|_| Error::CannotCanonicalize))
527 .map(|inner| CanonicalPath { inner })
528 }
529
530 pub fn all<T: AsRef<OsStr>>(
534 binary_name: T,
535 ) -> Result<impl Iterator<Item = Result<CanonicalPath>>> {
536 which_all(binary_name).map(|inner| {
537 inner.map(|inner| {
538 inner
539 .canonicalize()
540 .map_err(|_| Error::CannotCanonicalize)
541 .map(|inner| CanonicalPath { inner })
542 })
543 })
544 }
545
546 pub fn new_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<CanonicalPath>
551 where
552 T: AsRef<OsStr>,
553 U: AsRef<OsStr>,
554 V: AsRef<path::Path>,
555 {
556 which_in(binary_name, paths, cwd)
557 .and_then(|p| p.canonicalize().map_err(|_| Error::CannotCanonicalize))
558 .map(|inner| CanonicalPath { inner })
559 }
560
561 pub fn all_in<T, U, V>(
566 binary_name: T,
567 paths: Option<U>,
568 cwd: V,
569 ) -> Result<impl Iterator<Item = Result<CanonicalPath>>>
570 where
571 T: AsRef<OsStr>,
572 U: AsRef<OsStr>,
573 V: AsRef<path::Path>,
574 {
575 which_in_all(binary_name, paths, cwd).map(|inner| {
576 inner.map(|inner| {
577 inner
578 .canonicalize()
579 .map_err(|_| Error::CannotCanonicalize)
580 .map(|inner| CanonicalPath { inner })
581 })
582 })
583 }
584
585 pub fn as_path(&self) -> &path::Path {
587 self.inner.as_path()
588 }
589
590 pub fn into_path_buf(self) -> path::PathBuf {
592 self.inner
593 }
594}
595
596impl fmt::Debug for CanonicalPath {
597 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
598 fmt::Debug::fmt(&self.inner, f)
599 }
600}
601
602impl std::ops::Deref for CanonicalPath {
603 type Target = path::Path;
604
605 fn deref(&self) -> &path::Path {
606 self.inner.deref()
607 }
608}
609
610impl AsRef<path::Path> for CanonicalPath {
611 fn as_ref(&self) -> &path::Path {
612 self.as_path()
613 }
614}
615
616impl AsRef<OsStr> for CanonicalPath {
617 fn as_ref(&self) -> &OsStr {
618 self.as_os_str()
619 }
620}
621
622impl PartialEq<path::PathBuf> for CanonicalPath {
623 fn eq(&self, other: &path::PathBuf) -> bool {
624 self.inner == *other
625 }
626}
627
628impl PartialEq<CanonicalPath> for path::PathBuf {
629 fn eq(&self, other: &CanonicalPath) -> bool {
630 *self == other.inner
631 }
632}