pub mod actions;
pub mod paths;
pub mod scan;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Group {
Binary,
Credentials,
State,
}
#[derive(Debug, Clone)]
pub struct Item {
pub group: Group,
pub path: PathBuf,
pub size_bytes: u64,
pub note: &'static str,
pub needs_privilege: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Decisions {
pub binary: bool,
pub credentials: bool,
pub state: bool,
}
impl Decisions {
pub const DEFAULTS: Self = Self {
binary: true,
credentials: false,
state: true,
};
pub const PURGE: Self = Self {
binary: true,
credentials: true,
state: true,
};
pub const KEEP_DATA: Self = Self {
binary: true,
credentials: false,
state: false,
};
}
#[derive(Debug, Default)]
pub struct Outcome {
pub removed: Vec<PathBuf>,
pub kept: Vec<PathBuf>,
pub failed: Vec<(PathBuf, String)>,
pub backups: Vec<PathBuf>,
}
#[derive(Default)]
pub struct ExecuteContext {
pub rc_files: Vec<(PathBuf, String)>,
#[cfg(windows)]
pub windows_install_dir_literal: Option<String>,
#[cfg(windows)]
pub windows_install_dir_expanded: Option<String>,
}
pub fn execute(
plan: &scan::Plan,
decisions: Decisions,
self_delete: &dyn actions::SelfDeleteStrategy,
ctx: Option<ExecuteContext>,
) -> std::io::Result<Outcome> {
let mut out = Outcome::default();
let want = |g: Group| -> bool {
match g {
Group::Binary => decisions.binary,
Group::Credentials => decisions.credentials,
Group::State => decisions.state,
}
};
if want(Group::State) {
for it in plan.items.iter().filter(|i| i.group == Group::State) {
match actions::remove_path(&it.path, it.needs_privilege) {
Ok(()) => out.removed.push(it.path.clone()),
Err(e) => out.failed.push((it.path.clone(), e.to_string())),
}
}
} else {
for it in plan.items.iter().filter(|i| i.group == Group::State) {
out.kept.push(it.path.clone());
}
}
if want(Group::Credentials) {
for it in plan.items.iter().filter(|i| i.group == Group::Credentials) {
match actions::remove_path(&it.path, it.needs_privilege) {
Ok(()) => out.removed.push(it.path.clone()),
Err(e) => out.failed.push((it.path.clone(), e.to_string())),
}
}
} else {
for it in plan.items.iter().filter(|i| i.group == Group::Credentials) {
out.kept.push(it.path.clone());
}
}
if plan.atomcode_dir.exists() {
let _ = std::fs::remove_dir(&plan.atomcode_dir);
}
if decisions.binary {
if let Some(c) = ctx.as_ref() {
for (rc, prefix) in &c.rc_files {
match actions::apply_unix_path_cleanup(rc, prefix) {
Ok(r) if r.modified => {
out.removed.push(rc.clone());
if let Some(b) = r.backup_path {
out.backups.push(b);
}
}
Ok(_) => {}
Err(e) => out.failed.push((rc.clone(), e.to_string())),
}
}
#[cfg(windows)]
if let (Some(lit), Some(exp)) = (
c.windows_install_dir_literal.as_ref(),
c.windows_install_dir_expanded.as_ref(),
) {
match actions::apply_windows_path_cleanup(lit, exp) {
Ok(true) => out.removed.push(PathBuf::from("HKCU\\Environment\\Path")),
Ok(false) => {}
Err(e) => out
.failed
.push((PathBuf::from("HKCU\\Environment\\Path"), e.to_string())),
}
}
}
}
if decisions.binary {
for it in plan
.items
.iter()
.filter(|i| i.group == Group::Binary && i.path != plan.binary_path)
{
match actions::remove_path(&it.path, it.needs_privilege) {
Ok(()) => out.removed.push(it.path.clone()),
Err(e) => out.failed.push((it.path.clone(), e.to_string())),
}
}
match self_delete.run(&plan.binary_path) {
Ok(()) => out.removed.push(plan.binary_path.clone()),
Err(e) => out.failed.push((plan.binary_path.clone(), e.to_string())),
}
} else {
for it in plan.items.iter().filter(|i| i.group == Group::Binary) {
out.kept.push(it.path.clone());
}
}
Ok(out)
}