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
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
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/// ```
58pub 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/// ```
82pub 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.
87pub 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 let binary_checker: CompositeChecker = build_binary_checker();
91
92 let finder: Finder = Finder::new();
93
94 finder.find(binary_name, paths:env::var_os(key:"PATH"), cwd, binary_checker)
95}
96
97/// Find all binaries with `binary_name` ignoring `cwd`.
98pub fn which_all_global<T: AsRef<OsStr>>(
99 binary_name: T,
100) -> Result<impl Iterator<Item = path::PathBuf>> {
101 let binary_checker: CompositeChecker = build_binary_checker();
102
103 let finder: Finder = Finder::new();
104
105 finder.find(
106 binary_name,
107 paths:env::var_os("PATH"),
108 cwd:Option::<&Path>::None,
109 binary_checker,
110 )
111}
112
113/// Find all binaries matching a regular expression in a the system PATH.
114///
115/// Only available when feature `regex` is enabled.
116///
117/// # Arguments
118///
119/// * `regex` - A regular expression to match binaries with
120///
121/// # Examples
122///
123/// Find Python executables:
124///
125/// ```no_run
126/// use regex::Regex;
127/// use which::which;
128/// use std::path::PathBuf;
129///
130/// let re = Regex::new(r"python\d$").unwrap();
131/// let binaries: Vec<PathBuf> = which::which_re(re).unwrap().collect();
132/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
133/// assert_eq!(binaries, python_paths);
134/// ```
135///
136/// Find all cargo subcommand executables on the path:
137///
138/// ```
139/// use which::which_re;
140/// use regex::Regex;
141///
142/// which_re(Regex::new("^cargo-.*").unwrap()).unwrap()
143/// .for_each(|pth| println!("{}", pth.to_string_lossy()));
144/// ```
145#[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
150/// Find `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
151pub 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: impl Iterator| i.next().ok_or(err:Error::CannotFindBinaryPath))
159}
160
161/// Find all binaries matching a regular expression in a list of paths.
162///
163/// Only available when feature `regex` is enabled.
164///
165/// # Arguments
166///
167/// * `regex` - A regular expression to match binaries with
168/// * `paths` - A string containing the paths to search
169/// (separated in the same way as the PATH environment variable)
170///
171/// # Examples
172///
173/// ```no_run
174/// use regex::Regex;
175/// use which::which;
176/// use std::path::PathBuf;
177///
178/// let re = Regex::new(r"python\d$").unwrap();
179/// let paths = Some("/usr/bin:/usr/local/bin");
180/// let binaries: Vec<PathBuf> = which::which_re_in(re, paths).unwrap().collect();
181/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
182/// assert_eq!(binaries, python_paths);
183/// ```
184#[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
199/// Find all binaries with `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
200pub 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: CompositeChecker = build_binary_checker();
211
212 let finder: Finder = Finder::new();
213
214 finder.find(binary_name, paths, cwd:Some(cwd), binary_checker)
215}
216
217/// Find all binaries with `binary_name` in the path list `paths`, ignoring `cwd`.
218pub 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: CompositeChecker = build_binary_checker();
227
228 let finder: Finder = Finder::new();
229
230 finder.find(binary_name, paths, cwd:Option::<&Path>::None, binary_checker)
231}
232
233fn build_binary_checker() -> CompositeChecker {
234 CompositeCheckerCompositeChecker::new()
235 .add_checker(Box::new(ExistedChecker::new()))
236 .add_checker(Box::new(ExecutableChecker::new()))
237}
238
239/// A wrapper containing all functionality in this crate.
240pub 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 /// Whether or not to use the current working directory. `true` by default.
272 ///
273 /// # Panics
274 ///
275 /// If regex was set previously, and you've just passed in `use_cwd: true`, this will panic.
276 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 /// Sets a custom path for resolving relative paths.
286 ///
287 /// # Panics
288 ///
289 /// If regex was set previously, this will panic.
290 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 /// Sets the path name regex to search for. You ***MUST*** call this, or [`Self::binary_name`] prior to searching.
300 ///
301 /// When `Regex` is disabled this function takes the unit type as a stand in. The parameter will change when
302 /// `Regex` is enabled.
303 ///
304 /// # Panics
305 ///
306 /// If the `regex` feature wasn't turned on for this crate this will always panic. Additionally if a
307 /// `cwd` (aka current working directory) or `binary_name` was set previously, this will panic, as those options
308 /// are incompatible with `regex`.
309 #[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 /// Sets the path name to search for. You ***MUST*** call this, or [`Self::regex`] prior to searching.
330 ///
331 /// # Panics
332 ///
333 /// If a `regex` was set previously this will panic as this is not compatible with `regex`.
334 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 /// Uses the given string instead of the `PATH` env variable.
344 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 /// Uses the `PATH` env variable. Enabled by default.
350 pub fn system_path_list(mut self) -> Self {
351 self.custom_path_list = None;
352 self
353 }
354
355 /// Finishes configuring, runs the query and returns the first result.
356 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 /// Finishes configuring, runs the query and returns all results.
362 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/// An owned, immutable wrapper around a `PathBuf` containing the path of an executable.
396///
397/// The constructed `PathBuf` is the output of `which` or `which_in`, but `which::Path` has the
398/// advantage of being a type distinct from `std::path::Path` and `std::path::PathBuf`.
399///
400/// It can be beneficial to use `which::Path` instead of `std::path::Path` when you want the type
401/// system to enforce the need for a path that exists and points to a binary that is executable.
402///
403/// Since `which::Path` implements `Deref` for `std::path::Path`, all methods on `&std::path::Path`
404/// are also available to `&which::Path` values.
405#[derive(Clone, PartialEq, Eq)]
406pub struct Path {
407 inner: path::PathBuf,
408}
409
410impl Path {
411 /// Returns the path of an executable binary by name.
412 ///
413 /// This calls `which` and maps the result into a `Path`.
414 pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<Path> {
415 which(binary_name).map(|inner| Path { inner })
416 }
417
418 /// Returns the paths of all executable binaries by a name.
419 ///
420 /// this calls `which_all` and maps the results into `Path`s.
421 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 /// Returns the path of an executable binary by name in the path list `paths` and using the
426 /// current working directory `cwd` to resolve relative paths.
427 ///
428 /// This calls `which_in` and maps the result into a `Path`.
429 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 /// Returns all paths of an executable binary by name in the path list `paths` and using the
439 /// current working directory `cwd` to resolve relative paths.
440 ///
441 /// This calls `which_in_all` and maps the results into a `Path`.
442 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 /// Returns a reference to a `std::path::Path`.
456 pub fn as_path(&self) -> &path::Path {
457 self.inner.as_path()
458 }
459
460 /// Consumes the `which::Path`, yielding its underlying `std::path::PathBuf`.
461 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/// An owned, immutable wrapper around a `PathBuf` containing the _canonical_ path of an
505/// executable.
506///
507/// The constructed `PathBuf` is the result of `which` or `which_in` followed by
508/// `Path::canonicalize`, but `CanonicalPath` has the advantage of being a type distinct from
509/// `std::path::Path` and `std::path::PathBuf`.
510///
511/// It can be beneficial to use `CanonicalPath` instead of `std::path::Path` when you want the type
512/// system to enforce the need for a path that exists, points to a binary that is executable, is
513/// absolute, has all components normalized, and has all symbolic links resolved
514///
515/// Since `CanonicalPath` implements `Deref` for `std::path::Path`, all methods on
516/// `&std::path::Path` are also available to `&CanonicalPath` values.
517#[derive(Clone, PartialEq, Eq)]
518pub struct CanonicalPath {
519 inner: path::PathBuf,
520}
521
522impl CanonicalPath {
523 /// Returns the canonical path of an executable binary by name.
524 ///
525 /// This calls `which` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
526 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 /// Returns the canonical paths of an executable binary by name.
533 ///
534 /// This calls `which_all` and `Path::canonicalize` and maps the results into `CanonicalPath`s.
535 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 /// Returns the canonical path of an executable binary by name in the path list `paths` and
549 /// using the current working directory `cwd` to resolve relative paths.
550 ///
551 /// This calls `which_in` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
552 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 /// Returns all of the canonical paths of an executable binary by name in the path list `paths` and
564 /// using the current working directory `cwd` to resolve relative paths.
565 ///
566 /// This calls `which_in_all` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
567 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 /// Returns a reference to a `std::path::Path`.
588 pub fn as_path(&self) -> &path::Path {
589 self.inner.as_path()
590 }
591
592 /// Consumes the `which::CanonicalPath`, yielding its underlying `std::path::PathBuf`.
593 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}
635