pkg/backend/pkgar_backend/
mod.rs

1use std::{
2    cell::RefCell,
3    fs,
4    path::{Path, PathBuf},
5    rc::Rc,
6};
7
8use pkgar::{PackageFile, Transaction};
9use pkgar_keys::PublicKeyFile;
10
11use self::packages::Packages;
12use super::{Backend, Callback, Error};
13use crate::{repo_manager::RepoManager, PackageName, DOWNLOAD_PATH, PACKAGES_PATH};
14
15mod packages;
16
17pub struct PkgarBackend {
18    install_path: PathBuf,
19    packages: Packages,
20    repo_manager: RepoManager,
21    pkey_file: PublicKeyFile,
22}
23
24const PACKAGES_DIR: &str = "pkg/packages";
25
26impl PkgarBackend {
27    pub fn new<P: AsRef<Path>>(
28        install_path: P,
29        repo_manager: RepoManager,
30        callback: Rc<RefCell<dyn Callback>>,
31    ) -> Result<Self, Error> {
32        let install_path = install_path.as_ref();
33
34        let packages_path = install_path.join(PACKAGES_PATH);
35        let file = fs::read_to_string(&packages_path);
36
37        let packages;
38        match file {
39            Ok(toml) => {
40                packages = Packages::from_toml(&toml)?;
41            }
42
43            Err(_) => {
44                packages = Packages::default();
45                fs::create_dir_all(Path::new(&packages_path).parent().unwrap())?;
46                fs::write(packages_path, packages.to_toml())?;
47            }
48        }
49
50        let packages_dir = install_path.join(PACKAGES_DIR);
51        fs::create_dir_all(&packages_dir)?;
52
53        fs::create_dir_all("/tmp/pkg/")?;
54        repo_manager.download_backend.download(
55            "https://static.redox-os.org/pkg/id_ed25519.pub.toml",
56            Path::new("/tmp/pkg/pub_key.toml"),
57            callback,
58        )?;
59
60        Ok(PkgarBackend {
61            install_path: install_path.to_path_buf(),
62            packages,
63            repo_manager,
64            pkey_file: PublicKeyFile::open("/tmp/pkg/pub_key.toml")?,
65        })
66    }
67
68    fn get_package_head(&mut self, package: &PackageName) -> Result<PackageFile, Error> {
69        let path = self
70            .install_path
71            .join(PACKAGES_DIR)
72            .join(format!("{package}.pkgar_head"));
73
74        Ok(PackageFile::new(path, &self.pkey_file.pkey)?)
75    }
76
77    fn get_package(&mut self, package: &PackageName) -> Result<PackageFile, Error> {
78        Ok(PackageFile::new(
79            format!("{}/{package}.pkgar", DOWNLOAD_PATH),
80            &self.pkey_file.pkey,
81        )?)
82    }
83
84    fn remove_package_head(&mut self, package: &PackageName) -> Result<(), Error> {
85        let path = self
86            .install_path
87            .join(PACKAGES_DIR)
88            .join(format!("{package}.pkgar_head"));
89
90        fs::remove_file(path)?;
91        Ok(())
92    }
93
94    fn create_head(&mut self, package: &PackageName) -> Result<(), Error> {
95        // creates a head file
96        pkgar::split(
97            "/tmp/pkg/pub_key.toml",
98            format!("{}/{package}.pkgar", DOWNLOAD_PATH),
99            self.install_path
100                .join(PACKAGES_DIR)
101                .join(format!("{package}.pkgar_head")),
102            Option::<&str>::None,
103        )?;
104
105        Ok(())
106    }
107}
108
109impl Backend for PkgarBackend {
110    fn install(&mut self, package: PackageName) -> Result<(), Error> {
111        self.repo_manager.sync_pkgar(&package)?;
112
113        let mut pkg = self.get_package(&package)?;
114
115        let mut install = Transaction::install(&mut pkg, &self.install_path)?;
116        install.commit()?;
117
118        self.create_head(&package)?;
119
120        Ok(())
121    }
122
123    fn uninstall(&mut self, package: PackageName) -> Result<(), Error> {
124        if self.packages.protected.contains(&package) {
125            return Err(Error::ProtectedPackage(package));
126        }
127
128        let mut pkg = self.get_package_head(&package)?;
129        let mut remove = Transaction::remove(&mut pkg, &self.install_path)?;
130        remove.commit()?;
131
132        self.remove_package_head(&package)?;
133
134        Ok(())
135    }
136
137    fn upgrade(&mut self, package: PackageName) -> Result<(), Error> {
138        let mut pkg = self.get_package_head(&package)?;
139
140        self.repo_manager.sync_pkgar(&package)?;
141        let mut pkg2 = self.get_package(&package)?;
142
143        let mut update = Transaction::replace(&mut pkg, &mut pkg2, &self.install_path)?;
144        update.commit()?;
145
146        self.create_head(&package)?;
147
148        Ok(())
149    }
150
151    fn get_installed_packages(&self) -> Result<Vec<PackageName>, Error> {
152        let entries = fs::read_dir(self.install_path.join(PACKAGES_DIR))?;
153
154        let mut packages = vec![];
155
156        for entry in entries {
157            let entry = entry?;
158            let file_name = entry.file_name();
159            let file_name_str = file_name.to_str().ok_or(Error::IO(std::io::Error::new(
160                std::io::ErrorKind::Other,
161                "file name isn't UTF-8",
162            )))?;
163
164            if file_name_str.ends_with(".pkgar_head") {
165                let package = file_name_str.replace(".pkgar_head", "");
166                packages.push(PackageName::new(package)?);
167            }
168        }
169
170        Ok(packages)
171    }
172}
173
174impl Drop for PkgarBackend {
175    fn drop(&mut self) {
176        let packages_path = self.install_path.join(PACKAGES_PATH);
177        fs::write(packages_path, self.packages.to_toml()).unwrap();
178    }
179}