refactor
All checks were successful
ci/woodpecker/push/build Pipeline was successful

This commit is contained in:
JMARyA 2024-12-29 09:09:54 +01:00
parent 376e4f4eb8
commit 6eb3678c1c
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
7 changed files with 252 additions and 134 deletions

220
src/pkg/package.rs Normal file
View file

@ -0,0 +1,220 @@
use std::path::{Path, PathBuf};
use super::{Repository, arch::Architecture};
/// General Package
#[derive(Debug, Clone)]
pub struct Package {
/// Repository of the package
repo: String,
/// `Architecture` of the package
arch: Architecture,
/// Name of the package
name: String,
/// Version of the package
version: Option<String>,
}
impl Package {
/// Create a new package
pub fn new(repo: &str, arch: Architecture, pkg_name: &str, version: &str) -> Self {
Package {
repo: repo.to_string(),
arch: arch,
name: pkg_name.to_string(),
version: Some(version.to_string()),
}
}
/// Extract values from a package filename
pub fn extract_pkg_name(file_name: &str) -> Option<(String, String, String, Architecture)> {
// Extract (assuming the filename is "<pkg_name>-<version>-<relation>-<arch>.pkg.tar.zst")
let mut splitted = file_name.split('-').collect::<Vec<&str>>();
let arch = splitted.pop()?;
assert!(arch.ends_with(".pkg.tar.zst"));
let arch = arch.trim_end_matches(".pkg.tar.zst");
let relation = splitted.pop()?;
let version = splitted.pop()?;
let pkg_name = splitted.join(" ");
return Some((
pkg_name,
version.to_string(),
relation.to_string(),
Architecture::parse(arch)?,
));
}
/// Parse a pkg filename
pub fn from_filename(repo: &str, file_name: &str) -> Package {
let (pkg_name, version, _, arch) = Package::extract_pkg_name(file_name).unwrap();
Self {
repo: repo.to_string(),
arch,
name: pkg_name,
version: Some(version.to_string()),
}
}
/// Find a package with latest version
pub fn find(repo: &str, arch: &str, pkg_name: &str) -> Self {
let mut base = Package {
repo: repo.to_string(),
arch: Architecture::parse(arch).unwrap(),
name: pkg_name.to_string(),
version: None,
};
let versions = base.versions();
let ver = versions.first().unwrap();
base.version = Some(ver.clone());
base
}
/// Save a new package to repository
pub fn save(&self, pkg: Vec<u8>, sig: Option<Vec<u8>>) {
let pkg_file = self.base_path().join(&self.file_name());
let sig_file = self.base_path().join(format!("{}.sig", self.file_name()));
std::fs::write(&pkg_file, pkg).unwrap();
if let Some(sig) = sig {
std::fs::write(sig_file, sig).unwrap();
}
let db_file = PathBuf::from("./data")
.join(&self.repo)
.join(&self.arch.to_string())
.join(format!("{}.db.tar.gz", self.repo));
repo_add(db_file.to_str().unwrap(), pkg_file.to_str().unwrap());
// Add to any arch repo db if `arch=any`
if self.arch == Architecture::any {
let archs = Repository::new(&self.repo).unwrap().arch();
for arch in archs {
if arch == Architecture::any {
continue;
}
let db_file = PathBuf::from("./data")
.join(&self.repo)
.join(&arch.to_string())
.join(format!("{}.db.tar.gz", self.repo));
repo_add(db_file.to_str().unwrap(), pkg_file.to_str().unwrap());
}
}
}
fn base_path(&self) -> PathBuf {
// <repo>/<arch>/<pkg>/
let p = Path::new("./data")
.join(&self.repo)
.join(&self.arch.to_string())
.join(&self.name);
std::fs::create_dir_all(&p).unwrap();
p
}
/// Switch the `Architecture` of the package
pub fn switch_arch(&self, arch: &str) -> Self {
let mut new = self.clone();
new.arch = Architecture::parse(arch).unwrap();
new
}
/// Switch to a specific `version` of the package
pub fn get_version(&self, version: &str) -> Self {
let mut new_pkg = self.clone();
new_pkg.version = Some(version.to_string());
new_pkg
}
/// Check if the specific package exists
pub fn exists(&self) -> bool {
let pkg_file = self.base_path().join(self.file_name());
pkg_file.exists()
}
/// Checks if the package has a signature
pub fn is_signed(&self) -> bool {
let signed_file = self.base_path().join(format!("{}.sig", self.file_name()));
signed_file.exists()
}
/// Build a pkg filename from the packages values
pub fn file_name(&self) -> String {
// TODO : pkgrel support
format!(
"{}-{}-{}-{}.pkg.tar.zst",
self.name,
if let Some(ver) = &self.version {
ver.to_string()
} else {
let versions = self.versions();
versions.first().unwrap().clone()
},
1,
self.arch.to_string(),
)
}
/// Get all versions of the package
pub fn versions(&self) -> Vec<String> {
let dir_path = self.base_path();
let mut versions = vec![];
if let Ok(entries) = std::fs::read_dir(dir_path) {
for entry in entries.filter_map(Result::ok) {
let file_name = entry.file_name().into_string().unwrap_or_default();
if file_name.starts_with(&self.name) && file_name.ends_with(".pkg.tar.zst") {
if let Some(version) = Package::from_filename(&self.repo, &file_name).version {
versions.push(version.to_string());
}
}
}
}
// Sort versions in descending order (most recent version first)
versions.sort_by(|a, b| b.cmp(a));
versions
}
/// Get the content of the `.pkg.tar.zst`
pub fn pkg_content(&self) -> Option<Vec<u8>> {
if self.exists() {
return std::fs::read(self.base_path().join(self.file_name())).ok();
}
None
}
/// Get the content of the `.pkg.tar.zst.sig`
pub fn sig_content(&self) -> Option<Vec<u8>> {
if self.exists() {
return std::fs::read(self.base_path().join(format!("{}.sig", &self.file_name()))).ok();
}
None
}
}
pub fn run_command(cmd: Vec<&str>) {
std::process::Command::new(cmd.first().unwrap())
.args(cmd.into_iter().skip(1).collect::<Vec<&str>>())
.spawn()
.unwrap()
.wait()
.unwrap();
}
/// Add a package file to a repo db
pub fn repo_add(db_file: &str, pkg_file: &str) {
run_command(vec!["repo-add", db_file, pkg_file]);
}