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