1use std::collections::{btree_map, BTreeMap};
2use std::{cell::RefCell, cmp::Ordering, path::Path, rc::Rc};
3
4use crate::backend::pkgar_backend::PkgarBackend;
5use crate::backend::{Backend, Error};
6use crate::net_backend::{DefaultNetBackend, DownloadBackend};
7use crate::repo_manager::RepoManager;
8
9use crate::callback::Callback;
10use crate::package::{PackageInfo, PackageName, RemotePackage};
11
12use crate::{sorensen, PackageState};
13
14pub struct Library {
15 package_state: PackageState,
17 cached_info: BTreeMap<PackageName, RemotePackage>,
18 backend: Box<dyn Backend>,
19 callback: Rc<RefCell<dyn Callback>>,
20}
21
22impl Library {
23 pub fn new<P: AsRef<Path>>(
25 install_path: P,
26 target: &str,
27 callback: Rc<RefCell<dyn Callback>>,
28 ) -> Result<Self, Error> {
29 let install_path = install_path.as_ref();
30
31 let download_backend = DefaultNetBackend::new()?;
32
33 let mut repo_manager = RepoManager::new(callback.clone(), Box::new(download_backend));
34 repo_manager.update_remotes(target, install_path)?;
35
36 let backend = PkgarBackend::new(install_path, repo_manager)?;
37
38 Ok(Library {
39 package_state: backend.get_package_state(),
40 backend: Box::new(backend),
41 cached_info: BTreeMap::new(),
42 callback: callback,
43 })
44 }
45
46 pub fn new_local<P: AsRef<Path>>(
48 source_dir: P,
49 pubkey_dir: P,
50 install_path: P,
51 target: &str,
52 callback: Rc<RefCell<dyn Callback>>,
53 ) -> Result<Self, Error> {
54 let install_path = install_path.as_ref();
55
56 let download_backend = DefaultNetBackend::new()?;
57
58 let mut repo_manager = RepoManager::new(callback.clone(), Box::new(download_backend));
59
60 repo_manager.add_local(
61 "local",
62 &source_dir.as_ref().to_string_lossy(),
63 target,
64 pubkey_dir.as_ref(),
65 )?;
66
67 let backend = PkgarBackend::new(install_path, repo_manager)?;
68
69 Ok(Library {
70 package_state: backend.get_package_state(),
71 backend: Box::new(backend),
72 cached_info: BTreeMap::new(),
73 callback: callback,
74 })
75 }
76
77 pub fn new_remote<P: AsRef<Path>>(
79 remote_urls: &Vec<&str>,
80 install_path: P,
81 target: &str,
82 callback: Rc<RefCell<dyn Callback>>,
83 ) -> Result<Self, Error> {
84 let install_path = install_path.as_ref();
85
86 let download_backend = DefaultNetBackend::new()?;
87
88 let mut repo_manager = RepoManager::new(callback.clone(), Box::new(download_backend));
89
90 for remote_url in remote_urls {
91 repo_manager.add_remote(remote_url.trim(), target)?;
92 }
93
94 let backend = PkgarBackend::new(install_path, repo_manager)?;
95
96 Ok(Library {
97 package_state: backend.get_package_state(),
98 backend: Box::new(backend),
99 cached_info: BTreeMap::new(),
100 callback: callback,
101 })
102 }
103
104 pub fn get_installed_packages(&self) -> Result<Vec<PackageName>, Error> {
105 Ok(self.package_state.get_installed_list())
106 }
107
108 pub fn install(&mut self, packages: Vec<PackageName>) -> Result<(), Error> {
109 self.callback.borrow_mut().fetch_start(packages.len());
110 self.install_inner(packages.clone(), 100)?;
111 self.package_state.mark_as_manual(true, &packages);
112 self.callback.borrow_mut().fetch_end();
113 Ok(())
114 }
115
116 fn install_inner(&mut self, packages: Vec<PackageName>, iter: u32) -> Result<(), Error> {
117 if iter == 0 {
118 return Err(Error::RepoRecursion(packages));
119 }
120 let mut pinfos = Vec::new();
121 for p in &packages {
122 let premote = match self.cached_info.entry(p.clone()) {
123 btree_map::Entry::Occupied(occupied_entry) => occupied_entry.get().clone(),
124 btree_map::Entry::Vacant(vacant_entry) => {
125 let p = self.backend.get_package_detail(p)?;
126 vacant_entry.insert(p).clone()
127 }
128 };
129 self.callback.borrow_mut().fetch_package_increment(1, 0);
130 pinfos.push(premote);
131 }
132 let remainder = self.package_state.install(&pinfos);
133 if remainder.len() > 0 {
134 self.callback
135 .borrow_mut()
136 .fetch_package_increment(0, remainder.len());
137 self.install_inner(remainder, iter - 1)?;
138 }
139 Ok(())
140 }
141
142 pub fn uninstall(&mut self, packages: Vec<PackageName>) -> Result<(), Error> {
143 self.uninstall_inner(packages, 100)
144 }
145
146 fn uninstall_inner(&mut self, packages: Vec<PackageName>, iter: u32) -> Result<(), Error> {
147 if iter == 0 {
148 return Err(Error::RepoRecursion(packages));
149 }
150 let remainder = self.package_state.uninstall(&packages);
151 if remainder.len() > 0 {
152 self.uninstall_inner(remainder, iter - 1)?;
153 }
154 Ok(())
155 }
156
157 pub fn update(&mut self, mut packages: Vec<PackageName>) -> Result<(), Error> {
159 let repo_list = self.backend.get_repository_detail()?;
160 let local_list = self.backend.get_package_state();
161 if packages.len() == 0 {
162 packages = local_list.get_installed_list();
163 }
164
165 let mut new_packages = Vec::new();
166 for package in packages {
167 if let Some(source_hash) = repo_list.packages.get(package.as_str()) {
168 if let Some(local_hash) = local_list.installed.get(package.as_str()) {
169 if local_hash.blake3 != *source_hash {
170 new_packages.push(package);
171 }
172 }
173 }
174 }
175
176 self.install(new_packages)
177 }
178
179 pub fn get_all_package_names(&mut self) -> Result<Vec<PackageName>, Error> {
180 let repository = self.backend.get_repository_detail()?;
181 let list = repository
182 .packages
183 .keys()
184 .cloned()
185 .fold(Vec::new(), |mut acc, x| {
186 match PackageName::new(x) {
187 Ok(name) => {
188 acc.push(name);
189 }
190 Err(_) => {}
191 };
192 acc
193 });
194 Ok(list)
195 }
196
197 pub fn search(&mut self, package: &str) -> Result<Vec<(PackageName, f64)>, Error> {
198 let names = self.get_all_package_names()?;
199
200 let mut result = vec![];
201
202 for name in names {
203 let mut rank = 0.0;
204
205 let dst = sorensen::distance(
206 package.to_lowercase().as_bytes(),
207 name.as_str().to_lowercase().as_bytes(),
208 );
209
210 if dst >= 0.2 {
211 rank += dst;
212 }
213
214 if name.as_str().contains(package) {
215 rank += 0.01;
216 }
217
218 if rank > 0.0 {
219 result.push((name, rank));
220 }
221 }
222
223 result.as_mut_slice().sort_by(|a, b| {
225 let check1 = b.1.partial_cmp(&a.1);
226 if check1 == Some(Ordering::Equal) {
227 a.0.cmp(&b.0)
228 } else {
229 check1.unwrap_or(Ordering::Equal)
230 }
231 });
232
233 Ok(result)
234 }
235
236 pub fn abort(&mut self) -> Result<usize, Error> {
237 self.backend.abort_state()
238 }
239
240 pub fn apply(&mut self) -> Result<usize, Error> {
241 self.apply_inner()
242 }
243
244 fn apply_inner(&mut self) -> Result<usize, Error> {
245 let diff = self.backend.get_package_state().diff(&self.package_state);
246 if diff.is_empty() {
247 return Ok(0);
248 }
249
250 self.callback.borrow_mut().install_prompt(&diff)?;
251
252 for package in &diff.uninstall {
253 let r = self.backend.uninstall(package.clone());
255 if let Err(Error::RepoCacheNotFound(e)) = &r {
256 eprintln!("Repository source of {e} is not valid, please reinstall repository public keys to allow erasing, or reinstall the package.");
257 }
258 r?
259 }
260
261 for package in &diff.update {
262 if let Some(cache) = self.cached_info.remove(package) {
263 let r = self.backend.upgrade(&cache);
264 if let Err(Error::RepoCacheNotFound(e)) = &r {
265 eprintln!("Repository source of {e} is not valid, reinstalling!");
266 self.backend.install(cache)?;
267 }
268 r?
269 }
270 }
271
272 for package in &diff.install {
273 if let Some(cache) = self.cached_info.remove(package) {
274 self.backend.install(cache)?;
275 }
276 }
277
278 self.callback
279 .borrow_mut()
280 .install_check_conflict(self.backend.commit_check_conflict()?)?;
281
282 self.backend.commit_state(self.package_state.clone())
283 }
284
285 pub fn info(&mut self, package: PackageName) -> Result<PackageInfo, Error> {
286 let installed = self.package_state.get_installed_list().contains(&package);
287 let package = self.backend.get_package_detail(&package)?;
288
289 Ok(PackageInfo { installed, package })
290 }
291}