diff --git a/src/cli.rs b/src/cli.rs index a3b36e1..c75dd9b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -83,8 +83,12 @@ pub struct PackageInitCommand { #[derive(FromArgs, PartialEq, Debug)] #[argh(subcommand, name = "info")] -/// Print package info from PKGBUILD -pub struct PackageInfoCommand {} +/// Print package info from PKGBUILD or package file +pub struct PackageInfoCommand { + #[argh(positional)] + /// package file + pub pkg: Option, +} #[derive(FromArgs, PartialEq, Debug)] #[argh(subcommand, name = "push")] diff --git a/src/main.rs b/src/main.rs index 05618bc..13e8ceb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use cli::PaccoCLI; use cmd_lib::{run_cmd, run_fun}; use config::Config; use pacco::pkg::arch::Architecture; -use pacco::pkg::package::run_command; +use pacco::pkg::package::{PackageFile, run_command}; use pacco::pkg::{Package, Repository}; use rocket::data::ToByteUnit; use rocket::{routes, tokio}; @@ -335,9 +335,14 @@ async fn main() { } } } - cli::PackageCommands::Info(_) => { - #[rustfmt::skip] - run_cmd!(makepkg --printsrcinfo -s).unwrap(); + cli::PackageCommands::Info(info_args) => { + if let Some(pkg) = &info_args.pkg { + let pkginfo = PackageFile::new(std::path::PathBuf::from(pkg)).pkginfo(); + println!("{pkginfo:#?}"); + } else { + #[rustfmt::skip] + run_cmd!(makepkg --printsrcinfo -s).unwrap(); + } } cli::PackageCommands::Push(package_push_command) => { pacco_push( diff --git a/src/pkg/package.rs b/src/pkg/package.rs index 284ea15..5bc3a6b 100644 --- a/src/pkg/package.rs +++ b/src/pkg/package.rs @@ -1,6 +1,5 @@ use std::{ - fs::File, - io::{BufReader, Cursor, Read, Write}, + io::Write, path::{Path, PathBuf}, process::{Command, Stdio}, }; @@ -26,6 +25,31 @@ pub struct Package { pub compression: Compression, } +pub struct PackageFile { + pub path: PathBuf, + pub compression: Compression, +} + +impl PackageFile { + pub fn new(path: PathBuf) -> Self { + Self { + path: path.clone(), + compression: Compression::from_filename(path.to_str().unwrap()).unwrap(), + } + } + + pub fn pkginfo(&self) -> PackageInfo { + let content = read_file_tar(&self.path, ".PKGINFO", self.compression.clone()).unwrap(); + let keys = Package::pkginfo_from_str(&content); + PackageInfo::new(keys) + } + + pub fn pkginfo_raw(&self) -> Vec<(String, String)> { + let content = read_file_tar(&self.path, ".PKGINFO", self.compression.clone()).unwrap(); + Package::pkginfo_from_str(&content) + } +} + impl Package { /// Create a new package pub fn new( @@ -109,14 +133,12 @@ impl Package { ret } - pub fn pkginfo(&self) -> Vec<(String, String)> { - let content = read_file_tar( - &self.base_path().join(self.file_name()), - ".PKGINFO", - self.compression.clone(), - ) - .unwrap(); - Package::pkginfo_from_str(&content) + pub fn pkginfo(&self) -> PackageInfo { + PackageFile::new(self.base_path().join(self.file_name())).pkginfo() + } + + pub fn pkginfo_raw(&self) -> Vec<(String, String)> { + PackageFile::new(self.base_path().join(self.file_name())).pkginfo_raw() } pub fn arch(&self) -> Vec { @@ -661,3 +683,177 @@ impl Compression { } } } + +#[derive(Debug, Default)] +pub struct PackageInfo { + /// Architecture + pub architectures: Vec, + /// Build Timestamp + pub build_date: i64, + /// Licenses + pub licenses: Vec, + /// Make Dependencies + pub makedepends: Vec, + /// Packager + pub packager: Option, + /// Package Base + pub pkgbase: String, + /// Description + pub description: String, + /// Package Name + pub name: String, + /// Version + pub version: String, + /// Size + pub size: i64, + /// Website + pub website: Option, + /// XDATA + pub xdata: Option, + /// Dependencies + pub dependencies: Vec, + /// Optional Dependencies + pub opt_depends: Vec, + /// Provides + pub provides: Vec, + // Replaces + pub replaces: Vec, + /// Backup + pub backup: Vec, + /// Conflicts + pub conflict: Vec, + /// Check Dependencies + pub check_depends: Vec, + /// Groups + pub groups: Vec, +} + +#[derive(Debug, Default)] +pub struct OptionalDependency { + pub pkg: String, + pub reason: Option, +} + +impl PackageInfo { + pub fn new(keys: Vec<(String, String)>) -> Self { + let mut info = Self::default(); + + for (key, val) in keys { + match key.as_str() { + "arch" => { + info.architectures.push(Architecture::parse(&val).unwrap()); + } + "builddate" => { + info.build_date = val.parse().unwrap(); + } + "license" => { + let elements: Vec<_> = val.split(';').collect(); + for e in elements { + let licenses: Vec<_> = e.split(" OR ").collect(); + for e in licenses { + info.licenses.push(e.to_string()); + } + } + } + "makedepend" => { + let pkgs: Vec<_> = val.split(';').collect(); + for pkg in pkgs { + info.makedepends.push(pkg.to_string()); + } + } + "packager" => { + info.packager = Some(val); + } + "pkgbase" => { + info.pkgbase = val; + } + "pkgdesc" => { + info.description = val; + } + "pkgname" => { + info.name = val; + } + "pkgver" => { + info.version = val; + } + "size" => { + info.size = val.parse().unwrap(); + } + "url" => { + info.website = Some(val); + } + "xdata" => { + info.xdata = Some(val); + } + "depend" => { + let pkgs: Vec<_> = val.split(';').collect(); + for pkg in pkgs { + info.dependencies.push(pkg.to_string()); + } + } + "optdepend" => { + let pkgs: Vec<_> = val.split(';').collect(); + for pkg in pkgs { + let (pkg, reason) = if let Some((pkg, reason)) = pkg.split_once(':') { + let reason = if !reason.is_empty() { + Some(reason.trim().to_string()) + } else { + None + }; + (pkg, reason) + } else { + (pkg, None) + }; + + info.opt_depends.push(OptionalDependency { + pkg: pkg.to_string(), + reason: reason, + }); + } + } + "provides" => { + let provides: Vec<_> = val.split(';').collect(); + for p in provides { + info.provides.push(p.to_string()); + } + } + "replaces" => { + let replaces: Vec<_> = val.split(';').collect(); + for r in replaces { + info.replaces.push(r.to_string()); + } + } + "backup" => { + let backups: Vec<_> = val.split(';').collect(); + for b in backups { + info.backup.push(b.to_string()); + } + } + "conflict" => { + let conflicts: Vec<_> = val.split(';').collect(); + for e in conflicts { + info.conflict.push(e.to_string()); + } + } + "checkdepend" => { + let pkgs: Vec<_> = val.split(';').collect(); + for pkg in pkgs { + info.check_depends.push(pkg.to_string()); + } + } + "group" => { + let groups: Vec<_> = val.split(';').collect(); + for group in groups { + info.groups.push(group.to_string()); + } + } + _ => { + println!("Unrecognized key {key} -> {val}"); + std::process::exit(1); + } + } + } + + info + } +} diff --git a/src/routes/ui/pkg.rs b/src/routes/ui/pkg.rs index ca43b1f..f2f597c 100644 --- a/src/routes/ui/pkg.rs +++ b/src/routes/ui/pkg.rs @@ -46,7 +46,7 @@ pub async fn pkg_ui( let icons = pkg.icons(); let readmes = pkg.readmes(); let fonts = pkg.fonts(); - let mut pkginfo = pkg.pkginfo(); + let mut pkginfo = pkg.pkginfo_raw(); let pkg_info_header = Flex( // Package Name