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