use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
#[cfg(windows)]
use std::ffi::OsStr;
pub struct SymlinkManager<'a> {
symlinks_dir: &'a Path,
}
impl<'a> SymlinkManager<'a> {
fn platform_link_path(link: &Path) -> std::path::PathBuf {
#[cfg(windows)]
{
if link.extension() != Some(OsStr::new("exe")) {
return link.with_extension("exe");
}
}
link.to_path_buf()
}
pub fn new(symlinks_dir: &'a Path) -> Self {
Self { symlinks_dir }
}
pub fn add_link(&self, exec_path: &Path, name: &str) -> Result<()> {
if !exec_path.exists() {
anyhow::bail!("Target file not found: {}", exec_path.display());
}
let base_link = self.symlinks_dir.join(name);
let symlink = Self::platform_link_path(&base_link);
if symlink.exists() {
fs::remove_file(&symlink).context("Failed to remove existing symlink")?;
}
if base_link != symlink && base_link.exists() {
fs::remove_file(&base_link).context("Failed to remove stale symlink")?;
}
Self::create_symlink(exec_path, &symlink)?;
Ok(())
}
pub fn remove_link(&self, name: &str) -> Result<()> {
let base_link = self.symlinks_dir.join(name);
let symlink = Self::platform_link_path(&base_link);
if symlink.exists() {
fs::remove_file(&symlink).context("Failed to remove symlink")?;
}
if base_link != symlink && base_link.exists() {
fs::remove_file(&base_link).context("Failed to remove stale symlink")?;
}
Ok(())
}
#[cfg(unix)]
fn create_symlink(target_path: &Path, symlink: &Path) -> Result<()> {
std::os::unix::fs::symlink(target_path, symlink).context("Failed to create symlink")
}
#[cfg(windows)]
fn create_symlink(target_path: &Path, link: &Path) -> Result<()> {
fs::hard_link(target_path, link).context("Failed to create hardlink")
}
}