pkg/backend/pkgar_backend/
mod.rs1use std::{
2 cell::RefCell,
3 fs,
4 path::{Path, PathBuf},
5 rc::Rc,
6};
7
8use pkgar::{PackageFile, Transaction};
9use pkgar_keys::PublicKeyFile;
10
11use self::packages::Packages;
12use super::{Backend, Callback, Error};
13use crate::{repo_manager::RepoManager, PackageName, DOWNLOAD_PATH, PACKAGES_PATH};
14
15mod packages;
16
17pub struct PkgarBackend {
18 install_path: PathBuf,
19 packages: Packages,
20 repo_manager: RepoManager,
21 pkey_file: PublicKeyFile,
22}
23
24const PACKAGES_DIR: &str = "pkg/packages";
25
26impl PkgarBackend {
27 pub fn new<P: AsRef<Path>>(
28 install_path: P,
29 repo_manager: RepoManager,
30 callback: Rc<RefCell<dyn Callback>>,
31 ) -> Result<Self, Error> {
32 let install_path = install_path.as_ref();
33
34 let packages_path = install_path.join(PACKAGES_PATH);
35 let file = fs::read_to_string(&packages_path);
36
37 let packages;
38 match file {
39 Ok(toml) => {
40 packages = Packages::from_toml(&toml)?;
41 }
42
43 Err(_) => {
44 packages = Packages::default();
45 fs::create_dir_all(Path::new(&packages_path).parent().unwrap())?;
46 fs::write(packages_path, packages.to_toml())?;
47 }
48 }
49
50 let packages_dir = install_path.join(PACKAGES_DIR);
51 fs::create_dir_all(&packages_dir)?;
52
53 fs::create_dir_all("/tmp/pkg/")?;
54 repo_manager.download_backend.download(
55 "https://static.redox-os.org/pkg/id_ed25519.pub.toml",
56 Path::new("/tmp/pkg/pub_key.toml"),
57 callback,
58 )?;
59
60 Ok(PkgarBackend {
61 install_path: install_path.to_path_buf(),
62 packages,
63 repo_manager,
64 pkey_file: PublicKeyFile::open("/tmp/pkg/pub_key.toml")?,
65 })
66 }
67
68 fn get_package_head(&mut self, package: &PackageName) -> Result<PackageFile, Error> {
69 let path = self
70 .install_path
71 .join(PACKAGES_DIR)
72 .join(format!("{package}.pkgar_head"));
73
74 Ok(PackageFile::new(path, &self.pkey_file.pkey)?)
75 }
76
77 fn get_package(&mut self, package: &PackageName) -> Result<PackageFile, Error> {
78 Ok(PackageFile::new(
79 format!("{}/{package}.pkgar", DOWNLOAD_PATH),
80 &self.pkey_file.pkey,
81 )?)
82 }
83
84 fn remove_package_head(&mut self, package: &PackageName) -> Result<(), Error> {
85 let path = self
86 .install_path
87 .join(PACKAGES_DIR)
88 .join(format!("{package}.pkgar_head"));
89
90 fs::remove_file(path)?;
91 Ok(())
92 }
93
94 fn create_head(&mut self, package: &PackageName) -> Result<(), Error> {
95 pkgar::split(
97 "/tmp/pkg/pub_key.toml",
98 format!("{}/{package}.pkgar", DOWNLOAD_PATH),
99 self.install_path
100 .join(PACKAGES_DIR)
101 .join(format!("{package}.pkgar_head")),
102 Option::<&str>::None,
103 )?;
104
105 Ok(())
106 }
107}
108
109impl Backend for PkgarBackend {
110 fn install(&mut self, package: PackageName) -> Result<(), Error> {
111 self.repo_manager.sync_pkgar(&package)?;
112
113 let mut pkg = self.get_package(&package)?;
114
115 let mut install = Transaction::install(&mut pkg, &self.install_path)?;
116 install.commit()?;
117
118 self.create_head(&package)?;
119
120 Ok(())
121 }
122
123 fn uninstall(&mut self, package: PackageName) -> Result<(), Error> {
124 if self.packages.protected.contains(&package) {
125 return Err(Error::ProtectedPackage(package));
126 }
127
128 let mut pkg = self.get_package_head(&package)?;
129 let mut remove = Transaction::remove(&mut pkg, &self.install_path)?;
130 remove.commit()?;
131
132 self.remove_package_head(&package)?;
133
134 Ok(())
135 }
136
137 fn upgrade(&mut self, package: PackageName) -> Result<(), Error> {
138 let mut pkg = self.get_package_head(&package)?;
139
140 self.repo_manager.sync_pkgar(&package)?;
141 let mut pkg2 = self.get_package(&package)?;
142
143 let mut update = Transaction::replace(&mut pkg, &mut pkg2, &self.install_path)?;
144 update.commit()?;
145
146 self.create_head(&package)?;
147
148 Ok(())
149 }
150
151 fn get_installed_packages(&self) -> Result<Vec<PackageName>, Error> {
152 let entries = fs::read_dir(self.install_path.join(PACKAGES_DIR))?;
153
154 let mut packages = vec![];
155
156 for entry in entries {
157 let entry = entry?;
158 let file_name = entry.file_name();
159 let file_name_str = file_name.to_str().ok_or(Error::IO(std::io::Error::new(
160 std::io::ErrorKind::Other,
161 "file name isn't UTF-8",
162 )))?;
163
164 if file_name_str.ends_with(".pkgar_head") {
165 let package = file_name_str.replace(".pkgar_head", "");
166 packages.push(PackageName::new(package)?);
167 }
168 }
169
170 Ok(packages)
171 }
172}
173
174impl Drop for PkgarBackend {
175 fn drop(&mut self) {
176 let packages_path = self.install_path.join(PACKAGES_PATH);
177 fs::write(packages_path, self.packages.to_toml()).unwrap();
178 }
179}