1use std::{cell::RefCell, cmp::Ordering, fs, path::Path, rc::Rc};
4
5use backend::pkgar_backend::PkgarBackend;
6use backend::Backend;
7use net_backend::{DefaultNetBackend, DownloadBackend};
8use package_list::PackageList;
9use repo_manager::RepoManager;
10
11pub use backend::Error;
12pub use callback::Callback;
13pub use package::{Package, PackageInfo, PackageName};
14
15pub mod backend;
16pub mod callback;
17pub mod net_backend;
18pub mod package;
19mod package_list;
20mod repo_manager;
21mod sorensen;
22
23pub struct Library {
24 package_list: PackageList,
25 repo_manager: RepoManager,
26 backend: Box<dyn Backend>,
27}
28
29const DOWNLOAD_PATH: &str = "/tmp/pkg_dowload/";
30
31const PACKAGES_PATH: &str = "etc/pkg/packages.toml";
33
34impl Library {
35 pub fn new<P: AsRef<Path>>(
36 install_path: P,
37 target: &str,
38 callback: Rc<RefCell<dyn Callback>>,
39 ) -> Result<Self, Error> {
40 let install_path = install_path.as_ref();
41
42 let mut remotes = Vec::new();
43 {
44 let repos_path = install_path.join("etc/pkg.d");
45 let mut repo_files = Vec::new();
46 for entry_res in fs::read_dir(&repos_path)? {
47 let entry = entry_res?;
48 let path = entry.path();
49 if path.is_file() {
50 repo_files.push(path);
51 }
52 }
53 repo_files.sort();
54 for repo_file in repo_files {
55 let data = fs::read_to_string(repo_file)?;
56 for line in data.lines() {
57 if !line.starts_with('#') {
58 remotes.push(format!("{}/{target}", line.trim()));
59 }
60 }
61 }
62 }
63
64 let download_backend = DefaultNetBackend::new()?;
65
66 let repo_manager = RepoManager {
67 remotes: remotes.clone(),
68 download_path: DOWNLOAD_PATH.into(),
69 download_backend: Box::new(download_backend.clone()),
70 callback: callback.clone(),
71 };
72
73 let backend = PkgarBackend::new(install_path, repo_manager, callback.clone())?;
74
75 let repo_manager = RepoManager {
77 remotes: remotes.clone(),
78 download_path: DOWNLOAD_PATH.into(),
79 download_backend: Box::new(download_backend),
80 callback: callback.clone(),
81 };
82
83 Ok(Library {
84 repo_manager,
85 package_list: PackageList::default(),
86 backend: Box::new(backend),
87 })
88 }
89
90 pub fn get_installed_packages(&self) -> Result<Vec<PackageName>, Error> {
91 self.backend.get_installed_packages()
92 }
93
94 pub fn install(&mut self, packages: Vec<PackageName>) -> Result<(), Error> {
95 for package_name in packages {
96 self.package_list.install.push(package_name);
97 }
98
99 Ok(())
100 }
101
102 pub fn uninstall(&mut self, packages: Vec<PackageName>) -> Result<(), Error> {
103 for package_name in packages {
104 if self.get_installed_packages()?.contains(&package_name) {
105 self.package_list.uninstall.push(package_name);
106 }
107 }
108
109 Ok(())
110 }
111
112 pub fn update(&mut self, packages: Vec<PackageName>) -> Result<(), Error> {
114 if packages.is_empty() {
115 for package_name in &self.backend.get_installed_packages()? {
116 self.package_list.install.push(package_name.clone());
117 }
118 } else {
119 for package_name in packages {
120 if self.get_installed_packages()?.contains(&package_name) {
121 self.package_list.uninstall.push(package_name);
122 }
123 }
124 }
125
126 Ok(())
127 }
128
129 pub fn get_all_package_names(&mut self) -> Result<Vec<PackageName>, Error> {
130 let mut website = self.repo_manager.sync_website();
133
134 let mut names = vec![];
135 while let Some(end) = website.find(".toml</a>") {
136 let mut i = end;
137 loop {
138 let char = website.chars().nth(i).expect("this should work");
139 if char == '>' {
140 break;
141 }
142 i -= 1;
143 }
144 let package_name = PackageName::new(&website[i + 1..end])?;
145 if !names.contains(&package_name) {
146 names.push(package_name);
147 }
148
149 website = website.replacen(".toml</a>", "", 1);
150 }
151
152 Ok(names)
153 }
154
155 pub fn search(&mut self, package: &str) -> Result<Vec<(PackageName, f64)>, Error> {
156 let names = self.get_all_package_names()?;
157
158 let mut result = vec![];
159
160 for name in names {
161 let mut rank = 0.0;
162
163 let dst = sorensen::distance(
164 package.to_lowercase().as_bytes(),
165 name.as_str().to_lowercase().as_bytes(),
166 );
167
168 if dst >= 0.2 {
169 rank += dst;
170 }
171
172 if name.as_str().contains(package) {
173 rank += 0.01;
174 }
175
176 if rank > 0.0 {
177 result.push((name, rank));
178 }
179 }
180
181 result.as_mut_slice().sort_by(|a, b| {
183 let check1 = b.1.partial_cmp(&a.1);
184 if check1 == Some(Ordering::Equal) {
185 a.0.cmp(&b.0)
186 } else {
187 check1.unwrap_or(Ordering::Equal)
188 }
189 });
190
191 Ok(result)
192 }
193
194 pub fn apply(&mut self) -> Result<(), Error> {
195 for package in self.package_list.uninstall.iter() {
196 self.backend.uninstall(package.clone())?;
197 }
198
199 let install = self.with_dependecies(&self.package_list.install.clone())?;
200
201 for package in install.into_iter() {
202 if self.backend.get_installed_packages()?.contains(&package) {
203 self.backend.upgrade(package)?;
204 } else {
205 self.backend.install(package)?;
206 }
207 }
208
209 self.package_list = Default::default();
210 Ok(())
211 }
212
213 fn get_package(&mut self, package_name: &PackageName) -> Result<Package, Error> {
215 let toml = self.repo_manager.sync_toml(package_name)?;
216
217 Ok(Package::from_toml(&toml)?)
218 }
219
220 pub fn with_dependecies(
221 &mut self,
222 packages: &Vec<PackageName>,
223 ) -> Result<Vec<PackageName>, Error> {
224 let mut list = vec![];
225 for package in packages {
226 self.get_dependecies_recursive(package, &mut list)?;
227 }
228
229 Ok(list)
230 }
231
232 fn get_dependecies_recursive(
233 &mut self,
234 package_name: &PackageName,
235 list: &mut Vec<PackageName>,
236 ) -> Result<(), Error> {
237 let package = self.get_package(package_name)?;
238 for dep in &package.depends {
239 let package = self.get_package(dep)?;
240
241 if list.contains(&package.name) {
242 continue;
243 }
244
245 list.push(package.name.clone());
246 self.get_dependecies_recursive(package_name, list)?;
247 }
248 list.push(package.name);
249 Ok(())
250 }
251
252 pub fn info(&mut self, package: PackageName) -> Result<PackageInfo, Error> {
253 let installed = self.backend.get_installed_packages()?.contains(&package);
254 let package = self.get_package(&package)?;
255
256 Ok(PackageInfo {
257 installed,
258 version: package.version,
259 target: package.target,
260 download_size: "not implemented".to_string(),
262 depends: package.depends,
263 })
264 }
265}