cargo_exe_v2/
lib.rs

1//! A very simple tool to print the executable output of `cargo build`.
2#![deny(missing_docs)]
3
4use std::{path::{Path, PathBuf}, time::SystemTime};
5use cargo_toml::{Error, Manifest};
6
7
8/// Name of the output directory for the Debug profile.
9pub const DIR_DEBUG: &str = "debug";
10/// Name of the output directory for the Release profile.
11pub const DIR_RELEASE: &str = "release";
12/// Name of the directory which contains all output directories.
13pub const DIR_TARGET: &str = "target";
14/// Name of the crate manifest file.
15pub const FILE_MANIFEST: &str = "Cargo.toml";
16
17
18fn find_recursive(
19    path_dir: &Path,
20    path_sub: &Path,
21    results: &mut Vec<PathBuf>,
22    recurse: usize,
23) -> std::io::Result<()> {
24    for sub in path_dir.read_dir()? {
25        if let Ok(entry) = sub {
26            let path: PathBuf = entry.path();
27
28            if path.is_dir() {
29                let mut path_file: PathBuf = path.clone();
30                path_file.push(path_sub);
31
32                if path_file.is_file() {
33                    results.push(path_file);
34                }
35
36                if 0 < recurse {
37                    find_recursive(&path, path_sub, results, recurse - 1)?;
38                }
39            }
40        }
41    }
42
43    Ok(())
44}
45
46
47/// Operational behavior. Represents the approach taken to finding an executable
48///     file path.
49pub enum Mode {
50    /// The executable is assumed to be within `/target/debug/`.
51    Debug,
52    /// The executable is assumed to be within `/target/release/`.
53    Release,
54    /// The entirely of `/target/**/` will be searched, and the path to the most
55    ///     recently modified executable will be returned.
56    Latest,
57}
58
59impl Mode {
60    /// Given a path to a `target/` directory and the name of an executable
61    ///     file, return a path to the file according to the defined behavior.
62    ///
63    /// Returns `None` if the mode is `Latest` and the file cannot be found
64    ///     anywhere in the target directory.
65    pub fn make_path(
66        &self,
67        path_dir_target: impl Into<PathBuf>,
68        path_exe: impl AsRef<Path>,
69    ) -> Option<PathBuf> {
70        let mut path: PathBuf = path_dir_target.into();
71        let path_exe: &Path = path_exe.as_ref();
72
73        match self {
74            Mode::Debug => {
75                path.push(DIR_DEBUG);
76                path.push(path_exe);
77                Some(path)
78            }
79            Mode::Release => {
80                path.push(DIR_RELEASE);
81                path.push(path_exe);
82                Some(path)
83            }
84            Mode::Latest => {
85                let mut paths = Vec::new();
86
87                if let Err(e) = find_recursive(&path, path_exe, &mut paths, 2) {
88                    eprintln!(
89                        "WARNING: An error occurred while searching in `{}`. \
90                        Results may not be complete.\
91                        \n  Error: {}",
92                        path.display(),
93                        e
94                    );
95                }
96
97                paths.sort_by_key(|path| {
98                    path.metadata()
99                        .and_then(|meta| meta.modified())
100                        .unwrap_or(SystemTime::UNIX_EPOCH)
101                });
102
103                paths.pop()
104            }
105        }
106    }
107}
108
109
110/// If the given Path is a file, return its parent directory. Otherwise, return
111///     it unchanged.
112pub fn path_project(dir_or_manifest: &Path) -> &Path {
113    if dir_or_manifest.is_file() {
114        match dir_or_manifest.parent() {
115            Some(parent) => parent,
116            None => ".".as_ref(),
117        }
118    } else {
119        dir_or_manifest
120    }
121}
122
123
124/// Given a path to a `Cargo.toml` crate manifest, read the manifest and attempt
125///     to return the names of all executable binaries generated by the project.
126///
127/// Returns an `Err` if reading the manifest fails.
128pub fn names_bin(path_manifest: impl AsRef<Path>) -> Result<Vec<String>, Error> {
129    let manifest = Manifest::from_path(path_manifest)?;
130    Ok(manifest.bin.into_iter().filter_map(|p| p.name).collect())
131}