hpk/src/db/mod.rs

183 lines
6.2 KiB
Rust

use {
crate::{Arch, Package, Repository, Version},
hpk_package::ron::{self, ser::PrettyConfig},
rayon::prelude::*,
serde::{Deserialize, Serialize},
std::{
collections::HashMap,
error::Error,
fmt,
fs::{self, File},
path::PathBuf,
sync::Mutex,
},
url::Url,
zstd::{Decoder, Encoder},
};
#[derive(Clone, Debug)]
/// Represents an updated package to be installed on the system
pub struct Update {
pub name: String,
pub version: Version,
pub arch: Arch,
pub release: u8,
pub url: Url,
}
impl fmt::Display for Update {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}-{}_{}_{}",
self.name, self.version, self.arch, self.release
)
}
}
impl Update {
pub fn full_url(&self) -> Result<Url, url::ParseError> {
self.url.join(&format!("{self}.tar.zstd"))
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
/// A struct representing all locally installed packages and all packages which
/// are available in a remote repository
pub struct Database {
/// All locally installed packages
pub packages: HashMap<String, Package>,
/// All currently configured repositories
pub available: HashMap<String, Repository>,
}
impl Database {
pub fn update(&mut self) -> Result<(), Box<dyn Error>> {
unimplemented!();
}
pub fn add_package(&mut self, package: Package) {
let name = package.name.clone();
self.packages.insert(name, package);
}
pub fn remove_package(&mut self, name: &str) -> Option<Package> {
self.packages.remove(name)
}
pub fn get_upgradable(&mut self) -> Result<HashMap<String, Update>, Box<dyn Error>> {
self.update()?;
let updates = Mutex::new(HashMap::<String, Update>::new());
self.packages.par_iter().for_each(|(key, local_package)| {
for repo in self.available.values() {
// Check if the remote has a package by this name
if let Some(remote_package) = repo.packages.get(key) {
// Check if the remote package is an update
if remote_package.is_upgrade(local_package) {
if let Ok(mut updates) = updates.lock() {
// Check if we've already pulled in an update from another repo,
// and if so compare versions
if let Some(other_update) = updates.get(key) {
if remote_package.version > other_update.version
|| (remote_package.version == other_update.version
&& remote_package.release > other_update.release)
{
// The remote version is an update to the already
// pulled in update, so swap it out
let update = Update {
name: key.to_string(),
version: remote_package.version.clone(),
arch: remote_package.arch,
release: remote_package.release,
url: repo.base_url.clone(),
};
updates.insert(key.to_string(), update);
}
} else {
// First time we've seen this update, so insert it
// into our hashmap
let update = Update {
name: key.to_string(),
arch: remote_package.arch,
version: remote_package.version.clone(),
release: remote_package.release,
url: repo.base_url.clone(),
};
updates.insert(key.to_string(), update);
}
}
}
}
}
});
let updates = updates.into_inner()?;
Ok(updates)
}
pub fn from_file(prefix: Option<PathBuf>) -> Result<Self, Box<dyn Error>> {
let db = crate::get_db(prefix);
if let Some(dir) = db.parent() {
if !dir.exists() {
fs::create_dir_all(dir)?;
}
}
let fd = File::open(db)?;
let reader = Decoder::new(fd)?;
let res = ron::de::from_reader(reader)?;
Ok(res)
}
pub fn save_to_file(&self, prefix: Option<PathBuf>) -> Result<(), Box<dyn Error>> {
let db = crate::get_db(prefix);
if let Some(dir) = db.parent() {
if !dir.exists() {
fs::create_dir_all(dir)?;
}
}
let fd = File::create(db)?;
let mut writer = Encoder::new(fd, 0)?;
let cfg = PrettyConfig::new().struct_names(true);
ron::ser::to_writer_pretty(&mut writer, self, cfg)?;
writer.finish()?;
Ok(())
}
pub fn rebuild(_prefix: Option<PathBuf>) -> Result<Self, Box<dyn Error>> {
unimplemented!();
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn update_to_string() {
let up = Update {
name: "hpk".to_string(),
version: Version::Number(42),
arch: Arch::aarch64,
release: 1,
url: Url::parse("https://hitchhiker-linux.org/pub/packages/").unwrap(),
}
.to_string();
assert_eq!("hpk-42_aarch64_1".to_string(), up);
}
#[test]
fn upate_full_url() {
let up = Update {
name: "hpk".to_string(),
version: Version::Number(42),
arch: Arch::aarch64,
release: 1,
url: Url::parse("https://hitchhiker-linux.org/pub/packages/").unwrap(),
};
let full_url = up.full_url().unwrap().to_string();
assert_eq!(
"https://hitchhiker-linux.org/pub/packages/hpk-42_aarch64_1.tar.zstd".to_string(),
full_url
);
}
}