cargo_hoist/
executables.rs

1//! Executables
2//!
3//! Utilities for working with executables.
4
5use anyhow::Result;
6use std::os::unix::fs::PermissionsExt;
7use std::path::Path;
8use tracing::instrument;
9
10/// Returns the fully qualified path for a given binary
11/// if it is an executable.
12#[instrument]
13pub fn exec_path(exec: &Path) -> Result<String> {
14    let is_file = std::fs::metadata(exec)?.is_file();
15    let is_exec = std::fs::metadata(exec)?.permissions().mode() & 0o111 != 0;
16    if !is_file || !is_exec {
17        anyhow::bail!("{} is not executable", exec.display());
18    }
19    let bin_file_name = exec
20        .file_name()
21        .ok_or(anyhow::anyhow!("[std] failed to extract binary name"))?;
22    let binary_name = bin_file_name
23        .to_str()
24        .ok_or(anyhow::anyhow!(
25            "[std] failed to convert binary path name to string"
26        ))?
27        .to_string();
28    tracing::debug!("retrieved binary name: {}", binary_name);
29    Ok(binary_name)
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35    use std::os::unix::fs::OpenOptionsExt;
36    use std::path::PathBuf;
37    use tempfile::TempDir;
38
39    #[test]
40    fn test_exec_path() {
41        let tempdir = tempfile::tempdir().unwrap();
42        let test_dir = setup_test(&tempdir, "test_exec_path");
43        let binaries = create_binaries(&test_dir);
44        let bin1 = binaries.get(0).unwrap();
45        let bin1_path = test_dir.join("target/release/binary1");
46        let bin1_exec_path = exec_path(&bin1_path).unwrap();
47        assert_eq!(bin1, &bin1_exec_path);
48    }
49
50    fn setup_test(tempdir: &TempDir, t: &str) -> PathBuf {
51        let test_dir = tempdir.path().join(t);
52        std::fs::create_dir(&test_dir).unwrap();
53        std::env::set_current_dir(&test_dir).unwrap();
54        test_dir
55    }
56
57    fn create_binaries(p: &Path) -> Vec<String> {
58        let target_dir = p.join("target/release/");
59        std::fs::create_dir_all(&target_dir).unwrap();
60        let bin1_path = target_dir.join("binary1");
61        let bin2_path = target_dir.join("binary2");
62        let opts = std::fs::OpenOptions::new()
63            .write(true)
64            .create(true)
65            .mode(0o755)
66            .open(&bin1_path)
67            .unwrap();
68        opts.sync_all().unwrap();
69        let opts = std::fs::OpenOptions::new()
70            .write(true)
71            .create(true)
72            .mode(0o755)
73            .open(&bin2_path)
74            .unwrap();
75        opts.sync_all().unwrap();
76        let mut res = Vec::with_capacity(2);
77        res.push("binary1".to_string());
78        res.push("binary2".to_string());
79        res
80    }
81}