use std::path::{Path, PathBuf}; pub struct Repository { pub name: String, } impl Repository { pub fn list() -> Vec { let mut repos = vec![]; for entry in std::fs::read_dir("./data").unwrap() { let path = entry.unwrap().path(); let file_name = path.file_name().unwrap().to_str().unwrap().to_string(); repos.push(file_name); } repos } pub fn create(name: &str) -> Repository { let path = PathBuf::from("./data").join(name); std::fs::create_dir_all(path).unwrap(); Repository::new(name).unwrap() } } impl Repository { pub fn new(name: &str) -> Option { if PathBuf::from("./data").join(name).exists() { Some(Repository { name: name.to_string(), }) } else { None } } pub fn arch(&self) -> Vec { let dir_path = PathBuf::from("./data").join(&self.name); let mut arch = 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(); arch.push(file_name); } } arch } pub fn base_path(&self, arch: &str) -> PathBuf { PathBuf::from("./data").join(&self.name).join(arch) } pub fn db_content(&self, arch: &str) -> Option> { std::fs::read( self.base_path(arch) .join(format!("{}.db.tar.gz", self.name)), ) .ok() } pub fn sig_content(&self, arch: &str) -> Option> { std::fs::read( self.base_path(arch) .join(format!("{}.db.tar.gz.sig", self.name)), ) .ok() } pub fn extract_pkg_name(name: &str) -> (String, String, String, String) { // "{}-{}-{}-{}.pkg.tar.zst" let splitted: Vec<&str> = name.split('-').collect(); let name = splitted.get(0).unwrap(); let version = splitted.get(1).unwrap(); let rel = splitted.get(2).unwrap(); let arch = splitted.get(3).unwrap().trim_end_matches(".pkg.tar.zst"); ( name.to_string(), version.to_string(), rel.to_string(), arch.to_string(), ) } pub fn get_pkg(&self, arch: &str, pkg_name: &str) -> Option { let pkg_name = if pkg_name.ends_with(".sig") { pkg_name.trim_end_matches(".sig").to_string() } else { pkg_name.to_string() }; let (name, version, _, _) = Repository::extract_pkg_name(&pkg_name); let pkg = Package::new(&self.name, &arch, &name, &version); if pkg.exists() { return Some(pkg); } else { None } } } #[derive(Debug, Clone)] pub struct Package { repo: String, arch: String, name: String, version: Option, } impl Package { pub fn new(repo: &str, arch: &str, pkg_name: &str, version: &str) -> Self { Package { repo: repo.to_string(), arch: arch.to_string(), name: pkg_name.to_string(), version: Some(version.to_string()), } } pub fn find(repo: &str, arch: &str, pkg_name: &str) -> Self { let mut base = Package { repo: repo.to_string(), arch: arch.to_string(), name: pkg_name.to_string(), version: None, }; let versions = base.versions(); let ver = versions.first().unwrap(); base.version = Some(ver.clone()); base } pub fn save(&self, pkg: Vec, sig: Option>) { 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) .join(format!("{}.db.tar.gz", self.repo)); run_command(vec![ "repo-add", db_file.to_str().unwrap(), pkg_file.to_str().unwrap(), ]); if &self.arch == "any" { let archs = Repository::new(&self.repo).unwrap().arch(); for arch in archs { if arch == "any" { continue; } let db_file = PathBuf::from("./data") .join(&self.repo) .join(&arch) .join(format!("{}.db.tar.gz", self.repo)); run_command(vec![ "repo-add", db_file.to_str().unwrap(), pkg_file.to_str().unwrap(), ]); } } } fn base_path(&self) -> PathBuf { Path::new("./data").join(&self.repo).join(&self.arch) } pub fn switch_arch(&self, arch: &str) -> Self { let mut new = self.clone(); new.arch = arch.to_string(); new } pub fn exists(&self) -> bool { let pkg_file = self.base_path().join(self.file_name()); pkg_file.exists() } pub fn get_version(&self, version: &str) -> Self { let mut new_pkg = self.clone(); new_pkg.version = Some(version.to_string()); new_pkg } pub fn is_signed(&self) -> bool { let signed_file = self.base_path().join(format!("{}.sig", self.file_name())); signed_file.exists() } pub fn file_name(&self) -> String { 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, ) } pub fn versions(&self) -> Vec { 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") { // Extract version (assuming the filename is "---.pkg.tar.zst") if let Some(version) = file_name.split('-').nth(1) { versions.push(version.to_string()); } } } } // Sort versions in descending order (most recent version first) versions.sort_by(|a, b| b.cmp(a)); versions } pub fn pkg_content(&self) -> Option> { if self.exists() { return std::fs::read(self.base_path().join(self.file_name())).ok(); } None } pub fn sig_content(&self) -> Option> { 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::>()) .spawn() .unwrap() .wait() .unwrap(); }