Skip to main content

dolly_cli/
binary.rs

1use std::io;
2use std::path::{Path, PathBuf};
3
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum BinaryError {
8    #[error("build output not found at `{0}`; check `[build].output` in your recipe")]
9    OutputMissing(PathBuf),
10
11    #[error(transparent)]
12    Io(#[from] io::Error),
13}
14
15pub fn place(src: &Path, dest: &Path) -> Result<(), BinaryError> {
16    if !src.exists() {
17        return Err(BinaryError::OutputMissing(src.to_path_buf()));
18    }
19
20    if let Some(parent) = dest.parent() {
21        std::fs::create_dir_all(parent)?;
22    }
23
24    std::fs::copy(src, dest)?;
25    Ok(())
26}
27
28pub fn make_executable(path: &Path) -> Result<(), BinaryError> {
29    #[cfg(unix)]
30    {
31        use std::os::unix::fs::PermissionsExt;
32        let mut perms = std::fs::metadata(path)?.permissions();
33        perms.set_mode(0o755);
34        std::fs::set_permissions(path, perms)?;
35    }
36    Ok(())
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn place_copies_file_contents() {
45        let dir = tempfile::tempdir().unwrap();
46        let src = dir.path().join("src.bin");
47        let dest = dir.path().join("subdir").join("dest.bin");
48
49        std::fs::write(&src, b"hello world").unwrap();
50        place(&src, &dest).unwrap();
51
52        let contents = std::fs::read(&dest).unwrap();
53        assert_eq!(contents, b"hello world");
54    }
55
56    #[test]
57    fn place_creates_missing_parent_dir() {
58        let dir = tempfile::tempdir().unwrap();
59        let src = dir.path().join("src.bin");
60        let dest = dir.path().join("nested").join("dirs").join("dest.bin");
61
62        std::fs::write(&src, b"x").unwrap();
63        place(&src, &dest).unwrap();
64
65        assert!(dest.exists());
66    }
67
68    #[test]
69    fn place_overwrites_existing_dest() {
70        let dir = tempfile::tempdir().unwrap();
71        let src = dir.path().join("src.bin");
72        let dest = dir.path().join("dest.bin");
73
74        std::fs::write(&src, b"new").unwrap();
75        std::fs::write(&dest, b"old").unwrap();
76        place(&src, &dest).unwrap();
77
78        assert_eq!(std::fs::read(&dest).unwrap(), b"new");
79    }
80
81    #[test]
82    fn place_returns_output_missing_when_src_absent() {
83        let dir = tempfile::tempdir().unwrap();
84        let src = dir.path().join("missing.bin");
85        let dest = dir.path().join("dest.bin");
86
87        let err = place(&src, &dest).unwrap_err();
88        assert!(matches!(err, BinaryError::OutputMissing(p) if p == src));
89    }
90
91    #[cfg(unix)]
92    #[test]
93    fn make_executable_sets_mode_755() {
94        use std::os::unix::fs::PermissionsExt;
95
96        let dir = tempfile::tempdir().unwrap();
97        let path = dir.path().join("bin");
98        std::fs::write(&path, b"x").unwrap();
99
100        let mut perms = std::fs::metadata(&path).unwrap().permissions();
101        perms.set_mode(0o644);
102        std::fs::set_permissions(&path, perms).unwrap();
103
104        make_executable(&path).unwrap();
105
106        let mode = std::fs::metadata(&path).unwrap().permissions().mode() & 0o777;
107        assert_eq!(mode, 0o755);
108    }
109}