From 1c4af03a0b61410a56dd6fd56ca07dea67e8ff07 Mon Sep 17 00:00:00 2001 From: Jakob Meier Date: Fri, 11 Aug 2023 10:35:31 +0200 Subject: [PATCH] Added package list and initial package info support --- src/cmds/info.rs | 46 ++++++++++++++++++ src/cmds/list.rs | 12 +++++ src/cmds/mod.rs | 4 ++ src/main.rs | 12 ++++- src/types.rs | 118 +++++++++++++++++++++++++---------------------- 5 files changed, 134 insertions(+), 58 deletions(-) create mode 100644 src/cmds/info.rs create mode 100644 src/cmds/list.rs diff --git a/src/cmds/info.rs b/src/cmds/info.rs new file mode 100644 index 0000000..cc9b02c --- /dev/null +++ b/src/cmds/info.rs @@ -0,0 +1,46 @@ +use std::path::PathBuf; +use crate::types::{Database, PackageName}; + +/// show package information for given package (if available) +pub fn info(name: PackageName, db: Option, show_provides: bool, show_depends: bool) { + let db = Database::from_file(db.unwrap_or(PathBuf::from("db.yml"))) + .expect("Unable to open database"); + + { + let pkg = db.packages.get(&name).expect("Package not found"); + + println!("{}-{}-r{}", pkg.name, pkg.version, pkg.rel); + println!("{}", pkg.description); + println!("license: {}", pkg.license); + println!("url: {}", pkg.url); + println!("path: {}", pkg.path.to_str().unwrap_or("")); + + { + print!("arch: "); + for a in &pkg.arch { + print!("{}", a); + } + println!(); + } + } + + if show_depends { + println!("---Dependencies---"); + if let Some(deps) = db.depends.get(&name) { + for dep in deps { + println!("{}", dep); + } + } + } + + if show_provides { + println!("---Provides---"); + if let Some(prv) = db.provides.get(&name) { + for pr in prv { + println!("{}", pr); + } + } + + } + +} diff --git a/src/cmds/list.rs b/src/cmds/list.rs new file mode 100644 index 0000000..ada19bb --- /dev/null +++ b/src/cmds/list.rs @@ -0,0 +1,12 @@ +use std::path::PathBuf; +use crate::types::Database; + +/// lists all packages in the repo +pub fn list(db: Option) { + let db = Database::from_file(db.unwrap_or(PathBuf::from("db.yml"))) + .expect("Unable to open database"); + + for pkg in db.packages.values() { + println!("{}-{}-r{} ({})", pkg.name, pkg.version, pkg.rel, pkg.license); + } +} diff --git a/src/cmds/mod.rs b/src/cmds/mod.rs index 2f310ab..9ab54f2 100644 --- a/src/cmds/mod.rs +++ b/src/cmds/mod.rs @@ -1,2 +1,6 @@ mod scan; +mod list; +mod info; pub use scan::scan; +pub use list::list; +pub use info::info; diff --git a/src/main.rs b/src/main.rs index 95dcb2d..ecbd943 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, collections::HashMap}; +use std::path::PathBuf; use clap::Parser; mod cmds; @@ -30,7 +30,13 @@ enum Commands { /// the packagename package: String, /// package database path - db: Option + db: Option, + /// prints the dependencies after the package info + #[arg(short='d',long)] + show_depends: bool, + /// prints the packages provided by the package after the package info + #[arg(short='p', long)] + show_provides: bool }, /// Searches the database for a package with the name /// or containing the name @@ -47,6 +53,8 @@ fn main() { match cli { Commands::Scan { folder, db } => cmds::scan(folder, db), + Commands::List { db } => cmds::list(db), + Commands::Info { package, db, show_depends, show_provides } => cmds::info(package, db, show_provides, show_depends), _ => panic!("Unimplemented") } } diff --git a/src/types.rs b/src/types.rs index 37f8e77..f6d9ea8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,4 @@ -use std::{path::PathBuf, collections::{HashMap, HashSet}, fs::{FileType, DirEntry, self}}; - +use std::{path::PathBuf, collections::{HashMap, HashSet}, fs}; use regex::Regex; use serde::{Deserialize, Serialize}; use walkdir::WalkDir; @@ -12,21 +11,21 @@ pub type PackageName = String; #[derive(Serialize, Deserialize, Debug)] pub struct Package { /// the name of the package - name: String, + pub name: String, /// the version of the package - version: String, + pub version: String, /// license used by the package - license: String, + pub license: String, /// project url - url: String, + pub url: String, /// package description - description: String, + pub description: String, /// package release number (starts at zero) - rel: usize, + pub rel: usize, /// list? of supported/disabled architectures - arch: HashSet, + pub arch: HashSet, /// relative path to APKBUILD - path: PathBuf + pub path: PathBuf } /// Package information collection @@ -34,73 +33,80 @@ pub struct Package { pub struct Database { /// dictionary of package names associated with a dictionary /// of additional package info - packages: HashMap, + pub packages: HashMap, /// dictionary of package names associated with a dependency list /// the dependencies may not create cycles /// all dependencies of a given package have to be build, /// before the package can be build - depends: HashMap>, + pub depends: HashMap>, /// dictionary of provide names associated with a list of packages, /// which provide the given package /// in case multiple packages provide the same provider, /// all of them should be build /// (runtime apk should take care of the rest) - provides: HashMap> + pub provides: HashMap> } impl Database { pub fn from_scan(folder: PathBuf) -> Self { let mut db = Self { - packages: HashMap::new(), - depends: HashMap::new(), - provides: HashMap::new(), + packages: HashMap::new(), + depends: HashMap::new(), + provides: HashMap::new(), }; - for entry in WalkDir::new(folder).follow_links(true) { - // ignore broken entries - if let Ok(entry) = entry { - if entry.file_type().is_dir() { - // ignore folders themselves - continue; - } - if entry.file_type().is_file() && entry.file_name() != "APKBUILD" { - // ignore every file that is not the APKBUILD file itself - continue; - } - - - let file = entry.into_path(); - let folder = file.parent().expect("Unable to get APKBUILD dir").to_path_buf(); - - let apk = APKBUILD::from_file(file); - let depends = apk.depends; - let mut provides = apk.provides; - for sub in apk.subs { - provides.insert(sub); - } - let pkg = Package { - name: apk.name.to_string(), - arch: apk.arch, - version: apk.version, - license: apk.license, - description: apk.description, - url: apk.url, - rel: apk.rel, - path: folder - }; - - db.packages.insert(apk.name.to_string(), pkg); - db.depends.insert(apk.name.to_string(), depends); - db.provides.insert(apk.name.to_string(), provides); + // loop through all folders, but ignore broken entries + for entry in WalkDir::new(folder).follow_links(true).into_iter().flatten(){ + if entry.file_type().is_dir() { + // ignore folders themselves + continue; + } + if entry.file_type().is_file() && entry.file_name() != "APKBUILD" { + // ignore every file that is not the APKBUILD file itself + continue; } + + + let file = entry.into_path(); + let folder = file.parent().expect("Unable to get APKBUILD dir").to_path_buf(); + + let apk = Apkbuild::from_file(file); + let depends = apk.depends; + let mut provides = apk.provides; + for sub in apk.subs { + provides.insert(sub); + } + let pkg = Package { + name: apk.name.to_string(), + arch: apk.arch, + version: apk.version, + license: apk.license, + description: apk.description, + url: apk.url, + rel: apk.rel, + path: folder + }; + + db.packages.insert(apk.name.to_string(), pkg); + db.depends.insert(apk.name.to_string(), depends); + db.provides.insert(apk.name.to_string(), provides); } db } + pub fn from_file(file: PathBuf) -> Option { + if let Ok(content) = fs::read_to_string(file) { + if let Ok(yml) = serde_yaml::from_str(&content) { + return Some(yml); + } + } + + None + } } /// unprocessed Data directly extracted from APKBUILD #[derive(Debug)] -struct APKBUILD { +struct Apkbuild { /// the name of the package name: String, /// the version of the package @@ -122,12 +128,12 @@ struct APKBUILD { /// list of subpackages subs: Vec } -impl APKBUILD { +impl Apkbuild { fn from_file(file: PathBuf) -> Self{ let cont = fs::read_to_string(file) .expect("Failed to open APKBUILD"); - let mut apk = APKBUILD { + let mut apk = Self { name: easy_match("pkgname", &cont), description: easy_match("pkgdesc", &cont), rel: easy_match("pkgrel", &cont).parse() @@ -167,7 +173,7 @@ impl APKBUILD { fn easy_match(var: &str, text: &str) -> String { let re = Regex::new(&format!("{}=\"[^\"]+\"|{}=[^\"\n]+\n", var,var)).unwrap(); let extr = re.find(text) - .expect(&format!("Invalid APKBUILD, expected to find {}", var)).as_str(); + .unwrap_or_else(|| panic!("Invalid APKBUILD, expected to find {}", var)).as_str(); // tidy up the value, by removing unneccesarry characters // var="" -- 2.38.5