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