183 lines
6.2 KiB
Rust
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
|
|
);
|
|
}
|
|
}
|