soar_core/package/
remove.rs1use std::{
2 ffi::OsString,
3 fs,
4 path::{Path, PathBuf},
5 sync::{Arc, Mutex},
6};
7
8use rusqlite::{params, Connection};
9
10use crate::{
11 config::get_config,
12 database::{models::InstalledPackage, packages::ProvideStrategy},
13 error::ErrorContext,
14 utils::{desktop_dir, icons_dir, process_dir},
15 SoarResult,
16};
17
18pub struct PackageRemover {
19 package: InstalledPackage,
20 db: Arc<Mutex<Connection>>,
21}
22
23impl PackageRemover {
24 pub async fn new(package: InstalledPackage, db: Arc<Mutex<Connection>>) -> Self {
25 Self { package, db }
26 }
27
28 pub async fn remove(&self) -> SoarResult<()> {
29 let mut conn = self.db.lock()?;
30 let tx = conn.transaction()?;
31
32 if self.package.is_installed {
35 let bin_path = get_config().get_bin_path()?;
36 let def_bin = bin_path.join(&self.package.pkg_name);
37 if def_bin.is_symlink() && def_bin.is_file() {
38 fs::remove_file(&def_bin)
39 .with_context(|| format!("removing binary {}", def_bin.display()))?;
40 }
41
42 if let Some(provides) = &self.package.provides {
43 for provide in provides {
44 if let Some(ref target) = provide.target {
45 let is_symlink = matches!(
46 provide.strategy,
47 Some(ProvideStrategy::KeepTargetOnly) | Some(ProvideStrategy::KeepBoth)
48 );
49 if is_symlink {
50 let target_name = bin_path.join(target);
51 if target_name.exists() {
52 std::fs::remove_file(&target_name).with_context(|| {
53 format!("removing provide {}", target_name.display())
54 })?;
55 }
56 }
57 }
58 }
59 }
60
61 let installed_path = PathBuf::from(&self.package.installed_path);
62
63 let mut remove_action = |path: &Path| -> SoarResult<()> {
64 if path.extension() == Some(&OsString::from("desktop")) {
65 if let Ok(real_path) = fs::read_link(path) {
66 if real_path.parent() == Some(&installed_path) {
67 let _ = fs::remove_file(path);
68 }
69 }
70 }
71 Ok(())
72 };
73 process_dir(desktop_dir(), &mut remove_action)?;
74
75 let mut remove_action = |path: &Path| -> SoarResult<()> {
76 if let Ok(real_path) = fs::read_link(path) {
77 if real_path.parent() == Some(&installed_path) {
78 let _ = fs::remove_file(path);
79 }
80 }
81 Ok(())
82 };
83 process_dir(icons_dir(), &mut remove_action)?;
84 }
85
86 if let Err(err) = fs::remove_dir_all(&self.package.installed_path) {
87 if err.kind() != std::io::ErrorKind::NotFound {
89 return Err(err).with_context(|| {
90 format!("removing package directory {}", self.package.installed_path)
91 })?;
92 }
93 };
94
95 {
96 let mut stmt = tx.prepare(
97 r#"
98 DELETE FROM packages WHERE id = ?
99 "#,
100 )?;
101 stmt.execute(params![self.package.id])?;
102
103 let mut stmt = tx.prepare(
104 r#"
105 DELETE FROM portable_package WHERE package_id = ?
106 "#,
107 )?;
108 stmt.execute(params![self.package.id])?;
109 }
110
111 tx.commit()?;
112
113 Ok(())
114 }
115}