Skip to main content

which/
lib.rs

1//! which
2//!
3//! A Rust equivalent of Unix command `which(1)`.
4//! # Example:
5//!
6//! To find which rustc executable binary is using:
7//!
8//! ```no_run
9//! # #[cfg(feature = "real-sys")]
10//! # {
11//! use which::which;
12//! use std::path::PathBuf;
13//!
14//! let result = which("rustc").unwrap();
15//! assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
16//! # }
17//! ```
18
19mod checker;
20mod error;
21mod finder;
22mod helper;
23pub mod sys;
24#[cfg(all(windows, feature = "real-sys"))]
25mod win_ffi;
26
27use std::fmt;
28use std::path;
29
30use std::ffi::{OsStr, OsString};
31
32pub use crate::error::*;
33use crate::finder::Finder;
34use crate::sys::Sys;
35
36/// Find an executable binary's path by name.
37///
38/// If given an absolute path, returns it if the file exists and is executable.
39///
40/// If given a relative path, returns an absolute path to the file if
41/// it exists and is executable.
42///
43/// If given a string without path separators, looks for a file named
44/// `binary_name` at each directory in `$PATH` and if it finds an executable
45/// file there, returns it.
46///
47/// # Example
48///
49/// ```no_run
50/// use which::which;
51/// use std::path::PathBuf;
52///
53/// let result = which::which("rustc").unwrap();
54/// assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
55///
56/// ```
57#[cfg(feature = "real-sys")]
58pub 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
62/// Find an executable binary's path by name, ignoring `cwd`.
63///
64/// If given an absolute path, returns it if the file exists and is executable.
65///
66/// Does not resolve relative paths.
67///
68/// If given a string without path separators, looks for a file named
69/// `binary_name` at each directory in `$PATH` and if it finds an executable
70/// file there, returns it.
71///
72/// # Example
73///
74/// ```no_run
75/// use which::which;
76/// use std::path::PathBuf;
77///
78/// let result = which::which_global("rustc").unwrap();
79/// assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
80///
81/// ```
82#[cfg(feature = "real-sys")]
83pub fn which_global<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
84    which_all_global(binary_name).and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
85}
86
87/// Find all binaries with `binary_name` using `cwd` to resolve relative paths.
88#[cfg(feature = "real-sys")]
89pub fn which_all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = path::PathBuf>> {
90    let cwd = sys::RealSys.current_dir().ok();
91
92    Finder::new(&sys::RealSys).find(binary_name, sys::RealSys.env_path(), cwd, Noop)
93}
94
95/// Find all binaries with `binary_name` ignoring `cwd`.
96#[cfg(feature = "real-sys")]
97pub fn which_all_global<T: AsRef<OsStr>>(
98    binary_name: T,
99) -> Result<impl Iterator<Item = path::PathBuf>> {
100    Finder::new(&sys::RealSys).find(
101        binary_name,
102        sys::RealSys.env_path(),
103        Option::<&Path>::None,
104        Noop,
105    )
106}
107
108/// Find all binaries matching a regular expression in a the system PATH.
109///
110/// Only available when feature `regex` is enabled.
111///
112/// # Arguments
113///
114/// * `regex` - A regular expression to match binaries with
115///
116/// # Examples
117///
118/// Find Python executables:
119///
120/// ```no_run
121/// use regex::Regex;
122/// use which::which;
123/// use std::path::PathBuf;
124///
125/// let re = Regex::new(r"python\d$").unwrap();
126/// let binaries: Vec<PathBuf> = which::which_re(re).unwrap().collect();
127/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
128/// assert_eq!(binaries, python_paths);
129/// ```
130///
131/// Find all cargo subcommand executables on the path:
132///
133/// ```
134/// use which::which_re;
135/// use regex::Regex;
136///
137/// which_re(Regex::new("^cargo-.*").unwrap()).unwrap()
138///     .for_each(|pth| println!("{}", pth.to_string_lossy()));
139/// ```
140#[cfg(all(feature = "regex", feature = "real-sys"))]
141pub fn which_re(
142    regex: impl std::borrow::Borrow<Regex>,
143) -> Result<impl Iterator<Item = path::PathBuf>> {
144    which_re_in(regex, sys::RealSys.env_path())
145}
146
147/// Find `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
148#[cfg(feature = "real-sys")]
149pub 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/// Find all binaries matching a regular expression in a list of paths.
160///
161/// Only available when feature `regex` is enabled.
162///
163/// # Arguments
164///
165/// * `regex` - A regular expression to match binaries with
166/// * `paths` - A string containing the paths to search
167///   (separated in the same way as the PATH environment variable)
168///
169/// # Examples
170///
171/// ```no_run
172/// use regex::Regex;
173/// use which::which;
174/// use std::path::PathBuf;
175///
176/// let re = Regex::new(r"python\d$").unwrap();
177/// let paths = Some("/usr/bin:/usr/local/bin");
178/// let binaries: Vec<PathBuf> = which::which_re_in(re, paths).unwrap().collect();
179/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
180/// assert_eq!(binaries, python_paths);
181/// ```
182#[cfg(all(feature = "regex", feature = "real-sys"))]
183pub fn which_re_in<T>(
184    regex: impl std::borrow::Borrow<Regex>,
185    paths: Option<T>,
186) -> Result<impl Iterator<Item = path::PathBuf>>
187where
188    T: AsRef<OsStr>,
189{
190    Finder::new(&sys::RealSys).find_re(regex, paths, Noop)
191}
192
193/// Find all binaries with `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
194#[cfg(feature = "real-sys")]
195pub fn which_in_all<'a, T, U, V>(
196    binary_name: T,
197    paths: Option<U>,
198    cwd: V,
199) -> Result<impl Iterator<Item = path::PathBuf> + 'a>
200where
201    T: AsRef<OsStr>,
202    U: AsRef<OsStr>,
203    V: AsRef<path::Path> + 'a,
204{
205    Finder::new(&sys::RealSys).find(binary_name, paths, Some(cwd), Noop)
206}
207
208/// Find all binaries with `binary_name` in the path list `paths`, ignoring `cwd`.
209#[cfg(feature = "real-sys")]
210pub fn which_in_global<T, U>(
211    binary_name: T,
212    paths: Option<U>,
213) -> Result<impl Iterator<Item = path::PathBuf>>
214where
215    T: AsRef<OsStr>,
216    U: AsRef<OsStr>,
217{
218    Finder::new(&sys::RealSys).find(binary_name, paths, Option::<&Path>::None, Noop)
219}
220
221/// A wrapper containing all functionality in this crate.
222pub struct WhichConfig<TSys: sys::Sys, F = Noop> {
223    cwd: CwdOption,
224    custom_path_list: Option<OsString>,
225    binary_name: Option<OsString>,
226    nonfatal_error_handler: F,
227    #[cfg(feature = "regex")]
228    regex: Option<Regex>,
229    sys: TSys,
230}
231
232enum CwdOption {
233    Unspecified,
234    UseSysCwd,
235    RefuseCwd,
236    UseCustomCwd(path::PathBuf),
237}
238
239/// A handler for non-fatal errors which does nothing with them.
240#[derive(Default, Debug, Clone)]
241pub struct Noop;
242
243/// Defines what should happen when a nonfatal error is encountered. A nonfatal error may represent a problem,
244/// but it doesn't necessarily require `which` to stop its search.
245///
246/// This trait is implemented for any closure or function that takes a single argument which is a [`NonFatalError`].
247/// You may also implement it for your own types.
248pub trait NonFatalErrorHandler {
249    fn handle(&mut self, e: NonFatalError);
250}
251
252impl NonFatalErrorHandler for Noop {
253    fn handle(&mut self, _: NonFatalError) {
254        // Do nothing
255    }
256}
257
258impl<T> NonFatalErrorHandler for T
259where
260    T: FnMut(NonFatalError),
261{
262    fn handle(&mut self, e: NonFatalError) {
263        (self)(e);
264    }
265}
266
267#[cfg(feature = "real-sys")]
268impl<F: Default> Default for WhichConfig<&sys::RealSys, F> {
269    fn default() -> Self {
270        Self {
271            cwd: CwdOption::Unspecified,
272            custom_path_list: None,
273            binary_name: None,
274            nonfatal_error_handler: F::default(),
275            #[cfg(feature = "regex")]
276            regex: None,
277            sys: &sys::RealSys,
278        }
279    }
280}
281
282#[cfg(feature = "regex")]
283type Regex = regex::Regex;
284
285#[cfg(not(feature = "regex"))]
286type Regex = ();
287
288#[cfg(feature = "real-sys")]
289impl WhichConfig<&sys::RealSys, Noop> {
290    pub fn new() -> Self {
291        Self::new_with_sys(&sys::RealSys)
292    }
293}
294
295impl<TSys: Sys> WhichConfig<TSys, Noop> {
296    /// Creates a new `WhichConfig` with the given `sys::Sys`.
297    ///
298    /// This is useful for providing all the system related
299    /// functionality to this crate.
300    pub fn new_with_sys(sys: TSys) -> Self {
301        Self {
302            cwd: CwdOption::Unspecified,
303            custom_path_list: None,
304            binary_name: None,
305            nonfatal_error_handler: Noop,
306            #[cfg(feature = "regex")]
307            regex: None,
308            sys,
309        }
310    }
311}
312
313impl<'a, TSys: Sys + 'a, F: NonFatalErrorHandler + 'a> WhichConfig<TSys, F> {
314    /// Whether or not to use the current working directory. `true` by default.
315    ///
316    /// # Panics
317    ///
318    /// If regex was set previously, and you've just passed in `use_cwd: true`, this will panic.
319    pub fn system_cwd(mut self, use_cwd: bool) -> Self {
320        #[cfg(feature = "regex")]
321        if self.regex.is_some() && use_cwd {
322            panic!("which can't use regex and cwd at the same time!")
323        }
324        // Otherwise, keep custom cwd if specified.
325        self.cwd = if use_cwd {
326            CwdOption::UseSysCwd
327        } else {
328            CwdOption::RefuseCwd
329        };
330        self
331    }
332
333    /// Sets a custom path for resolving relative paths.
334    ///
335    /// # Panics
336    ///
337    /// If regex was set previously, this will panic.
338    pub fn custom_cwd(mut self, cwd: path::PathBuf) -> Self {
339        #[cfg(feature = "regex")]
340        if self.regex.is_some() {
341            panic!("which can't use regex and cwd at the same time!")
342        }
343        self.cwd = CwdOption::UseCustomCwd(cwd);
344        self
345    }
346
347    /// Sets the path name regex to search for. You ***MUST*** call this, or [`Self::binary_name`] prior to searching.
348    ///
349    /// When `Regex` is disabled this function takes the unit type as a stand in. The parameter will change when
350    /// `Regex` is enabled.
351    ///
352    /// # Panics
353    ///
354    /// If the `regex` feature wasn't turned on for this crate this will always panic. Additionally if a
355    /// `cwd` (aka current working directory) or `binary_name` was set previously, this will panic, as those options
356    /// are incompatible with `regex`.
357    #[allow(unused_variables)]
358    #[allow(unused_mut)]
359    pub fn regex(mut self, regex: Regex) -> Self {
360        #[cfg(not(feature = "regex"))]
361        {
362            panic!("which's regex feature was not enabled in your Cargo.toml!")
363        }
364        #[cfg(feature = "regex")]
365        {
366            if matches!(self.cwd, CwdOption::UseSysCwd)
367                || matches!(self.cwd, CwdOption::UseCustomCwd(_))
368            {
369                panic!("which can't use regex and cwd at the same time!")
370            }
371            if self.binary_name.is_some() {
372                panic!("which can't use `binary_name` and `regex` at the same time!");
373            }
374            self.regex = Some(regex);
375            self
376        }
377    }
378
379    /// Sets the path name to search for. You ***MUST*** call this, or [`Self::regex`] prior to searching.
380    ///
381    /// # Panics
382    ///
383    /// If a `regex` was set previously this will panic as this is not compatible with `regex`.
384    pub fn binary_name(mut self, name: OsString) -> Self {
385        #[cfg(feature = "regex")]
386        if self.regex.is_some() {
387            panic!("which can't use `binary_name` and `regex` at the same time!");
388        }
389        self.binary_name = Some(name);
390        self
391    }
392
393    /// Uses the given string instead of the `PATH` env variable.
394    pub fn custom_path_list(mut self, custom_path_list: OsString) -> Self {
395        self.custom_path_list = Some(custom_path_list);
396        self
397    }
398
399    /// Uses the `PATH` env variable. Enabled by default.
400    pub fn system_path_list(mut self) -> Self {
401        self.custom_path_list = None;
402        self
403    }
404
405    /// Sets a closure that will receive non-fatal errors. You can also pass in other types
406    /// that implement [`NonFatalErrorHandler`].
407    ///
408    /// # Example
409    /// ```
410    /// # #[cfg(feature = "real-sys")]
411    /// # {
412    /// # use which::WhichConfig;
413    /// let mut nonfatal_errors = Vec::new();
414    ///
415    /// WhichConfig::new()
416    ///     .binary_name("tar".into())
417    ///     .nonfatal_error_handler(|e| nonfatal_errors.push(e))
418    ///     .all_results()
419    ///     .unwrap()
420    ///     .collect::<Vec<_>>();
421    ///
422    /// if !nonfatal_errors.is_empty() {
423    ///     println!("nonfatal errors encountered: {nonfatal_errors:?}");
424    /// }
425    /// # }
426    /// ```
427    ///
428    /// You could also log it if you choose
429    ///
430    /// ```
431    /// # #[cfg(feature = "real-sys")]
432    /// # {
433    /// # use which::WhichConfig;
434    /// WhichConfig::new()
435    ///     .binary_name("tar".into())
436    ///     .nonfatal_error_handler(|e| eprintln!("{e}"))
437    ///     .all_results()
438    ///     .unwrap()
439    ///     .collect::<Vec<_>>();
440    /// # }
441    /// ```
442    pub fn nonfatal_error_handler<NewF>(self, handler: NewF) -> WhichConfig<TSys, NewF> {
443        WhichConfig {
444            custom_path_list: self.custom_path_list,
445            cwd: self.cwd,
446            binary_name: self.binary_name,
447            nonfatal_error_handler: handler,
448            #[cfg(feature = "regex")]
449            regex: self.regex,
450            sys: self.sys,
451        }
452    }
453
454    /// Finishes configuring, runs the query and returns the first result.
455    pub fn first_result(self) -> Result<path::PathBuf> {
456        self.all_results()
457            .and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
458    }
459
460    /// Finishes configuring, runs the query and returns all results.
461    pub fn all_results(self) -> Result<impl Iterator<Item = path::PathBuf> + 'a> {
462        let paths = self.custom_path_list.or_else(|| self.sys.env_path());
463
464        #[cfg(feature = "regex")]
465        if let Some(regex) = self.regex {
466            return Finder::new(self.sys)
467                .find_re(regex, paths, self.nonfatal_error_handler)
468                .map(|i| Box::new(i) as Box<dyn Iterator<Item = path::PathBuf> + 'a>);
469        }
470
471        let cwd = match self.cwd {
472            CwdOption::RefuseCwd => None,
473            CwdOption::UseCustomCwd(custom) => Some(custom),
474            CwdOption::UseSysCwd | CwdOption::Unspecified => self.sys.current_dir().ok(),
475        };
476
477        Finder::new(self.sys)
478            .find(
479                self.binary_name.expect(
480                    "binary_name not set! You must set binary_name or regex before searching!",
481                ),
482                paths,
483                cwd,
484                self.nonfatal_error_handler,
485            )
486            .map(|i| Box::new(i) as Box<dyn Iterator<Item = path::PathBuf> + 'a>)
487    }
488}
489
490/// An owned, immutable wrapper around a `PathBuf` containing the path of an executable.
491///
492/// The constructed `PathBuf` is the output of `which` or `which_in`, but `which::Path` has the
493/// advantage of being a type distinct from `std::path::Path` and `std::path::PathBuf`.
494///
495/// It can be beneficial to use `which::Path` instead of `std::path::Path` when you want the type
496/// system to enforce the need for a path that exists and points to a binary that is executable.
497///
498/// Since `which::Path` implements `Deref` for `std::path::Path`, all methods on `&std::path::Path`
499/// are also available to `&which::Path` values.
500#[derive(Clone, PartialEq, Eq)]
501pub struct Path {
502    inner: path::PathBuf,
503}
504
505impl Path {
506    /// Returns the path of an executable binary by name.
507    ///
508    /// This calls `which` and maps the result into a `Path`.
509    #[cfg(feature = "real-sys")]
510    pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<Path> {
511        which(binary_name).map(|inner| Path { inner })
512    }
513
514    /// Returns the paths of all executable binaries by a name.
515    ///
516    /// this calls `which_all` and maps the results into `Path`s.
517    #[cfg(feature = "real-sys")]
518    pub fn all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = Path>> {
519        which_all(binary_name).map(|inner| inner.map(|inner| Path { inner }))
520    }
521
522    /// Returns the path of an executable binary by name in the path list `paths` and using the
523    /// current working directory `cwd` to resolve relative paths.
524    ///
525    /// This calls `which_in` and maps the result into a `Path`.
526    #[cfg(feature = "real-sys")]
527    pub fn new_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<Path>
528    where
529        T: AsRef<OsStr>,
530        U: AsRef<OsStr>,
531        V: AsRef<path::Path>,
532    {
533        which_in(binary_name, paths, cwd).map(|inner| Path { inner })
534    }
535
536    /// Returns all paths of an executable binary by name in the path list `paths` and using the
537    /// current working directory `cwd` to resolve relative paths.
538    ///
539    /// This calls `which_in_all` and maps the results into a `Path`.
540    #[cfg(feature = "real-sys")]
541    pub fn all_in<'a, T, U, V>(
542        binary_name: T,
543        paths: Option<U>,
544        cwd: V,
545    ) -> Result<impl Iterator<Item = Path> + 'a>
546    where
547        T: AsRef<OsStr>,
548        U: AsRef<OsStr>,
549        V: AsRef<path::Path> + 'a,
550    {
551        which_in_all(binary_name, paths, cwd).map(|inner| inner.map(|inner| Path { inner }))
552    }
553
554    /// Returns a reference to a `std::path::Path`.
555    pub fn as_path(&self) -> &path::Path {
556        self.inner.as_path()
557    }
558
559    /// Consumes the `which::Path`, yielding its underlying `std::path::PathBuf`.
560    pub fn into_path_buf(self) -> path::PathBuf {
561        self.inner
562    }
563}
564
565impl fmt::Debug for Path {
566    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
567        fmt::Debug::fmt(&self.inner, f)
568    }
569}
570
571impl std::ops::Deref for Path {
572    type Target = path::Path;
573
574    fn deref(&self) -> &path::Path {
575        self.inner.deref()
576    }
577}
578
579impl AsRef<path::Path> for Path {
580    fn as_ref(&self) -> &path::Path {
581        self.as_path()
582    }
583}
584
585impl AsRef<OsStr> for Path {
586    fn as_ref(&self) -> &OsStr {
587        self.as_os_str()
588    }
589}
590
591impl PartialEq<path::PathBuf> for Path {
592    fn eq(&self, other: &path::PathBuf) -> bool {
593        self.inner == *other
594    }
595}
596
597impl PartialEq<Path> for path::PathBuf {
598    fn eq(&self, other: &Path) -> bool {
599        *self == other.inner
600    }
601}
602
603/// An owned, immutable wrapper around a `PathBuf` containing the _canonical_ path of an
604/// executable.
605///
606/// The constructed `PathBuf` is the result of `which` or `which_in` followed by
607/// `Path::canonicalize`, but `CanonicalPath` has the advantage of being a type distinct from
608/// `std::path::Path` and `std::path::PathBuf`.
609///
610/// It can be beneficial to use `CanonicalPath` instead of `std::path::Path` when you want the type
611/// system to enforce the need for a path that exists, points to a binary that is executable, is
612/// absolute, has all components normalized, and has all symbolic links resolved
613///
614/// Since `CanonicalPath` implements `Deref` for `std::path::Path`, all methods on
615/// `&std::path::Path` are also available to `&CanonicalPath` values.
616#[derive(Clone, PartialEq, Eq)]
617pub struct CanonicalPath {
618    inner: path::PathBuf,
619}
620
621impl CanonicalPath {
622    /// Returns the canonical path of an executable binary by name.
623    ///
624    /// This calls `which` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
625    #[cfg(feature = "real-sys")]
626    pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<CanonicalPath> {
627        which(binary_name)
628            .and_then(|p| {
629                sys::RealSys
630                    .canonicalize(&p)
631                    .map_err(|_| Error::CannotCanonicalize)
632            })
633            .map(|inner| CanonicalPath { inner })
634    }
635
636    /// Returns the canonical paths of an executable binary by name.
637    ///
638    /// This calls `which_all` and `Path::canonicalize` and maps the results into `CanonicalPath`s.
639    #[cfg(feature = "real-sys")]
640    pub fn all<T: AsRef<OsStr>>(
641        binary_name: T,
642    ) -> Result<impl Iterator<Item = Result<CanonicalPath>>> {
643        which_all(binary_name).map(|inner| {
644            inner.map(|inner| {
645                sys::RealSys
646                    .canonicalize(&inner)
647                    .map_err(|_| Error::CannotCanonicalize)
648                    .map(|inner| CanonicalPath { inner })
649            })
650        })
651    }
652
653    /// Returns the canonical path of an executable binary by name in the path list `paths` and
654    /// using the current working directory `cwd` to resolve relative paths.
655    ///
656    /// This calls `which_in` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
657    #[cfg(feature = "real-sys")]
658    pub fn new_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<CanonicalPath>
659    where
660        T: AsRef<OsStr>,
661        U: AsRef<OsStr>,
662        V: AsRef<path::Path>,
663    {
664        which_in(binary_name, paths, cwd)
665            .and_then(|p| {
666                sys::RealSys
667                    .canonicalize(&p)
668                    .map_err(|_| Error::CannotCanonicalize)
669            })
670            .map(|inner| CanonicalPath { inner })
671    }
672
673    /// Returns all of the canonical paths of an executable binary by name in the path list `paths` and
674    /// using the current working directory `cwd` to resolve relative paths.
675    ///
676    /// This calls `which_in_all` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
677    #[cfg(feature = "real-sys")]
678    pub fn all_in<'a, T, U, V>(
679        binary_name: T,
680        paths: Option<U>,
681        cwd: V,
682    ) -> Result<impl Iterator<Item = Result<CanonicalPath>> + 'a>
683    where
684        T: AsRef<OsStr>,
685        U: AsRef<OsStr>,
686        V: AsRef<path::Path> + 'a,
687    {
688        which_in_all(binary_name, paths, cwd).map(|inner| {
689            inner.map(|inner| {
690                sys::RealSys
691                    .canonicalize(&inner)
692                    .map_err(|_| Error::CannotCanonicalize)
693                    .map(|inner| CanonicalPath { inner })
694            })
695        })
696    }
697
698    /// Returns a reference to a `std::path::Path`.
699    pub fn as_path(&self) -> &path::Path {
700        self.inner.as_path()
701    }
702
703    /// Consumes the `which::CanonicalPath`, yielding its underlying `std::path::PathBuf`.
704    pub fn into_path_buf(self) -> path::PathBuf {
705        self.inner
706    }
707}
708
709impl fmt::Debug for CanonicalPath {
710    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
711        fmt::Debug::fmt(&self.inner, f)
712    }
713}
714
715impl std::ops::Deref for CanonicalPath {
716    type Target = path::Path;
717
718    fn deref(&self) -> &path::Path {
719        self.inner.deref()
720    }
721}
722
723impl AsRef<path::Path> for CanonicalPath {
724    fn as_ref(&self) -> &path::Path {
725        self.as_path()
726    }
727}
728
729impl AsRef<OsStr> for CanonicalPath {
730    fn as_ref(&self) -> &OsStr {
731        self.as_os_str()
732    }
733}
734
735impl PartialEq<path::PathBuf> for CanonicalPath {
736    fn eq(&self, other: &path::PathBuf) -> bool {
737        self.inner == *other
738    }
739}
740
741impl PartialEq<CanonicalPath> for path::PathBuf {
742    fn eq(&self, other: &CanonicalPath) -> bool {
743        *self == other.inner
744    }
745}