use super::alc_toml::validate_package_name;
use super::resolve::packages_dir;
use super::AppService;
impl AppService {
pub async fn pkg_unlink(&self, name: String) -> Result<String, String> {
validate_package_name(&name)?;
let pkgs = packages_dir()?;
let dest = pkgs.join(&name);
match dest.symlink_metadata() {
Ok(m) => {
if m.file_type().is_symlink() {
std::fs::remove_file(&dest)
.map_err(|e| format!("Failed to remove symlink {}: {e}", dest.display()))?;
} else {
return Err(format!(
"Package '{}' is not a symlink. Use pkg_remove to remove installed packages.",
name
));
}
}
Err(_) => {
return Err(format!("Package '{name}' is not installed"));
}
}
Ok(serde_json::json!({ "unlinked": name }).to_string())
}
}
#[cfg(all(test, unix))]
mod tests {
use std::os::unix::fs::symlink;
use crate::service::test_support::{make_app_service, FakeHome};
#[tokio::test]
async fn pkg_unlink_removes_symlink() {
let env = FakeHome::new();
let home = &env.home;
let pkgs = home.join(".algocline").join("packages");
std::fs::create_dir_all(&pkgs).unwrap();
let target = home.join("my_pkg");
std::fs::create_dir_all(&target).unwrap();
let dest = pkgs.join("my_pkg");
symlink(&target, &dest).unwrap();
let svc = make_app_service().await;
let result = svc.pkg_unlink("my_pkg".to_string()).await.unwrap();
let json: serde_json::Value = serde_json::from_str(&result).unwrap();
assert_eq!(json["unlinked"], "my_pkg");
assert!(!dest.symlink_metadata().is_ok());
}
#[tokio::test]
async fn pkg_unlink_real_dir_returns_error() {
let env = FakeHome::new();
let home = &env.home;
let pkgs = home.join(".algocline").join("packages");
let dest = pkgs.join("my_pkg");
std::fs::create_dir_all(&dest).unwrap();
let svc = make_app_service().await;
let err = svc.pkg_unlink("my_pkg".to_string()).await.unwrap_err();
assert!(err.contains("not a symlink"), "got: {err}");
}
#[tokio::test]
async fn pkg_unlink_not_installed_returns_error() {
let env = FakeHome::new();
let home = &env.home;
let pkgs = home.join(".algocline").join("packages");
std::fs::create_dir_all(&pkgs).unwrap();
let svc = make_app_service().await;
let err = svc.pkg_unlink("nonexistent".to_string()).await.unwrap_err();
assert!(err.contains("not installed"), "got: {err}");
}
#[tokio::test]
async fn pkg_unlink_dangling_symlink_removed() {
let env = FakeHome::new();
let home = &env.home;
let pkgs = home.join(".algocline").join("packages");
std::fs::create_dir_all(&pkgs).unwrap();
let dest = pkgs.join("dangling_pkg");
symlink(home.join("nowhere"), &dest).unwrap();
assert!(!dest.exists());
let svc = make_app_service().await;
let result = svc.pkg_unlink("dangling_pkg".to_string()).await.unwrap();
let json: serde_json::Value = serde_json::from_str(&result).unwrap();
assert_eq!(json["unlinked"], "dangling_pkg");
assert!(!dest.symlink_metadata().is_ok());
}
}