use std::io;
use std::path::{Path, PathBuf};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BinaryError {
#[error("build output not found at `{0}`; check `[build].output` in your recipe")]
OutputMissing(PathBuf),
#[error(transparent)]
Io(#[from] io::Error),
}
pub fn place(src: &Path, dest: &Path) -> Result<(), BinaryError> {
if !src.exists() {
return Err(BinaryError::OutputMissing(src.to_path_buf()));
}
if let Some(parent) = dest.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::copy(src, dest)?;
Ok(())
}
pub fn make_executable(path: &Path) -> Result<(), BinaryError> {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(path)?.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(path, perms)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn place_copies_file_contents() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("src.bin");
let dest = dir.path().join("subdir").join("dest.bin");
std::fs::write(&src, b"hello world").unwrap();
place(&src, &dest).unwrap();
let contents = std::fs::read(&dest).unwrap();
assert_eq!(contents, b"hello world");
}
#[test]
fn place_creates_missing_parent_dir() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("src.bin");
let dest = dir.path().join("nested").join("dirs").join("dest.bin");
std::fs::write(&src, b"x").unwrap();
place(&src, &dest).unwrap();
assert!(dest.exists());
}
#[test]
fn place_overwrites_existing_dest() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("src.bin");
let dest = dir.path().join("dest.bin");
std::fs::write(&src, b"new").unwrap();
std::fs::write(&dest, b"old").unwrap();
place(&src, &dest).unwrap();
assert_eq!(std::fs::read(&dest).unwrap(), b"new");
}
#[test]
fn place_returns_output_missing_when_src_absent() {
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("missing.bin");
let dest = dir.path().join("dest.bin");
let err = place(&src, &dest).unwrap_err();
assert!(matches!(err, BinaryError::OutputMissing(p) if p == src));
}
#[cfg(unix)]
#[test]
fn make_executable_sets_mode_755() {
use std::os::unix::fs::PermissionsExt;
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("bin");
std::fs::write(&path, b"x").unwrap();
let mut perms = std::fs::metadata(&path).unwrap().permissions();
perms.set_mode(0o644);
std::fs::set_permissions(&path, perms).unwrap();
make_executable(&path).unwrap();
let mode = std::fs::metadata(&path).unwrap().permissions().mode() & 0o777;
assert_eq!(mode, 0o755);
}
}