1use std::fs;
8use std::path::Path;
9use vanta_core::{Area, VtaError, VtaResult};
10
11pub fn link_best(src: &Path, dst: &Path) -> VtaResult<&'static str> {
14 if let Some(parent) = dst.parent() {
15 fs::create_dir_all(parent).map_err(|e| err(dst, e))?;
16 }
17 let _ = fs::remove_file(dst); if fs::hard_link(src, dst).is_ok() {
20 return Ok("hardlink");
21 }
22 if symlink(src, dst).is_ok() {
23 return Ok("symlink");
24 }
25 fs::copy(src, dst).map_err(|e| err(dst, e))?;
26 Ok("copy")
27}
28
29#[cfg(unix)]
30fn symlink(src: &Path, dst: &Path) -> std::io::Result<()> {
31 std::os::unix::fs::symlink(src, dst)
32}
33
34#[cfg(windows)]
35fn symlink(src: &Path, dst: &Path) -> std::io::Result<()> {
36 std::os::windows::fs::symlink_file(src, dst)
37}
38
39fn err(path: &Path, e: std::io::Error) -> VtaError {
40 VtaError::new(Area::Store, 3, format!("linking {}: {e}", path.display()))
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 #[test]
48 fn links_a_file() {
49 let dir = std::env::temp_dir().join(format!("vanta-link-{}", std::process::id()));
50 let _ = fs::remove_dir_all(&dir);
51 fs::create_dir_all(&dir).unwrap();
52 let src = dir.join("src.bin");
53 let dst = dir.join("bin/tool");
54 fs::write(&src, b"#!/bin/true").unwrap();
55 let how = link_best(&src, &dst).unwrap();
56 assert!(["hardlink", "symlink", "copy"].contains(&how));
57 assert_eq!(fs::read(&dst).unwrap(), b"#!/bin/true");
58 let _ = fs::remove_dir_all(&dir);
59 }
60}