pkg/backend/pkgar_backend/
mod.rs1use std::{
2 cell::RefCell,
3 fs,
4 path::{Path, PathBuf},
5 rc::Rc,
6};
7
8use pkgar::{MergedTransaction, PackageFile, Transaction};
9use pkgar_core::PublicKey;
10use pkgar_keys::PublicKeyFile;
11
12use super::{Backend, Error};
13use crate::{
14 callback::Callback,
15 package::{RemotePackage, Repository},
16 package_state::PackageState,
17 repo_manager::RepoManager,
18 Package, PackageName,
19};
20
21pub struct PkgarBackend {
23 install_path: PathBuf,
25 packages: PackageState,
27 repo_manager: RepoManager,
29 commits: Option<MergedTransaction>,
31 keys_synced: bool,
32 callback: Rc<RefCell<dyn Callback>>,
33}
34
35impl PkgarBackend {
36 pub fn new<P: AsRef<Path>>(install_path: P, repo_manager: RepoManager) -> Result<Self, Error> {
37 let install_path = install_path.as_ref();
38
39 let packages_path = install_path.join(crate::PACKAGES_TOML_PATH);
40 let packages_dir = install_path.join(crate::PACKAGES_HEAD_DIR);
41 let file = fs::read_to_string(&packages_path);
42
43 let packages;
44 match file {
45 Ok(toml) => {
46 packages = PackageState::from_toml(&toml)?;
47 }
48 Err(_) => {
49 packages = PackageState::default();
50 fs::create_dir_all(Path::new(&packages_path).parent().unwrap())?;
51 }
52 }
53
54 fs::write(&packages_path, packages.to_toml())?;
56
57 fs::create_dir_all(&packages_dir)?;
58
59 let callback = repo_manager.callback.clone();
60
61 Ok(PkgarBackend {
62 install_path: install_path.to_path_buf(),
63 packages,
64 repo_manager,
65 commits: Some(MergedTransaction::new()),
67 keys_synced: false,
68 callback,
69 })
70 }
71
72 fn add_transaction(&mut self, transaction: Transaction, src: Option<&PackageFile>) {
73 let mut commits = self
74 .commits
75 .take()
76 .unwrap_or_else(|| MergedTransaction::new());
77 commits.merge(transaction, src);
78 self.commits = Some(commits);
79 }
80
81 fn get_package_head(&self, package: &PackageName) -> Result<PackageFile, Error> {
83 let path = self
84 .install_path
85 .join(crate::PACKAGES_HEAD_DIR)
86 .join(format!("{package}.pkgar_head"));
87
88 let Some(pkg) = self.packages.installed.get(package) else {
89 return Err(Error::PackageNotInstalled(package.clone()));
90 };
91 let Some(remote) = self.packages.pubkeys.get(&pkg.remote) else {
92 return Err(Error::RepoCacheNotFound(package.clone()));
93 };
94
95 let pkg = PackageFile::new(&path, &remote.pkey).map_err(Error::from)?;
96
97 Ok(pkg)
98 }
99
100 fn remove_package_head(&mut self, package: &PackageName) -> Result<(), Error> {
101 let path = self
102 .install_path
103 .join(crate::PACKAGES_HEAD_DIR)
104 .join(format!("{package}.pkgar_head"));
105
106 fs::remove_file(path)?;
107 Ok(())
108 }
109
110 fn create_head(
111 &self,
112 archive_path: &Path,
113 package: &PackageName,
114 pubkey: &PublicKey,
115 ) -> Result<(), Error> {
116 let head_path = self
118 .install_path
119 .join(crate::PACKAGES_HEAD_DIR)
120 .join(format!("{package}.pkgar_head"));
121
122 let mut package = PackageFile::new(archive_path, &pubkey)?;
123 package.split(&head_path, None)?;
124
125 Ok(())
126 }
127
128 fn sync_keys(&mut self) -> Result<(), Error> {
129 if self.keys_synced {
130 return Ok(());
131 }
132
133 for (name, map) in &mut self.repo_manager.remote_map {
134 if map.pubkey.is_none() {
135 if let Some(pubk) = self.packages.pubkeys.get(name) {
136 map.pubkey = Some(pubk.pkey)
137 }
138 }
139 }
140
141 self.repo_manager.sync_keys()?;
142
143 self.keys_synced = true;
144 Ok(())
145 }
146}
147
148impl Backend for PkgarBackend {
149 fn install(&mut self, package: RemotePackage) -> Result<(), Error> {
150 self.sync_keys()?;
151 if package.package.version.is_empty() {
152 return Ok(()); }
154 let (local_path, repo) = self
156 .repo_manager
157 .get_package_pkgar(&package.package.name, package.package.network_size)?;
158 let mut pkg = PackageFile::new(&local_path, &repo.pubkey.unwrap())?;
159 self.callback.borrow_mut().install_extract(&package);
160 let install = Transaction::install(&mut pkg, &self.install_path)?;
161 self.create_head(&local_path, &package.package.name, &repo.pubkey.unwrap())?;
162 self.add_transaction(install, Some(&pkg));
163 Ok(())
164 }
165
166 fn uninstall(&mut self, package: PackageName) -> Result<(), Error> {
167 if self.packages.protected.contains(&package) {
168 return Err(Error::ProtectedPackage(package));
169 }
170 self.sync_keys()?;
171
172 let mut pkg = self.get_package_head(&package)?;
173 let remove = Transaction::remove(&mut pkg, &self.install_path)?;
174 self.add_transaction(remove, Some(&pkg));
175
176 self.remove_package_head(&package)?;
177
178 Ok(())
179 }
180
181 fn upgrade(&mut self, package: &RemotePackage) -> Result<(), Error> {
182 self.sync_keys()?;
183
184 let name = &package.package.name;
185 let mut pkg = self.get_package_head(name)?;
186 let (local_path, repo) = self
187 .repo_manager
188 .get_package_pkgar(name, package.package.network_size)?;
189 let mut pkg2 = PackageFile::new(&local_path, &repo.pubkey.unwrap())?;
190 let update = Transaction::replace(&mut pkg, &mut pkg2, &self.install_path)?;
191 self.create_head(&local_path, &name, &repo.pubkey.unwrap())?;
192 self.add_transaction(update, Some(&pkg));
193 Ok(())
194 }
195
196 fn get_package_detail(&self, package: &PackageName) -> Result<RemotePackage, Error> {
197 let (toml, remote) = self.repo_manager.get_package_toml(package)?;
198
199 Ok(RemotePackage {
200 package: Package::from_toml(&toml)?,
201 remote,
202 })
203 }
204
205 fn get_repository_detail(&self) -> Result<Repository, Error> {
207 let repo_str = PackageName::new("repo".to_string())?;
208 let (toml, _) = self.repo_manager.get_package_toml(&repo_str)?;
209
210 Ok(Repository::from_toml(&toml)?)
211 }
212
213 fn get_package_state(&self) -> PackageState {
214 self.packages.clone()
215 }
216
217 fn commit_check_conflict(&self) -> Result<&Vec<pkgar::TransactionConflict>, Error> {
218 let transaction = self
219 .commits
220 .as_ref()
221 .ok_or_else(|| Error::Pkgar(Box::new(pkgar::Error::DataNotInitialized)))?;
222 Ok(transaction.get_possible_conflicts())
223 }
224
225 fn commit_state(&mut self, new_state: PackageState) -> Result<usize, Error> {
226 let mut transaction = self
227 .commits
228 .take()
229 .ok_or_else(|| Error::Pkgar(Box::new(pkgar::Error::DataNotInitialized)))?
230 .into_transaction();
231 self.callback
232 .borrow_mut()
233 .commit_start(transaction.pending_commit());
234 while transaction.pending_commit() > 0 {
235 self.callback.borrow_mut().commit_increment(&transaction);
236 if let Err(e) = transaction.commit_one() {
237 self.add_transaction(transaction, None);
238 return Err(Error::from(e));
239 }
240 }
241 self.callback.borrow_mut().commit_end();
242
243 self.packages = new_state;
244 let packages_path = self.install_path.join(crate::PACKAGES_TOML_PATH);
245 for (k, v) in &self.repo_manager.remote_map {
246 let Some(pubkey) = v.pubkey else {
247 return Err(Error::RepoNotLoaded(k.to_string()));
248 };
249 let pk = PublicKeyFile::new(pubkey);
250 self.packages.pubkeys.insert(k.to_string(), pk);
251 }
252 fs::write(&packages_path, self.packages.to_toml())?;
253 Ok(transaction.total_committed())
254 }
255
256 fn abort_state(&mut self) -> Result<usize, Error> {
257 let mut transaction = self
258 .commits
259 .take()
260 .ok_or_else(|| Error::Pkgar(Box::new(pkgar::Error::DataNotInitialized)))?
261 .into_transaction();
262 self.callback
263 .borrow_mut()
264 .abort_start(transaction.pending_commit());
265 while transaction.pending_commit() > 0 {
266 self.callback.borrow_mut().commit_increment(&transaction);
267 if let Err(e) = transaction.abort_one() {
268 self.add_transaction(transaction, None);
269 return Err(Error::from(e));
270 }
271 }
272 self.callback.borrow_mut().abort_end();
273 Ok(transaction.total_committed())
274 }
275}