1#[cfg(feature = "rayon")]
2use rayon::prelude::*;
3
4use std::{
5 collections::HashSet,
6 env::{self, split_paths, VarError},
7 hash::Hash,
8 path::PathBuf,
9};
10
11#[cfg(unix)]
12mod unix;
13#[cfg(unix)]
14use unix::search_dir;
15
16#[cfg(windows)]
17mod windows;
18#[cfg(windows)]
19use windows::search_dir;
20
21#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
22pub struct Executable {
23 pub name: String,
24 pub path: PathBuf,
25}
26
27pub fn executables() -> Result<Vec<Executable>, VarError> {
31 let path = env::var("PATH")?;
32 let paths = split_paths(&path);
33
34 let search_dir = search_dir()?;
35
36 #[cfg(feature = "rayon")]
37 let executables = paths
38 .collect::<Vec<_>>()
39 .into_par_iter()
40 .filter_map(search_dir)
41 .reduce(Vec::new, |mut a, b| {
42 a.extend_from_slice(&b);
43 a
44 });
45 #[cfg(not(feature = "rayon"))]
46 let executables = paths.filter_map(search_dir).fold(Vec::new(), |mut a, b| {
47 a.extend_from_slice(&b);
48 a
49 });
50
51 Ok(executables)
52}
53
54#[derive(Debug, Clone, Eq)]
55struct UniqueExecutable(Executable);
56
57impl PartialEq for UniqueExecutable {
58 fn eq(&self, other: &Self) -> bool {
59 self.0.name == other.0.name
60 }
61}
62
63impl Hash for UniqueExecutable {
64 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
65 self.0.name.hash(state);
66 }
67}
68
69pub fn unique_executables() -> Result<Vec<Executable>, VarError> {
77 let path = env::var("PATH")?;
78
79 Ok(split_paths(&path)
80 .filter_map(search_dir()?)
81 .flat_map(|dir| dir.into_iter().map(UniqueExecutable))
82 .fold(HashSet::new(), |mut set, executable| {
83 if !set.contains(&executable) {
84 set.insert(executable);
85 };
86 set
87 })
88 .into_iter()
89 .map(|e| e.0)
90 .collect())
91}
92
93#[test]
94fn test() {
95 executables().unwrap();
96}