atomcode_core/uninstall/
mod.rs1pub mod actions;
7pub mod paths;
8pub mod scan;
9
10use std::path::PathBuf;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum Group {
14 Binary,
16 Credentials,
18 State,
20}
21
22#[derive(Debug, Clone)]
23pub struct Item {
24 pub group: Group,
25 pub path: PathBuf,
26 pub size_bytes: u64,
27 pub note: &'static str,
28 pub needs_privilege: bool,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct Decisions {
33 pub binary: bool,
34 pub credentials: bool,
35 pub state: bool,
36}
37
38impl Decisions {
39 pub const DEFAULTS: Self = Self {
40 binary: true,
41 credentials: false,
42 state: true,
43 };
44 pub const PURGE: Self = Self {
45 binary: true,
46 credentials: true,
47 state: true,
48 };
49 pub const KEEP_DATA: Self = Self {
50 binary: true,
51 credentials: false,
52 state: false,
53 };
54}
55
56#[derive(Debug, Default)]
59pub struct Outcome {
60 pub removed: Vec<PathBuf>,
61 pub kept: Vec<PathBuf>,
62 pub failed: Vec<(PathBuf, String)>,
63 pub backups: Vec<PathBuf>,
64}
65
66#[derive(Default)]
69pub struct ExecuteContext {
70 pub rc_files: Vec<(PathBuf, String)>,
72 #[cfg(windows)]
74 pub windows_install_dir_literal: Option<String>,
75 #[cfg(windows)]
77 pub windows_install_dir_expanded: Option<String>,
78}
79
80pub fn execute(
90 plan: &scan::Plan,
91 decisions: Decisions,
92 self_delete: &dyn actions::SelfDeleteStrategy,
93 ctx: Option<ExecuteContext>,
94) -> std::io::Result<Outcome> {
95 let mut out = Outcome::default();
96
97 let want = |g: Group| -> bool {
98 match g {
99 Group::Binary => decisions.binary,
100 Group::Credentials => decisions.credentials,
101 Group::State => decisions.state,
102 }
103 };
104
105 if want(Group::State) {
107 for it in plan.items.iter().filter(|i| i.group == Group::State) {
108 match actions::remove_path(&it.path, it.needs_privilege) {
109 Ok(()) => out.removed.push(it.path.clone()),
110 Err(e) => out.failed.push((it.path.clone(), e.to_string())),
111 }
112 }
113 } else {
114 for it in plan.items.iter().filter(|i| i.group == Group::State) {
115 out.kept.push(it.path.clone());
116 }
117 }
118
119 if want(Group::Credentials) {
121 for it in plan.items.iter().filter(|i| i.group == Group::Credentials) {
122 match actions::remove_path(&it.path, it.needs_privilege) {
123 Ok(()) => out.removed.push(it.path.clone()),
124 Err(e) => out.failed.push((it.path.clone(), e.to_string())),
125 }
126 }
127 } else {
128 for it in plan.items.iter().filter(|i| i.group == Group::Credentials) {
129 out.kept.push(it.path.clone());
130 }
131 }
132
133 if plan.atomcode_dir.exists() {
135 let _ = std::fs::remove_dir(&plan.atomcode_dir); }
137
138 if decisions.binary {
140 if let Some(c) = ctx.as_ref() {
141 for (rc, prefix) in &c.rc_files {
142 match actions::apply_unix_path_cleanup(rc, prefix) {
143 Ok(r) if r.modified => {
144 out.removed.push(rc.clone());
145 if let Some(b) = r.backup_path {
146 out.backups.push(b);
147 }
148 }
149 Ok(_) => {}
150 Err(e) => out.failed.push((rc.clone(), e.to_string())),
151 }
152 }
153 #[cfg(windows)]
154 if let (Some(lit), Some(exp)) = (
155 c.windows_install_dir_literal.as_ref(),
156 c.windows_install_dir_expanded.as_ref(),
157 ) {
158 match actions::apply_windows_path_cleanup(lit, exp) {
159 Ok(true) => out.removed.push(PathBuf::from("HKCU\\Environment\\Path")),
160 Ok(false) => {}
161 Err(e) => out
162 .failed
163 .push((PathBuf::from("HKCU\\Environment\\Path"), e.to_string())),
164 }
165 }
166 }
167 }
168
169 if decisions.binary {
171 for it in plan
172 .items
173 .iter()
174 .filter(|i| i.group == Group::Binary && i.path != plan.binary_path)
175 {
176 match actions::remove_path(&it.path, it.needs_privilege) {
177 Ok(()) => out.removed.push(it.path.clone()),
178 Err(e) => out.failed.push((it.path.clone(), e.to_string())),
179 }
180 }
181 match self_delete.run(&plan.binary_path) {
183 Ok(()) => out.removed.push(plan.binary_path.clone()),
184 Err(e) => out.failed.push((plan.binary_path.clone(), e.to_string())),
185 }
186 } else {
187 for it in plan.items.iter().filter(|i| i.group == Group::Binary) {
188 out.kept.push(it.path.clone());
189 }
190 }
191
192 Ok(out)
193}