1use std::{
2 cell::RefCell,
3 cmp::Ordering,
4 fs,
5 path::{Path, PathBuf},
6 rc::Rc,
7};
8
9use crate::backend::pkgar_backend::PkgarBackend;
10use crate::backend::{Backend, Error};
11use crate::net_backend::{DefaultNetBackend, DownloadBackend};
12use crate::package_list::PackageList;
13use crate::repo_manager::RepoManager;
14
15use crate::callback::Callback;
16use crate::package::{PackageInfo, PackageName};
17
18use crate::{sorensen, DOWNLOAD_PATH};
19
20pub struct Library {
21 package_list: PackageList,
22 backend: Box<dyn Backend>,
23}
24
25impl Library {
26 pub fn new<P: AsRef<Path>>(
27 install_path: P,
28 target: &str,
29 callback: Rc<RefCell<dyn Callback>>,
30 ) -> Result<Self, Error> {
31 let install_path = install_path.as_ref();
32
33 let download_backend = DefaultNetBackend::new()?;
34 let prefer_cache = PathBuf::from(DOWNLOAD_PATH).join("prefer_cache").exists();
35
36 let mut repo_manager = RepoManager {
37 remotes: Vec::new(),
38 download_path: DOWNLOAD_PATH.into(),
39 download_backend: Box::new(download_backend.clone()),
40 callback: callback.clone(),
41 prefer_cache,
42 };
43
44 {
45 let repos_path = install_path.join("etc/pkg.d");
46 let mut repo_files = Vec::new();
47 for entry_res in fs::read_dir(&repos_path)? {
48 let entry = entry_res?;
49 let path = entry.path();
50 if path.is_file() {
51 repo_files.push(path);
52 }
53 }
54 repo_files.sort();
55 for repo_file in repo_files {
56 let data = fs::read_to_string(repo_file)?;
57 for line in data.lines() {
58 if !line.starts_with('#') {
59 repo_manager.add_remote(line.trim(), target)?;
60 }
61 }
62 }
63
64 let local_pub_path = install_path.join("pkg");
66 let _ = repo_manager.add_local("installer_key", &local_pub_path);
67 }
68
69 let backend = PkgarBackend::new(install_path, repo_manager)?;
70
71 Ok(Library {
72 package_list: PackageList::default(),
73 backend: Box::new(backend),
74 })
75 }
76
77 pub fn get_installed_packages(&self) -> Result<Vec<PackageName>, Error> {
78 self.backend.get_installed_packages()
79 }
80
81 pub fn install(&mut self, packages: Vec<PackageName>) -> Result<(), Error> {
82 let installed_packages = self.get_installed_packages().unwrap_or(vec![]);
83 for package_name in packages {
84 if !installed_packages.contains(&package_name) {
85 self.package_list.install.push(package_name);
86 }
87 }
88
89 Ok(())
90 }
91
92 pub fn uninstall(&mut self, packages: Vec<PackageName>) -> Result<(), Error> {
94 let installed_packages = self.get_installed_packages()?;
95 for package_name in packages {
96 if installed_packages.contains(&package_name) {
97 self.package_list.uninstall.push(package_name);
98 }
99 }
100
101 Ok(())
102 }
103
104 pub fn update(&mut self, packages: Vec<PackageName>) -> Result<(), Error> {
106 let installed_packages = self.get_installed_packages()?;
107 if packages.is_empty() {
108 for package_name in &installed_packages {
109 self.package_list.install.push(package_name.clone());
110 }
111 } else {
112 for package_name in packages {
113 if installed_packages.contains(&package_name) {
114 self.package_list.install.push(package_name);
115 }
116 }
117 }
118
119 Ok(())
120 }
121
122 pub fn get_all_package_names(&mut self) -> Result<Vec<PackageName>, Error> {
123 let repository = self.backend.get_repository_detail()?;
124 let list = repository
125 .packages
126 .keys()
127 .cloned()
128 .fold(Vec::new(), |mut acc, x| {
129 match PackageName::new(x) {
130 Ok(name) => {
131 acc.push(name);
132 }
133 Err(_) => {}
134 };
135 acc
136 });
137 Ok(list)
138 }
139
140 pub fn search(&mut self, package: &str) -> Result<Vec<(PackageName, f64)>, Error> {
141 let names = self.get_all_package_names()?;
142
143 let mut result = vec![];
144
145 for name in names {
146 let mut rank = 0.0;
147
148 let dst = sorensen::distance(
149 package.to_lowercase().as_bytes(),
150 name.as_str().to_lowercase().as_bytes(),
151 );
152
153 if dst >= 0.2 {
154 rank += dst;
155 }
156
157 if name.as_str().contains(package) {
158 rank += 0.01;
159 }
160
161 if rank > 0.0 {
162 result.push((name, rank));
163 }
164 }
165
166 result.as_mut_slice().sort_by(|a, b| {
168 let check1 = b.1.partial_cmp(&a.1);
169 if check1 == Some(Ordering::Equal) {
170 a.0.cmp(&b.0)
171 } else {
172 check1.unwrap_or(Ordering::Equal)
173 }
174 });
175
176 Ok(result)
177 }
178
179 pub fn apply(&mut self) -> Result<(), Error> {
180 for package in self.package_list.uninstall.iter() {
181 let r = self.backend.uninstall(package.clone());
183 if let Err(Error::RepoCacheNotFound(e)) = &r {
184 eprintln!("Repository source of {e} is not valid, please reinstall repository public keys to allow erasing, or reinstall the package.");
185 }
186 r?
187 }
188
189 let install = self.with_dependecies(&self.package_list.install.clone())?;
190
191 for package in install.into_iter() {
192 if self.backend.get_installed_packages()?.contains(&package) {
193 match self.backend.upgrade(package.clone()) {
194 Err(Error::RepoCacheNotFound(e)) => {
195 eprintln!("Repository source of {e} is not valid, reinstalling!");
196 self.backend.install(package)?;
197 }
198 r => r?,
199 }
200 } else {
201 self.backend.install(package)?;
202 }
203 }
204
205 self.package_list = Default::default();
206 Ok(())
207 }
208
209 pub fn with_dependecies(
210 &mut self,
211 packages: &Vec<PackageName>,
212 ) -> Result<Vec<PackageName>, Error> {
213 let mut list = vec![];
214 for package in packages {
215 self.get_dependecies_recursive(package, &mut list)?;
216 }
217
218 Ok(list)
219 }
220
221 fn get_dependecies_recursive(
222 &mut self,
223 package_name: &PackageName,
224 list: &mut Vec<PackageName>,
225 ) -> Result<(), Error> {
226 let package = self.backend.get_package_detail(package_name)?;
227 if list.contains(&package.name) {
228 return Ok(());
229 }
230 for dep in &package.depends {
231 self.get_dependecies_recursive(dep, list)?;
232 }
233
234 if package.version != "" {
238 list.push(package.name);
239 }
240 Ok(())
241 }
242
243 pub fn info(&mut self, package: PackageName) -> Result<PackageInfo, Error> {
244 let installed = self.backend.get_installed_packages()?.contains(&package);
245 let package = self.backend.get_package_detail(&package)?;
246
247 Ok(PackageInfo { installed, package })
248 }
249}