implement user pages + ui
All checks were successful
ci/woodpecker/push/build Pipeline was successful
All checks were successful
ci/woodpecker/push/build Pipeline was successful
This commit is contained in:
parent
67c31725c1
commit
3fabc91438
11 changed files with 1021 additions and 264 deletions
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue