algocline_app/service/pkg/
remove.rs1use super::super::lockfile::{load_lockfile, lockfile_path, save_lockfile};
4use super::super::manifest;
5use super::super::path::ContainedPath;
6use super::super::project::resolve_project_root;
7use super::super::resolve::packages_dir;
8use super::super::AppService;
9
10impl AppService {
11 pub async fn pkg_remove(
20 &self,
21 name: &str,
22 project_root: Option<String>,
23 scope: Option<String>,
24 ) -> Result<String, String> {
25 let effective_scope =
26 determine_remove_scope(name, project_root.as_deref(), scope.as_deref());
27
28 match effective_scope {
29 RemoveScope::Project(root) => {
30 let mut lock = match load_lockfile(&root)? {
32 Some(l) => l,
33 None => {
34 return Err(format!(
35 "Package '{name}' not found in project (no alc.lock at {})",
36 root.display()
37 ));
38 }
39 };
40
41 let before = lock.packages.len();
42 lock.packages.retain(|p| p.name != name);
43
44 if lock.packages.len() == before {
45 return Err(format!(
46 "Package '{name}' not found in alc.lock at {}",
47 root.display()
48 ));
49 }
50
51 save_lockfile(&root, &lock)?;
52
53 Ok(serde_json::json!({
54 "removed": name,
55 "scope": "project",
56 "lockfile": lockfile_path(&root).display().to_string(),
57 })
58 .to_string())
59 }
60 RemoveScope::Global => {
61 let pkg_dir = packages_dir()?;
63 let dest = ContainedPath::child(&pkg_dir, name)?;
64
65 if !dest.as_ref().exists() {
66 return Err(format!("Package '{name}' not found"));
67 }
68
69 std::fs::remove_dir_all(&dest)
70 .map_err(|e| format!("Failed to remove '{name}': {e}"))?;
71
72 let _ = manifest::record_remove(name);
74
75 Ok(serde_json::json!({ "removed": name, "scope": "global" }).to_string())
76 }
77 }
78 }
79}
80
81enum RemoveScope {
84 Project(std::path::PathBuf),
85 Global,
86}
87
88fn determine_remove_scope(
97 name: &str,
98 project_root: Option<&str>,
99 scope: Option<&str>,
100) -> RemoveScope {
101 if scope == Some("global") {
103 return RemoveScope::Global;
104 }
105
106 let root = match resolve_project_root(project_root) {
107 Some(r) => r,
108 None => return RemoveScope::Global,
109 };
110
111 if project_root.is_some() {
113 return RemoveScope::Project(root);
114 }
115
116 if scope == Some("project") {
118 return RemoveScope::Project(root);
119 }
120
121 match load_lockfile(&root) {
124 Ok(Some(lock)) if lock.packages.iter().any(|p| p.name == name) => {
125 RemoveScope::Project(root)
126 }
127 _ => RemoveScope::Global,
128 }
129}