pkg/backend/pkgar_backend/
mod.rs1use std::{
2 collections::HashMap,
3 fs,
4 path::{Path, PathBuf},
5};
6
7use pkgar::{PackageFile, Transaction};
8use pkgar_keys::PublicKeyFile;
9
10use self::packages::Packages;
11use super::{Backend, Error};
12use crate::{
13 package::Repository, repo_manager::RepoManager, Package, PackageName, DOWNLOAD_PATH,
14 PACKAGES_PATH,
15};
16
17mod packages;
18
19pub struct PkgarBackend {
20 install_path: PathBuf,
21 packages: Packages,
22 repo_manager: RepoManager,
23 pkey_files: HashMap<String, PublicKeyFile>,
24}
25
26const PACKAGES_DIR: &str = "pkg/packages";
27
28impl PkgarBackend {
29 pub fn new<P: AsRef<Path>>(install_path: P, repo_manager: RepoManager) -> Result<Self, Error> {
30 let install_path = install_path.as_ref();
31
32 let packages_path = install_path.join(PACKAGES_PATH);
33 let file = fs::read_to_string(&packages_path);
34
35 let packages;
36 match file {
37 Ok(toml) => {
38 packages = Packages::from_toml(&toml)?;
39 }
40
41 Err(_) => {
42 packages = Packages::default();
43 fs::create_dir_all(Path::new(&packages_path).parent().unwrap())?;
44 let write_result = fs::write(packages_path, packages.to_toml());
45 PkgarBackend::check_write_result(write_result)?;
46 }
47 }
48
49 let packages_dir = install_path.join(PACKAGES_DIR);
50 fs::create_dir_all(&packages_dir)?;
51
52 let mut pkey_files = HashMap::new();
53 for remote in repo_manager.remotes.iter() {
54 pkey_files.insert(
55 remote.key.clone(),
56 PublicKeyFile::open(remote.pubkey.clone())?,
57 );
58 }
59
60 Ok(PkgarBackend {
61 install_path: install_path.to_path_buf(),
62 packages,
63 repo_manager,
64 pkey_files,
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 for remote in self.repo_manager.remotes.iter() {
76 let pubkey = self.pkey_files.get(&remote.key);
77 if let Some(key) = pubkey {
78 let pkg = PackageFile::new(&path, &key.pkey);
79 if let Ok(p) = pkg {
80 return Ok(p);
81 }
82 }
83 }
84 Err(Error::ValidRepoNotFound)
85 }
86
87 fn get_package(&self, package: &PackageName, repokey: &str) -> Result<PackageFile, Error> {
88 Ok(PackageFile::new(
89 format!("{}/{package}.pkgar", DOWNLOAD_PATH),
90 &self
91 .pkey_files
92 .get(repokey)
93 .ok_or(Error::ValidRepoNotFound)?
94 .pkey,
95 )?)
96 }
97
98 fn get_package_toml(&self, package: &PackageName) -> Result<String, Error> {
99 self.repo_manager.sync_toml(package)
100 }
101
102 fn remove_package_head(&mut self, package: &PackageName) -> Result<(), Error> {
103 let path = self
104 .install_path
105 .join(PACKAGES_DIR)
106 .join(format!("{package}.pkgar_head"));
107
108 fs::remove_file(path)?;
109 Ok(())
110 }
111
112 fn create_head(&mut self, package: &PackageName, pubkey_path: &str) -> Result<(), Error> {
113 pkgar::split(
115 pubkey_path,
116 format!("{}/{package}.pkgar", DOWNLOAD_PATH),
117 self.install_path
118 .join(PACKAGES_DIR)
119 .join(format!("{package}.pkgar_head")),
120 Option::<&str>::None,
121 )?;
122
123 Ok(())
124 }
125
126 fn check_write_result(write_result: Result<(), std::io::Error>) -> Result<(), Error> {
127 if let Err(error) = write_result {
128 if error.kind() == std::io::ErrorKind::PermissionDenied {
129 return Err(Error::MissingPermissions);
130 } else {
131 return Err(Error::IO(error));
132 }
133 }
134 Ok(())
135 }
136}
137
138impl Backend for PkgarBackend {
139 fn install(&mut self, package: PackageName) -> Result<(), Error> {
140 let repo = self.repo_manager.sync_pkgar(&package)?;
141 let mut pkg = self.get_package(&package, &repo.key)?;
142 let pubkey_path = repo.pubkey.clone();
143
144 let mut install = Transaction::install(&mut pkg, &self.install_path)?;
145 install.commit()?;
146
147 self.create_head(&package, &pubkey_path)?;
148
149 Ok(())
150 }
151
152 fn uninstall(&mut self, package: PackageName) -> Result<(), Error> {
153 if self.packages.protected.contains(&package) {
154 return Err(Error::ProtectedPackage(package));
155 }
156
157 let mut pkg = self.get_package_head(&package)?;
158 let mut remove = Transaction::remove(&mut pkg, &self.install_path)?;
159 remove.commit()?;
160
161 self.remove_package_head(&package)?;
162
163 Ok(())
164 }
165
166 fn upgrade(&mut self, package: PackageName) -> Result<(), Error> {
167 let mut pkg = self.get_package_head(&package)?;
168
169 let repo = self.repo_manager.sync_pkgar(&package)?;
170 let pubkey_path = repo.pubkey.clone();
171
172 let mut pkg2 = self.get_package(&package, &repo.key)?;
173
174 let mut update = Transaction::replace(&mut pkg, &mut pkg2, &self.install_path)?;
175 update.commit()?;
176
177 self.create_head(&package, &pubkey_path)?;
178
179 Ok(())
180 }
181
182 fn get_package_detail(&self, package: &PackageName) -> Result<Package, Error> {
183 let toml = self.get_package_toml(package)?;
184
185 Ok(Package::from_toml(&toml)?)
186 }
187
188 fn get_repository_detail(&self) -> Result<Repository, Error> {
189 let repo_str = PackageName::new("repo".to_string())?;
190 let toml = self.get_package_toml(&repo_str)?;
191
192 Ok(Repository::from_toml(&toml)?)
193 }
194
195 fn get_installed_packages(&self) -> Result<Vec<PackageName>, Error> {
196 let entries = fs::read_dir(self.install_path.join(PACKAGES_DIR))?;
197
198 let mut packages = vec![];
199
200 for entry in entries {
201 let entry = entry?;
202 let file_name = entry.file_name();
203 let file_name_str = file_name.to_str().ok_or(Error::IO(std::io::Error::new(
204 std::io::ErrorKind::Other,
205 "file name isn't UTF-8",
206 )))?;
207
208 if file_name_str.ends_with(".pkgar_head") {
209 let package = file_name_str.replace(".pkgar_head", "");
210 packages.push(PackageName::new(package)?);
211 }
212 }
213
214 Ok(packages)
215 }
216}
217
218impl Drop for PkgarBackend {
219 fn drop(&mut self) {
220 let packages_path = self.install_path.join(PACKAGES_PATH);
221 let write_result = fs::write(packages_path, self.packages.to_toml());
222 PkgarBackend::check_write_result(write_result).unwrap();
223 }
224}