implement user pages + ui
All checks were successful
ci/woodpecker/push/build Pipeline was successful

This commit is contained in:
JMARyA 2025-01-12 03:58:16 +01:00
parent 67c31725c1
commit 3fabc91438
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
11 changed files with 1021 additions and 264 deletions

View file

@ -1,4 +1,10 @@
use std::path::{Path, PathBuf};
use std::{
fs::File,
io::{Cursor, Read},
path::{Path, PathBuf},
};
use tar::Archive;
use super::{Repository, arch::Architecture};
@ -6,11 +12,11 @@ use super::{Repository, arch::Architecture};
#[derive(Debug, Clone)]
pub struct Package {
/// Repository of the package
repo: String,
pub repo: String,
/// `Architecture` of the package
arch: Architecture,
pub arch: Architecture,
/// Name of the package
name: String,
pub name: String,
/// Version of the package
version: Option<String>,
}
@ -20,12 +26,78 @@ impl Package {
pub fn new(repo: &str, arch: Architecture, pkg_name: &str, version: &str) -> Self {
Package {
repo: repo.to_string(),
arch: arch,
arch,
name: pkg_name.to_string(),
version: Some(version.to_string()),
}
}
pub fn install_script(&self) -> Option<String> {
let pkg = self.base_path().join(self.file_name());
read_file_tar(&pkg, ".INSTALL")
}
pub fn file_list(&self) -> Vec<String> {
list_tar_file(&self.base_path().join(self.file_name())).unwrap()
}
pub fn binaries(&self) -> Vec<String> {
list_tar_file(&self.base_path().join(self.file_name()))
.unwrap_or_default()
.into_iter()
.filter_map(|x| {
let mut paths: Vec<_> = x.split("/").collect();
paths.reverse();
let parent = paths.get(1)?;
if (*parent == "bin" || *parent == "sbin") && !x.ends_with("/") {
return Some(x);
}
None
})
.collect()
}
pub fn pkginfo(&self) -> Vec<(String, String)> {
let content = read_file_tar(&self.base_path().join(self.file_name()), ".PKGINFO").unwrap();
let mut ret: Vec<(String, Vec<String>)> = Vec::new();
for line in content.split("\n") {
if line.starts_with('#') || line.is_empty() {
continue;
}
let (key, val) = line.split_once(" = ").unwrap();
if let Some(e) = ret.iter_mut().find(|x| x.0 == key) {
e.1.push(val.to_string());
} else {
ret.push((key.to_string(), vec![val.to_string()]));
}
}
let mut ret: Vec<_> = ret.into_iter().map(|x| (x.0, x.1.join(" "))).collect();
ret.sort_by(|a, b| a.0.cmp(&b.0));
ret
}
pub fn arch(&self) -> Vec<Architecture> {
let mut ret = Vec::new();
for a in [
Architecture::x86_64,
Architecture::aarch64,
Architecture::any,
] {
let check_pkg = self.switch_arch(a.clone());
if check_pkg.exists() {
ret.push(a);
}
}
ret
}
/// Extract values from a package filename
///
/// # Example
@ -47,10 +119,12 @@ impl Package {
/// ```
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 file_name = file_name.trim_end_matches(".sig").to_string();
let mut splitted = file_name.split('-').collect::<Vec<&str>>();
let arch = splitted.pop()?;
assert!(arch.ends_with(".pkg.tar.zst"));
assert!(arch.ends_with(".pkg.tar.zst"), "{file_name}");
let arch = arch.trim_end_matches(".pkg.tar.zst");
let relation = splitted.pop()?;
@ -58,12 +132,12 @@ impl Package {
let pkg_name = splitted.join("-");
return Some((
Some((
pkg_name,
version.to_string(),
relation.to_string(),
Architecture::parse(arch)?,
));
))
}
/// Parse a pkg filename
@ -78,25 +152,52 @@ impl Package {
}
/// Find a package with latest version
pub fn find(repo: &str, arch: &str, pkg_name: &str) -> Self {
pub fn find(repo: &str, arch: Architecture, pkg_name: &str) -> Option<Self> {
let mut base = Package {
repo: repo.to_string(),
arch: Architecture::parse(arch).unwrap(),
arch,
name: pkg_name.to_string(),
version: None,
};
let versions = base.versions();
let ver = versions.first().unwrap();
let ver = versions.first()?;
base.version = Some(ver.clone());
base
Some(base)
}
pub fn systemd_units(&self) -> Vec<String> {
// TODO : Extract unit infos
list_tar_file(&self.base_path().join(self.file_name()))
.unwrap_or_default()
.into_iter()
.filter(|x| {
let ext = x.split(".").last().unwrap();
ext == "service" || ext == "timer" || ext == "mount"
})
.collect()
}
pub fn pacman_hooks(&self) -> Vec<(String, String)> {
let pkg_file = self.base_path().join(self.file_name());
let files = list_tar_file(&pkg_file).unwrap_or_default();
files
.into_iter()
.filter(|x| {
x.starts_with("etc/pacman.d/hooks/") || x.starts_with("usr/share/libalpm/hooks/")
})
.map(|x| {
let content = read_file_tar(&pkg_file, &x).unwrap();
(x, content)
})
.collect()
}
/// 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 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();
@ -106,7 +207,7 @@ impl Package {
let db_file = PathBuf::from("./data")
.join(&self.repo)
.join(&self.arch.to_string())
.join(self.arch.to_string())
.join(format!("{}.db.tar.gz", self.repo));
repo_add(db_file.to_str().unwrap(), pkg_file.to_str().unwrap());
@ -122,7 +223,7 @@ impl Package {
let db_file = PathBuf::from("./data")
.join(&self.repo)
.join(&arch.to_string())
.join(arch.to_string())
.join(format!("{}.db.tar.gz", self.repo));
repo_add(db_file.to_str().unwrap(), pkg_file.to_str().unwrap());
@ -134,16 +235,16 @@ impl Package {
// <repo>/<arch>/<pkg>/
let p = Path::new("./data")
.join(&self.repo)
.join(&self.arch.to_string())
.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 {
pub fn switch_arch(&self, arch: Architecture) -> Self {
let mut new = self.clone();
new.arch = Architecture::parse(arch).unwrap();
new.arch = arch;
new
}
@ -236,3 +337,44 @@ pub fn run_command(cmd: Vec<&str>) {
pub fn repo_add(db_file: &str, pkg_file: &str) {
run_command(vec!["repo-add", db_file, pkg_file]);
}
pub fn read_file_tar(tar: &PathBuf, file_path: &str) -> Option<String> {
let mut file = File::open(tar).ok()?;
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
let uncompressed = zstd::decode_all(Cursor::new(buf)).unwrap();
let content = Cursor::new(uncompressed);
let mut a = Archive::new(content);
for e in a.entries().ok()? {
let mut e = e.ok()?;
let path = e.path().unwrap();
let path = path.to_str().unwrap();
if path == file_path {
let mut file_content = Vec::new();
e.read_to_end(&mut file_content).unwrap();
return String::from_utf8(file_content).ok();
}
}
None
}
pub fn list_tar_file(tar: &PathBuf) -> Option<Vec<String>> {
let mut file = File::open(tar).ok()?;
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
let uncompressed = zstd::decode_all(Cursor::new(buf)).unwrap();
let content = Cursor::new(uncompressed);
let mut a = Archive::new(content);
let mut paths = Vec::new();
for e in a.entries().ok()? {
let e = e.ok()?;
let path = e.path().ok()?;
let path = path.to_str()?;
paths.push(path.to_string());
}
Some(paths)
}