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