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
17mod 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
35/// Find an executable binary's path by name.
36///
37/// If given an absolute path, returns it if the file exists and is executable.
38///
39/// If given a relative path, returns an absolute path to the file if
40/// it exists and is executable.
41///
42/// If given a string without path separators, looks for a file named
43/// `binary_name` at each directory in `$PATH` and if it finds an executable
44/// file there, returns it.
45///
46/// # Example
47///
48/// ```no_run
49/// use which::which;
50/// use std::path::PathBuf;
51///
52/// let result = which::which("rustc").unwrap();
53/// assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
54///
55/// ```
56pub fn which<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
57 which_all(binary_name).and_then(|mut i: impl Iterator| i.next().ok_or(err:Error::CannotFindBinaryPath))
58}
59
60/// Find an executable binary's path by name, ignoring `cwd`.
61///
62/// If given an absolute path, returns it if the file exists and is executable.
63///
64/// Does not resolve relative paths.
65///
66/// If given a string without path separators, looks for a file named
67/// `binary_name` at each directory in `$PATH` and if it finds an executable
68/// file there, returns it.
69///
70/// # Example
71///
72/// ```no_run
73/// use which::which;
74/// use std::path::PathBuf;
75///
76/// let result = which::which_global("rustc").unwrap();
77/// assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
78///
79/// ```
80pub fn which_global<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
81 which_all_global(binary_name).and_then(|mut i: impl Iterator| i.next().ok_or(err:Error::CannotFindBinaryPath))
82}
83
84/// Find all binaries with `binary_name` using `cwd` to resolve relative paths.
85pub fn which_all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = path::PathBuf>> {
86 let cwd: Option = env::current_dir().ok();
87
88 let binary_checker: CompositeChecker = build_binary_checker();
89
90 let finder: Finder = Finder::new();
91
92 finder.find(binary_name, paths:env::var_os(key:"PATH"), cwd, binary_checker)
93}
94
95/// Find all binaries with `binary_name` ignoring `cwd`.
96pub fn which_all_global<T: AsRef<OsStr>>(
97 binary_name: T,
98) -> Result<impl Iterator<Item = path::PathBuf>> {
99 let binary_checker: CompositeChecker = build_binary_checker();
100
101 let finder: Finder = Finder::new();
102
103 finder.find(
104 binary_name,
105 paths:env::var_os("PATH"),
106 cwd:Option::<&Path>::None,
107 binary_checker,
108 )
109}
110
111/// Find all binaries matching a regular expression in a the system PATH.
112///
113/// Only available when feature `regex` is enabled.
114///
115/// # Arguments
116///
117/// * `regex` - A regular expression to match binaries with
118///
119/// # Examples
120///
121/// Find Python executables:
122///
123/// ```no_run
124/// use regex::Regex;
125/// use which::which;
126/// use std::path::PathBuf;
127///
128/// let re = Regex::new(r"python\d$").unwrap();
129/// let binaries: Vec<PathBuf> = which::which_re(re).unwrap().collect();
130/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
131/// assert_eq!(binaries, python_paths);
132/// ```
133///
134/// Find all cargo subcommand executables on the path:
135///
136/// ```
137/// use which::which_re;
138/// use regex::Regex;
139///
140/// which_re(Regex::new("^cargo-.*").unwrap()).unwrap()
141/// .for_each(|pth| println!("{}", pth.to_string_lossy()));
142/// ```
143#[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
148/// Find `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
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: impl Iterator| i.next().ok_or(err: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(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
197/// Find all binaries with `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
198pub 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: CompositeChecker = build_binary_checker();
209
210 let finder: Finder = Finder::new();
211
212 finder.find(binary_name, paths, cwd:Some(cwd), binary_checker)
213}
214
215/// Find all binaries with `binary_name` in the path list `paths`, ignoring `cwd`.
216pub 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: CompositeChecker = build_binary_checker();
225
226 let finder: Finder = Finder::new();
227
228 finder.find(binary_name, paths, cwd:Option::<&Path>::None, binary_checker)
229}
230
231fn build_binary_checker() -> CompositeChecker {
232 CompositeCheckerCompositeChecker::new()
233 .add_checker(Box::new(ExistedChecker::new()))
234 .add_checker(Box::new(ExecutableChecker::new()))
235}
236
237/// A wrapper containing all functionality in this crate.
238pub 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 /// Whether or not to use the current working directory. `true` by default.
270 ///
271 /// # Panics
272 ///
273 /// If regex was set previously, and you've just passed in `use_cwd: true`, this will panic.
274 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 /// Sets a custom path for resolving relative paths.
284 ///
285 /// # Panics
286 ///
287 /// If regex was set previously, this will panic.
288 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 /// Sets the path name regex to search for. You ***MUST*** call this, or [`Self::binary_name`] prior to searching.
298 ///
299 /// When `Regex` is disabled this function takes the unit type as a stand in. The parameter will change when
300 /// `Regex` is enabled.
301 ///
302 /// # Panics
303 ///
304 /// If the `regex` feature wasn't turned on for this crate this will always panic. Additionally if a
305 /// `cwd` (aka current working directory) or `binary_name` was set previously, this will panic, as those options
306 /// are incompatible with `regex`.
307 #[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 /// Sets the path name to search for. You ***MUST*** call this, or [`Self::regex`] prior to searching.
328 ///
329 /// # Panics
330 ///
331 /// If a `regex` was set previously this will panic as this is not compatible with `regex`.
332 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 /// Uses the given string instead of the `PATH` env variable.
342 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 /// Uses the `PATH` env variable. Enabled by default.
348 pub fn system_path_list(mut self) -> Self {
349 self.custom_path_list = None;
350 self
351 }
352
353 /// Finishes configuring, runs the query and returns the first result.
354 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 /// Finishes configuring, runs the query and returns all results.
360 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/// An owned, immutable wrapper around a `PathBuf` containing the path of an executable.
394///
395/// The constructed `PathBuf` is the output of `which` or `which_in`, but `which::Path` has the
396/// advantage of being a type distinct from `std::path::Path` and `std::path::PathBuf`.
397///
398/// It can be beneficial to use `which::Path` instead of `std::path::Path` when you want the type
399/// system to enforce the need for a path that exists and points to a binary that is executable.
400///
401/// Since `which::Path` implements `Deref` for `std::path::Path`, all methods on `&std::path::Path`
402/// are also available to `&which::Path` values.
403#[derive(Clone, PartialEq, Eq)]
404pub struct Path {
405 inner: path::PathBuf,
406}
407
408impl Path {
409 /// Returns the path of an executable binary by name.
410 ///
411 /// This calls `which` and maps the result into a `Path`.
412 pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<Path> {
413 which(binary_name).map(|inner| Path { inner })
414 }
415
416 /// Returns the paths of all executable binaries by a name.
417 ///
418 /// this calls `which_all` and maps the results into `Path`s.
419 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 /// Returns the path of an executable binary by name in the path list `paths` and using the
424 /// current working directory `cwd` to resolve relative paths.
425 ///
426 /// This calls `which_in` and maps the result into a `Path`.
427 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 /// Returns all paths of an executable binary by name in the path list `paths` and using the
437 /// current working directory `cwd` to resolve relative paths.
438 ///
439 /// This calls `which_in_all` and maps the results into a `Path`.
440 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 /// Returns a reference to a `std::path::Path`.
454 pub fn as_path(&self) -> &path::Path {
455 self.inner.as_path()
456 }
457
458 /// Consumes the `which::Path`, yielding its underlying `std::path::PathBuf`.
459 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/// An owned, immutable wrapper around a `PathBuf` containing the _canonical_ path of an
503/// executable.
504///
505/// The constructed `PathBuf` is the result of `which` or `which_in` followed by
506/// `Path::canonicalize`, but `CanonicalPath` has the advantage of being a type distinct from
507/// `std::path::Path` and `std::path::PathBuf`.
508///
509/// It can be beneficial to use `CanonicalPath` instead of `std::path::Path` when you want the type
510/// system to enforce the need for a path that exists, points to a binary that is executable, is
511/// absolute, has all components normalized, and has all symbolic links resolved
512///
513/// Since `CanonicalPath` implements `Deref` for `std::path::Path`, all methods on
514/// `&std::path::Path` are also available to `&CanonicalPath` values.
515#[derive(Clone, PartialEq, Eq)]
516pub struct CanonicalPath {
517 inner: path::PathBuf,
518}
519
520impl CanonicalPath {
521 /// Returns the canonical path of an executable binary by name.
522 ///
523 /// This calls `which` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
524 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 /// Returns the canonical paths of an executable binary by name.
531 ///
532 /// This calls `which_all` and `Path::canonicalize` and maps the results into `CanonicalPath`s.
533 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 /// Returns the canonical path of an executable binary by name in the path list `paths` and
547 /// using the current working directory `cwd` to resolve relative paths.
548 ///
549 /// This calls `which_in` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
550 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 /// Returns all of the canonical paths of an executable binary by name in the path list `paths` and
562 /// using the current working directory `cwd` to resolve relative paths.
563 ///
564 /// This calls `which_in_all` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
565 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 /// Returns a reference to a `std::path::Path`.
586 pub fn as_path(&self) -> &path::Path {
587 self.inner.as_path()
588 }
589
590 /// Consumes the `which::CanonicalPath`, yielding its underlying `std::path::PathBuf`.
591 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}
633