From 4a315efd7f64a50632cc17f6f41b7b216372dbb2 Mon Sep 17 00:00:00 2001 From: Igor Tonkopryadchenko Date: Wed, 5 Oct 2022 22:45:17 +0300 Subject: [PATCH 1/5] initial clap4 migration with declarative style refactor --- Cargo.lock | 66 +++++++++-- Cargo.toml | 2 +- src/args.rs | 175 ++++++++++++++++++++++++++++ src/clone.rs | 4 +- src/comment.rs | 14 +-- src/install.rs | 10 +- src/list.rs | 18 +-- src/list_comments.rs | 13 +-- src/main.rs | 266 ++++++++++--------------------------------- src/remove.rs | 12 +- src/search.rs | 13 +-- src/style.rs | 42 +++---- src/update.rs | 4 +- src/upgrade.rs | 11 +- src/util.rs | 14 +-- src/whoami.rs | 13 +-- 16 files changed, 347 insertions(+), 330 deletions(-) create mode 100644 src/args.rs diff --git a/Cargo.lock b/Cargo.lock index 36566f3..d319ebe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -189,25 +189,37 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.22" +version = "4.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +checksum = "30607dd93c420c6f1f80b544be522a0238a7db35e6a12968d28910983fee0df0" dependencies = [ "atty", "bitflags", + "clap_derive", "clap_lex", - "indexmap", "once_cell", "strsim", "termcolor", - "textwrap", +] + +[[package]] +name = "clap_derive" +version = "4.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a307492e1a34939f79d3b6b9650bd2b971513cd775436bf2b78defeb5af00b" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" dependencies = [ "os_str_bytes", ] @@ -630,6 +642,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1030,6 +1048,30 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.46" @@ -1423,12 +1465,6 @@ dependencies = [ "winapi 0.2.8", ] -[[package]] -name = "textwrap" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" - [[package]] name = "thiserror" version = "1.0.37" @@ -1602,6 +1638,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "walkdir" version = "2.3.2" diff --git a/Cargo.toml b/Cargo.toml index 6e6bce0..a4545c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ categories = ["command-line-utilities"] bat = { version = "0.21.0", default-features = false, features = ["paging", "regex-fancy"]} colored = { git = "https://github.com/mackwic/colored" } chrono = "0.4.19" -clap = { version = "3.2.16", features = ["cargo", "env"] } +clap = { version = "4", features = ["derive", "env", "cargo"] } dirs = "4.0.0" edit = "0.1.4" exitcode = "1.1.2" diff --git a/src/args.rs b/src/args.rs new file mode 100644 index 0000000..e7c8bbc --- /dev/null +++ b/src/args.rs @@ -0,0 +1,175 @@ +// use std::fmt; + +use clap::{Args, Parser, Subcommand, ValueEnum}; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +#[command(propagate_version = true)] +pub struct Cli { + #[command(subcommand)] + pub command: Commands, +} + +#[derive(Subcommand)] +pub enum Commands { + /// Clone a package base from the MPR + Clone { + /// The package to clone + #[arg(required = true)] + package_name: String, + + #[command(flatten)] + mpr_url: MprURL, + }, + /// Comment on a package page + Comment { + /// The package to comment on + #[arg(required = true)] + package_name: String, + + /// The comment to post + #[arg(short, long = "msg")] + message: Option, + + #[command(flatten)] + mpr_url: MprURL, + + #[command(flatten)] + mpr_token: MprToken, + }, + /// Install packages from APT and the MPR + Install { + /// The package(s) to install + #[arg(required = true)] + package_names: Vec, + + #[command(flatten)] + mpr_url: MprURL, + }, + /// List packages available via APT and the MPR + List { + #[command(flatten)] + mpr_url: MprURL, + + /// Output the package's name without any extra details + #[arg(long = "name-only", default_value_t = false)] + name_only: bool, + + #[arg(long, value_enum, default_value_t=SearchMode::None)] + mode: SearchMode, + + /// The package(s) to get information for + #[arg(required = true)] + package_names: Vec, + }, + /// List the comments on a package + ListComments { + #[command(flatten)] + mpr_url: MprURL, + + /// When to send output to a pager + #[arg(long, value_enum, default_value_t=Paging::Auto)] + paging: Paging, + + /// The package(s) to get information for + #[arg(required = true)] + package_name: String, + }, + /// Remove packages from the system + Remove { + #[command(flatten)] + mpr_url: MprURL, + + /// Remove configuration files along with the package(s) + #[arg(long)] + purge: bool, + + /// Remove configuration files along with the package(s) + #[arg(long)] + autoremove: bool, + + /// The package(s) to get information for + #[arg(required = true)] + package_names: Vec, + }, + /// Search for an APT/MPR package + Search { + #[command(flatten)] + mpr_url: MprURL, + + /// Output the package's name without any extra details + #[arg(long = "name-only", default_value_t = false)] + name_only: bool, + + #[arg(long, value_enum, default_value_t=SearchMode::None)] + mode: SearchMode, + + /// The package(s) to get information for + #[arg(required = true)] + query: Vec, + }, + /// Update the APT cache on the system + Update { + #[command(flatten)] + mpr_url: MprURL, + }, + /// Upgrade the packages on the system + Upgrade { + #[command(flatten)] + mpr_url: MprURL, + + #[arg(long, value_enum, default_value_t=UpgradeMode::Both)] + mode: UpgradeMode, + }, + /// Show the currently authenticated user + Whoami { + #[command(flatten)] + mpr_url: MprURL, + + #[command(flatten)] + mpr_token: MprToken, + }, +} + +#[derive(Clone, Debug, ValueEnum)] +pub enum UpgradeMode { + /// Upgrade both APT and MPR packages + Both, + /// Only upgrade APT packages + AptOnly, + /// Only upgrade MPR packages + MprOnly +} + +#[derive(Clone, Debug, ValueEnum)] +pub enum Paging { + Auto, + Always, + Never +} + +#[derive(Args)] +pub struct MprURL { + /// URL to access the MPR from + #[arg(env = "MPR_URL", default_value = "https://mpr.makedeb.org", long = "mpr-url")] + pub url: String, +} + +#[derive(Args)] +pub struct MprToken { + /// The API token to authenticate to the MPR with + #[arg(env = "MPR_TOKEN", required = true, hide_env_values = true, long = "token")] + pub token: String, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +pub enum SearchMode { + /// No filter applied + None, + /// Only packages available on the MPR + MprOnly, + /// Only packages available via APT + AptOnly, + /// Only installed packages + Installed +} \ No newline at end of file diff --git a/src/clone.rs b/src/clone.rs index e80cb49..532a581 100644 --- a/src/clone.rs +++ b/src/clone.rs @@ -4,9 +4,7 @@ use crate::{ }; use rust_apt::cache::Cache as AptCache; -pub fn clone(args: &clap::ArgMatches) { - let pkg: &String = args.get_one("pkg").unwrap(); - let mpr_url: &String = args.get_one("mpr-url").unwrap(); +pub fn clone(pkg: &String, mpr_url: &String) { let cache = Cache::new(AptCache::new(), MprCache::new()); let mut pkgbases: Vec<&String> = Vec::new(); diff --git a/src/comment.rs b/src/comment.rs index c960136..8903ea9 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -9,17 +9,7 @@ struct CommentResult { link: String, } -pub fn comment(args: &clap::ArgMatches) { - let pkg: &String = args.get_one("pkg").unwrap(); - let mpr_url: &String = args.get_one("mpr-url").unwrap(); - let api_token: &String = match args.get_one("token") { - Some(token) => token, - None => { - message::error("No API token was provided.\n"); - quit::with_code(exitcode::USAGE); - } - }; - +pub fn comment(pkg: &String, message: &Option, api_token: String, mpr_url: String) { // Get a list of packages. let mpr_cache = MprCache::new(); let mut pkgnames: Vec<&String> = Vec::new(); @@ -36,7 +26,7 @@ pub fn comment(args: &clap::ArgMatches) { // Get the message. // If no message was supplied, get one from the user. - let msg: String = match args.get_one::("msg") { + let msg: String = match message { Some(msg) => (msg).to_owned(), None => { // Get the editor. diff --git a/src/install.rs b/src/install.rs index 36e0043..369a2b4 100644 --- a/src/install.rs +++ b/src/install.rs @@ -6,9 +6,7 @@ use crate::{ }; use rust_apt::cache::Cache as AptCache; -pub fn install(args: &clap::ArgMatches) { - let pkglist: Vec<&String> = args.get_many("pkg").unwrap().collect(); - let mpr_url: &String = args.get_one("mpr-url").unwrap(); +pub fn install(pkglist: &Vec, mpr_url: String) { let cache = Cache::new(AptCache::new(), MprCache::new()); // Package sources. @@ -20,7 +18,7 @@ pub fn install(args: &clap::ArgMatches) { // should just show those packages and abort. let mut unfindable = false; - for pkg in &pkglist { + for pkg in pkglist { if cache.get_apt_pkg(pkg).is_none() && cache.get_mpr_pkg(pkg).is_none() { message::error(&format!( "Unable to find package '{}'.\n", @@ -34,7 +32,7 @@ pub fn install(args: &clap::ArgMatches) { quit::with_code(exitcode::USAGE); } - for pkg in &pkglist { + for pkg in pkglist { let apt_pkg = cache.get_apt_pkg(pkg); let mpr_pkg = cache.get_mpr_pkg(pkg); @@ -80,5 +78,5 @@ pub fn install(args: &clap::ArgMatches) { quit::with_code(exitcode::UNAVAILABLE); } - cache.commit(&mpr_install_order, mpr_url); + cache.commit(&mpr_install_order, &mpr_url); } diff --git a/src/list.rs b/src/list.rs index 2984018..9e544b9 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,19 +1,11 @@ use crate::{ + args::SearchMode, cache::{Cache, CachePackage, MprCache}, style, }; use rust_apt::cache::Cache as AptCache; -pub fn list(args: &clap::ArgMatches) { - let pkglist: Vec<&String> = match args.get_many("pkg") { - Some(pkglist) => pkglist.collect(), - None => Vec::new(), - }; - let apt_only = args.is_present("apt-only"); - let mpr_only = args.is_present("mpr-only"); - let installed_only = args.is_present("installed-only"); - let name_only = args.is_present("name-only"); - +pub fn list(pkglist: &Vec, _: &String, mode: &SearchMode, name_only: &bool) { let cache = Cache::new(AptCache::new(), MprCache::new()); let mut candidates: Vec<&Vec> = Vec::new(); @@ -34,10 +26,8 @@ pub fn list(args: &clap::ArgMatches) { style::generate_pkginfo_entries( &candidates, &cache, - apt_only, - mpr_only, - installed_only, - name_only + mode, + *name_only ) ); } diff --git a/src/list_comments.rs b/src/list_comments.rs index 8e2fdf6..ee8b4c2 100644 --- a/src/list_comments.rs +++ b/src/list_comments.rs @@ -1,4 +1,4 @@ -use crate::{cache::MprCache, message}; +use crate::{cache::MprCache, message, args::Paging}; use bat::{self, PrettyPrinter}; use chrono::{TimeZone, Utc}; use serde::Deserialize; @@ -11,10 +11,7 @@ struct Comment { user: String, } -pub fn list_comments(args: &clap::ArgMatches) { - let pkgbase: &String = args.get_one("pkg").unwrap(); - let mpr_url: &String = args.get_one("mpr-url").unwrap(); - let paging = args.get_one::("paging").unwrap().as_str(); +pub fn list_comments(pkgbase: &String, mpr_url: &String, paging: &Paging) { let mpr_cache = MprCache::new(); let mut pkgbases: Vec<&String> = Vec::new(); @@ -77,9 +74,9 @@ pub fn list_comments(args: &clap::ArgMatches) { // Get the paging mode from the user. let paging_mode = match paging { - "always" => bat::PagingMode::Always, - "never" => bat::PagingMode::Never, - &_ => bat::PagingMode::QuitIfOneScreen, + Paging::Always => bat::PagingMode::Always, + Paging::Never => bat::PagingMode::Never, + Paging::Auto => bat::PagingMode::QuitIfOneScreen, }; PrettyPrinter::new() diff --git a/src/main.rs b/src/main.rs index 59b291b..3f7261e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![feature(let_chains)] +mod args; mod cache; mod clone; mod comment; @@ -16,7 +17,8 @@ mod upgrade; mod util; mod whoami; -use clap::{self, Arg, Command, PossibleValue}; +use args::{Cli, Commands}; +use clap::Parser; pub use rust_apt::util as apt_util; use std::{ env, @@ -26,183 +28,8 @@ use std::{ use style::Colorize; use which::which; -#[rustfmt::skip] -fn get_cli() -> Command<'static> { - // Common arguments used in multiple commands. - let token_arg = Arg::new("token") - .help("The API token to authenticate to the MPR with") - .long("token") - .env("MPR_TOKEN") - .hide_env_values(true) - .takes_value(true) - .required(true); - - let mpr_url_arg = Arg::new("mpr-url") - .help("URL to access the MPR from") - .long("mpr-url") - .env("MPR_URL") - .hide_env_values(true) - .takes_value(true) - .default_value("https://mpr.makedeb.org"); - - let mpr_only_arg = Arg::new("mpr-only") - .help("Filter results to packages available on the MPR") - .long("mpr-only"); - - let apt_only_arg = Arg::new("apt-only") - .help("Filter results to packages available via APT") - .long("apt-only"); - - let installed_only_arg = Arg::new("installed-only") - .help("Filter results to installed packages") - .short('i') - .long("installed"); - - let name_only_arg = Arg::new("name-only") - .help("Output the package's name without any extra details") - .long("name-only"); - - // The CLI. - Command::new(clap::crate_name!()) - .version(clap::crate_version!()) - .about(clap::crate_description!()) - .arg_required_else_help(true) - .subcommand( - Command::new("clone") - .about("Clone a package base from the MPR") - .arg( - Arg::new("pkg") - .help("The package to clone") - .required(true) - ) - .arg(mpr_url_arg.clone()) - ) - .subcommand( - Command::new("comment") - .arg_required_else_help(true) - .about("Comment on a package page") - .arg( - Arg::new("pkg") - .help("The package to comment on") - .required(true) - .takes_value(true) - ) - .arg( - Arg::new("msg") - .help("The comment to post") - .short('m') - .long("msg") - ) - .arg(token_arg.clone()) - .arg(mpr_url_arg.clone()) - ) - .subcommand( - Command::new("install") - .about("Install packages from APT and the MPR") - .arg( - Arg::new("pkg") - .help("The package(s) to install") - .multiple_values(true) - .required(true) - ) - .arg(mpr_url_arg.clone()) - ) - .subcommand( - Command::new("list") - .about("List packages available via APT and the MPR") - .arg( - Arg::new("pkg") - .help("The package(s) to get information for") - .multiple_values(true) - ) - .arg(mpr_only_arg.clone()) - .arg(apt_only_arg.clone()) - .arg(installed_only_arg.clone()) - .arg(name_only_arg.clone()) - ) - .subcommand( - Command::new("list-comments") - .arg_required_else_help(true) - .about("List the comments on a package") - .arg( - Arg::new("pkg") - .help("The package to view comments for") - .required(true) - ) - .arg( - Arg::new("paging") - .help("When to send output to a pager") - .long("paging") - .takes_value(true) - .default_value("auto") - .value_parser([ - PossibleValue::new("auto"), - PossibleValue::new("always"), - PossibleValue::new("never") - ]) - ) - .arg(mpr_url_arg.clone()) - ) - .subcommand( - Command::new("remove") - .about("Remove packages from the system") - .arg_required_else_help(true) - .arg( - Arg::new("pkg") - .help("The package(s) to remove") - .multiple_values(true) - ) - .arg( - Arg::new("purge") - .help("Remove configuration files along with the package(s)") - .long("purge") - ) - .arg( - Arg::new("autoremove") - .help("Automatically remove any unneeded packages") - .long("autoremove") - ) - .arg(mpr_url_arg.clone().hide(true)) - ) - .subcommand( - Command::new("search") - .about("Search for an APT/MPR package") - .arg_required_else_help(true) - .arg( - Arg::new("query") - .required(true) - .help("The query to search for") - .multiple_values(true) - ) - .arg(mpr_only_arg.clone()) - .arg(apt_only_arg.clone()) - .arg(installed_only_arg.clone()) - .arg(name_only_arg.clone()) - ) - .subcommand( - Command::new("update") - .about("Update the APT cache on the system") - .arg(mpr_url_arg.clone()) - ) - .subcommand( - Command::new("upgrade") - .about("Upgrade the packages on the system") - .arg(Arg::new("apt-only").help("Only upgrade APT packages").long("apt-only").conflicts_with("mpr-only")) - .arg(Arg::new("mpr-only").help("Only upgrade MPR packages").long("mpr-only").conflicts_with("apt-only")) - .arg(mpr_url_arg.clone()) - ) - .subcommand( - Command::new("whoami") - .about("Show the currently authenticated user") - .arg(token_arg.clone()) - .arg(mpr_url_arg.clone()) - ) -} - #[quit::main] fn main() { - let cmd_results = get_cli().get_matches(); - // Make sure that this executable has the `setuid` flag set and is owned by // root. Parts of this program (intentionally) expect such behavior. let cmd_name = { @@ -239,35 +66,66 @@ fn main() { util::sudo::to_root(); - // If we're running a command that should be permission-checked, then do so. - if vec!["install", "remove", "update", "upgrade"].contains(&cmd_results.subcommand().unwrap().0) - { - // If we're running a command that invokes 'makedeb', ensure that we're not - // running as root. - if vec!["install", "upgrade"].contains(&cmd_results.subcommand().unwrap().0) - && *util::sudo::NORMAL_UID == 0 - { - message::error(&format!( - "This command cannot be ran as root, as it needs to call '{}', which is required to run under a non-root user.\n", - "makedeb".bold().green() - )); - quit::with_code(exitcode::USAGE); - } + let cli = Cli::parse(); - util::sudo::check_perms(); - } + match &cli.command { + Commands::Clone { package_name, mpr_url } => { + println!("'clone' {:?} from {:?}", package_name, mpr_url.url); + clone::clone(package_name, &mpr_url.url) + }, + Commands::Comment { package_name, message, mpr_url, mpr_token } => { + println!("'comment': package {:?}: '{:?}'. Token: '{:?}' on {:?}", package_name, message, mpr_token.token, mpr_url.url); + comment::comment(package_name, message, mpr_token.token.clone(), mpr_url.url.clone()) + }, + Commands::Install { package_names, mpr_url } => { + println!("'install': packages {:?}: on {:?}", package_names, mpr_url.url); + + if *util::sudo::NORMAL_UID == 0 { + return message::error(&format!( + "This command cannot be ran as root, as it needs to call '{}', which is required to run under a non-root user.\n", + "makedeb".bold().green() + )); + } + + install::install(package_names, mpr_url.url.clone()) + }, + Commands::List { package_names, mode, mpr_url, name_only } => { + println!("'list': packages {:?}: on {:?}. filter mode: {:?}, name_only: {}", package_names, mpr_url.url, mode, name_only); + list::list(package_names, &mpr_url.url, mode, name_only) + }, + Commands::ListComments { package_name, mpr_url, paging } => { + println!("'list comments': package {:?}: on {:?}. paging mode: {:?}", package_name, mpr_url.url, paging); + list_comments::list_comments(package_name, &mpr_url.url, paging) + }, + Commands::Remove { package_names, mpr_url, purge, autoremove } => { + println!("'remove': packages {:?}: on {:?}. purge: {}, autoremove: {}", package_names, mpr_url.url, purge, autoremove); + util::sudo::check_perms(); + remove::remove(package_names, &mpr_url.url, *purge, *autoremove) + }, + Commands::Search { query, mode, mpr_url, name_only } => { + println!("'search': query {:?}: on {:?}. filter mode: {:?}, name_only: {}", query, mpr_url.url, mode, name_only); + search::search(query, &mpr_url.url, mode, *name_only) + }, + Commands::Update { mpr_url } => { + println!("'update': on {:?}", mpr_url.url); + util::sudo::check_perms(); + update::update(&mpr_url.url) + }, + Commands::Upgrade { mpr_url, mode } => { + println!("'upgrade': on {:?}, mode: {:?}", mpr_url.url, mode); + + if *util::sudo::NORMAL_UID == 0 { + return message::error(&format!( + "This command cannot be ran as root, as it needs to call '{}', which is required to run under a non-root user.\n", + "makedeb".bold().green() + )); + } - match cmd_results.subcommand() { - Some(("clone", args)) => clone::clone(args), - Some(("comment", args)) => comment::comment(args), - Some(("install", args)) => install::install(args), - Some(("list", args)) => list::list(args), - Some(("list-comments", args)) => list_comments::list_comments(args), - Some(("remove", args)) => remove::remove(args), - Some(("search", args)) => search::search(args), - Some(("update", args)) => update::update(args), - Some(("upgrade", args)) => upgrade::upgrade(args), - Some(("whoami", args)) => whoami::whoami(args), - _ => unreachable!(), + upgrade::upgrade(&mpr_url.url, mode) + }, + Commands::Whoami { mpr_url, mpr_token } => { + println!("'whoami': Token '{:?}' on {:?}", mpr_token.token, mpr_url.url); + whoami::whoami(mpr_token.token.clone(), mpr_url.url.clone()) + }, }; } diff --git a/src/remove.rs b/src/remove.rs index eb3d9e6..f6b2e56 100644 --- a/src/remove.rs +++ b/src/remove.rs @@ -5,17 +5,7 @@ use crate::{ }; use rust_apt::cache::{Cache as AptCache, PackageSort}; -pub fn remove(args: &clap::ArgMatches) { - let pkglist: Vec<&String> = { - if let Some(pkglist) = args.get_many("pkg") { - pkglist.collect() - } else { - Vec::new() - } - }; - let purge = args.is_present("purge"); - let autoremove = args.is_present("autoremove"); - let mpr_url: &String = args.get_one("mpr-url").unwrap(); +pub fn remove(pkglist: &Vec, mpr_url: &String, purge: bool, autoremove: bool) { let cache = Cache::new(AptCache::new(), MprCache::new()); // Lock the cache. diff --git a/src/search.rs b/src/search.rs index cfb7297..56e44a8 100644 --- a/src/search.rs +++ b/src/search.rs @@ -1,16 +1,11 @@ use crate::{ cache::{Cache, CachePackage, MprCache}, + args::SearchMode, style, }; use rust_apt::cache::Cache as AptCache; -pub fn search(args: &clap::ArgMatches) { - let query_list: Vec<&String> = args.get_many("query").unwrap().collect(); - let apt_only = args.is_present("apt-only"); - let mpr_only = args.is_present("mpr-only"); - let installed_only = args.is_present("installed-only"); - let name_only = args.is_present("name-only"); - +pub fn search(query_list: &Vec, _: &String, mode: &SearchMode, name_only: bool) { let cache = Cache::new(AptCache::new(), MprCache::new()); let mut candidates: Vec<&Vec> = Vec::new(); @@ -47,9 +42,7 @@ pub fn search(args: &clap::ArgMatches) { style::generate_pkginfo_entries( &candidates, &cache, - apt_only, - mpr_only, - installed_only, + mode, name_only ) ); diff --git a/src/style.rs b/src/style.rs index 414be86..8af897e 100644 --- a/src/style.rs +++ b/src/style.rs @@ -6,6 +6,7 @@ use chrono::{TimeZone, Utc}; use crate::{ apt_util, + args::SearchMode, cache::{Cache, CachePackage}, }; use std::{cmp::Ordering, fmt::Write}; @@ -154,9 +155,7 @@ pub fn generate_pkginfo_entry( pub fn generate_pkginfo_entries( pkgs: &Vec<&Vec>, cache: &Cache, - apt_only: bool, - mpr_only: bool, - installed_only: bool, + mode: &SearchMode, name_only: bool, ) -> String { let mut matches = Vec::new(); @@ -165,28 +164,23 @@ pub fn generate_pkginfo_entries( for pkg_group in pkgs { let pkgname = &pkg_group.get(0).unwrap().pkgname; - // APT only. - if apt_only && cache.get_apt_pkg(pkgname).is_none() { - continue; - } - - // MPR only. - if mpr_only && cache.get_mpr_pkg(pkgname).is_none() { - continue; - } - - // Installed only. - if installed_only - && let Some(pkg) = cache.apt_cache().get(pkgname) - && !pkg.is_installed() - { - continue; - } else if cache.apt_cache().get(pkgname).is_none() { - continue; + match mode { + SearchMode::None | SearchMode::AptOnly => { + if cache.get_apt_pkg(&pkgname).is_some() { + matches.push(pkg_group) + } + }, + SearchMode::MprOnly => { + if cache.get_mpr_pkg(&pkgname).is_some() { + matches.push(pkg_group) + } + }, + SearchMode::Installed => { + if let Some(pkg) = cache.apt_cache().get(pkgname) && !pkg.is_installed() { + matches.push(pkg_group) + } + } } - - // Package be passed all the tests bro. We's be adding it to the vector now. - matches.push(pkg_group); } let matches_len = matches.len(); diff --git a/src/update.rs b/src/update.rs index 9e1efa2..7fef738 100644 --- a/src/update.rs +++ b/src/update.rs @@ -8,9 +8,7 @@ use std::{ process::Command, }; -pub fn update(args: &clap::ArgMatches) { - let mpr_url: &String = args.get_one("mpr-url").unwrap(); - +pub fn update(mpr_url: &String) { // For some reason we have to set our current UID to 0 instead of just the EUID // when using setuid functionality. TODO: No clue why, but this fixes the // issue for now. diff --git a/src/upgrade.rs b/src/upgrade.rs index 54afdce..2c27fb8 100644 --- a/src/upgrade.rs +++ b/src/upgrade.rs @@ -1,4 +1,5 @@ use crate::{ + args::UpgradeMode, cache::{Cache, MprCache}, install_util, util, }; @@ -8,10 +9,12 @@ use rust_apt::{ }; use std::{collections::HashMap, fs}; -pub fn upgrade(args: &clap::ArgMatches) { - let apt_only = args.is_present("apt-only"); - let mpr_only = args.is_present("mpr-only"); - let mpr_url: &String = args.get_one("mpr-url").unwrap(); +pub fn upgrade(mpr_url: &String, upgrade_mode: &UpgradeMode) { + let (apt_only, mpr_only) = match upgrade_mode { + UpgradeMode::Both => (false, false), + UpgradeMode::AptOnly => (true, false), + UpgradeMode::MprOnly => (false, true), + }; let cache = Cache::new(AptCache::new(), MprCache::new()); diff --git a/src/util.rs b/src/util.rs index 82d097e..d572ec1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -20,13 +20,13 @@ struct AuthenticationError { } // Struct to handle API-authenticated requests to the MPR. -pub struct AuthenticatedRequest<'a> { - api_token: &'a str, - mpr_url: &'a str, +pub struct AuthenticatedRequest { + api_token: String, + mpr_url: String, } -impl<'a> AuthenticatedRequest<'a> { - pub fn new(api_token: &'a str, mpr_url: &'a str) -> Self { +impl AuthenticatedRequest { + pub fn new(api_token: String, mpr_url: String) -> Self { Self { api_token, mpr_url } } @@ -60,7 +60,7 @@ impl<'a> AuthenticatedRequest<'a> { let client = reqwest::blocking::Client::new(); let resp = client .get(format!("{}/api/{}", self.mpr_url, path)) - .header("Authorization", self.api_token) + .header("Authorization", &self.api_token) .send(); self.handle_response(resp) @@ -72,7 +72,7 @@ impl<'a> AuthenticatedRequest<'a> { let resp = client .post(format!("{}/api/{}", self.mpr_url, path)) .body(body) - .header("Authorization", self.api_token) + .header("Authorization", &self.api_token) .send(); self.handle_response(resp) diff --git a/src/whoami.rs b/src/whoami.rs index 32769f5..21f3e3d 100644 --- a/src/whoami.rs +++ b/src/whoami.rs @@ -1,4 +1,4 @@ -use crate::{message, util}; +use crate::{util}; use serde::Deserialize; #[derive(Deserialize)] @@ -6,16 +6,7 @@ struct Authenticated { user: String, } -pub fn whoami(args: &clap::ArgMatches) { - let api_token: &String = match args.get_one("token") { - Some(token) => token, - None => { - message::error("No API key was provided."); - quit::with_code(exitcode::USAGE); - } - }; - let mpr_url: &String = args.get_one("mpr-url").unwrap(); - +pub fn whoami(api_token: String, mpr_url: String) { let request = util::AuthenticatedRequest::new(api_token, mpr_url); let resp_text = request.get("test"); let json = serde_json::from_str::(&resp_text).unwrap(); From 434d98b11211ed35939990597f283fbb832f01dc Mon Sep 17 00:00:00 2001 From: Igor Tonkopryadchenko Date: Wed, 5 Oct 2022 22:48:40 +0300 Subject: [PATCH 2/5] change to 'rust-apt' main branch --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d319ebe..eab460e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1215,7 +1215,7 @@ dependencies = [ [[package]] name = "rust-apt" version = "0.4.1" -source = "git+https://gitlab.com/volian/rust-apt?branch=fix/tagfile-parsing#7750594be3ca30ee29804bf46cf76896dd61655b" +source = "git+https://gitlab.com/volian/rust-apt#d782cb01814c747c3dde82b23a8d4eac200bbff8" dependencies = [ "cxx", "cxx-build", diff --git a/Cargo.toml b/Cargo.toml index a4545c3..52aa884 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ makedeb-srcinfo = "0.8.0" quit = "1.1.4" regex = "1.6.0" reqwest = { version = "0.11.11", features = ["blocking", "json"] } -rust-apt = { git = "https://gitlab.com/volian/rust-apt", branch = "fix/tagfile-parsing" } +rust-apt = { git = "https://gitlab.com/volian/rust-apt" } serde = { version = "1.0.142", features = ["derive"] } serde_json = "1.0.83" tempfile = "3.3.0" From ac1664235482e9c7d3462591b9d33b9a697918e3 Mon Sep 17 00:00:00 2001 From: Igor Tonkopryadchenko Date: Sun, 9 Oct 2022 10:02:06 +0300 Subject: [PATCH 3/5] removed debug prints --- src/args.rs | 2 -- src/main.rs | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/src/args.rs b/src/args.rs index e7c8bbc..0c9182e 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,5 +1,3 @@ -// use std::fmt; - use clap::{Args, Parser, Subcommand, ValueEnum}; #[derive(Parser)] diff --git a/src/main.rs b/src/main.rs index 3f7261e..6a5a005 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,16 +70,12 @@ fn main() { match &cli.command { Commands::Clone { package_name, mpr_url } => { - println!("'clone' {:?} from {:?}", package_name, mpr_url.url); clone::clone(package_name, &mpr_url.url) }, Commands::Comment { package_name, message, mpr_url, mpr_token } => { - println!("'comment': package {:?}: '{:?}'. Token: '{:?}' on {:?}", package_name, message, mpr_token.token, mpr_url.url); comment::comment(package_name, message, mpr_token.token.clone(), mpr_url.url.clone()) }, Commands::Install { package_names, mpr_url } => { - println!("'install': packages {:?}: on {:?}", package_names, mpr_url.url); - if *util::sudo::NORMAL_UID == 0 { return message::error(&format!( "This command cannot be ran as root, as it needs to call '{}', which is required to run under a non-root user.\n", @@ -90,30 +86,23 @@ fn main() { install::install(package_names, mpr_url.url.clone()) }, Commands::List { package_names, mode, mpr_url, name_only } => { - println!("'list': packages {:?}: on {:?}. filter mode: {:?}, name_only: {}", package_names, mpr_url.url, mode, name_only); list::list(package_names, &mpr_url.url, mode, name_only) }, Commands::ListComments { package_name, mpr_url, paging } => { - println!("'list comments': package {:?}: on {:?}. paging mode: {:?}", package_name, mpr_url.url, paging); list_comments::list_comments(package_name, &mpr_url.url, paging) }, Commands::Remove { package_names, mpr_url, purge, autoremove } => { - println!("'remove': packages {:?}: on {:?}. purge: {}, autoremove: {}", package_names, mpr_url.url, purge, autoremove); util::sudo::check_perms(); remove::remove(package_names, &mpr_url.url, *purge, *autoremove) }, Commands::Search { query, mode, mpr_url, name_only } => { - println!("'search': query {:?}: on {:?}. filter mode: {:?}, name_only: {}", query, mpr_url.url, mode, name_only); search::search(query, &mpr_url.url, mode, *name_only) }, Commands::Update { mpr_url } => { - println!("'update': on {:?}", mpr_url.url); util::sudo::check_perms(); update::update(&mpr_url.url) }, Commands::Upgrade { mpr_url, mode } => { - println!("'upgrade': on {:?}, mode: {:?}", mpr_url.url, mode); - if *util::sudo::NORMAL_UID == 0 { return message::error(&format!( "This command cannot be ran as root, as it needs to call '{}', which is required to run under a non-root user.\n", @@ -124,7 +113,6 @@ fn main() { upgrade::upgrade(&mpr_url.url, mode) }, Commands::Whoami { mpr_url, mpr_token } => { - println!("'whoami': Token '{:?}' on {:?}", mpr_token.token, mpr_url.url); whoami::whoami(mpr_token.token.clone(), mpr_url.url.clone()) }, }; From 3d4a43f0a8c4c26d87c848306954eda7db36caaf Mon Sep 17 00:00:00 2001 From: Igor Tonkopryadchenko Date: Sun, 9 Oct 2022 17:34:19 +0300 Subject: [PATCH 4/5] fix cargo fmt and clippy warnings --- src/args.rs | 21 ++++++++++---- src/install.rs | 2 +- src/list.rs | 7 +---- src/list_comments.rs | 2 +- src/main.rs | 69 ++++++++++++++++++++++++++++++-------------- src/remove.rs | 2 +- src/search.rs | 9 ++---- src/style.rs | 4 +-- src/upgrade.rs | 2 +- src/whoami.rs | 2 +- 10 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/args.rs b/src/args.rs index 0c9182e..4b7cd91 100644 --- a/src/args.rs +++ b/src/args.rs @@ -136,27 +136,36 @@ pub enum UpgradeMode { /// Only upgrade APT packages AptOnly, /// Only upgrade MPR packages - MprOnly + MprOnly, } #[derive(Clone, Debug, ValueEnum)] pub enum Paging { Auto, Always, - Never + Never, } #[derive(Args)] pub struct MprURL { /// URL to access the MPR from - #[arg(env = "MPR_URL", default_value = "https://mpr.makedeb.org", long = "mpr-url")] + #[arg( + env = "MPR_URL", + default_value = "https://mpr.makedeb.org", + long = "mpr-url" + )] pub url: String, } #[derive(Args)] pub struct MprToken { /// The API token to authenticate to the MPR with - #[arg(env = "MPR_TOKEN", required = true, hide_env_values = true, long = "token")] + #[arg( + env = "MPR_TOKEN", + required = true, + hide_env_values = true, + long = "token" + )] pub token: String, } @@ -169,5 +178,5 @@ pub enum SearchMode { /// Only packages available via APT AptOnly, /// Only installed packages - Installed -} \ No newline at end of file + Installed, +} diff --git a/src/install.rs b/src/install.rs index 369a2b4..87348f8 100644 --- a/src/install.rs +++ b/src/install.rs @@ -6,7 +6,7 @@ use crate::{ }; use rust_apt::cache::Cache as AptCache; -pub fn install(pkglist: &Vec, mpr_url: String) { +pub fn install(pkglist: &Vec, mpr_url: String) { let cache = Cache::new(AptCache::new(), MprCache::new()); // Package sources. diff --git a/src/list.rs b/src/list.rs index 9e544b9..a2a99ed 100644 --- a/src/list.rs +++ b/src/list.rs @@ -23,11 +23,6 @@ pub fn list(pkglist: &Vec, _: &String, mode: &SearchMode, name_only: &bo print!( "{}", - style::generate_pkginfo_entries( - &candidates, - &cache, - mode, - *name_only - ) + style::generate_pkginfo_entries(&candidates, &cache, mode, *name_only) ); } diff --git a/src/list_comments.rs b/src/list_comments.rs index ee8b4c2..bd50010 100644 --- a/src/list_comments.rs +++ b/src/list_comments.rs @@ -1,4 +1,4 @@ -use crate::{cache::MprCache, message, args::Paging}; +use crate::{args::Paging, cache::MprCache, message}; use bat::{self, PrettyPrinter}; use chrono::{TimeZone, Utc}; use serde::Deserialize; diff --git a/src/main.rs b/src/main.rs index 6a5a005..637dbce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -69,13 +69,25 @@ fn main() { let cli = Cli::parse(); match &cli.command { - Commands::Clone { package_name, mpr_url } => { - clone::clone(package_name, &mpr_url.url) - }, - Commands::Comment { package_name, message, mpr_url, mpr_token } => { - comment::comment(package_name, message, mpr_token.token.clone(), mpr_url.url.clone()) - }, - Commands::Install { package_names, mpr_url } => { + Commands::Clone { + package_name, + mpr_url, + } => clone::clone(package_name, &mpr_url.url), + Commands::Comment { + package_name, + message, + mpr_url, + mpr_token, + } => comment::comment( + package_name, + message, + mpr_token.token.clone(), + mpr_url.url.clone(), + ), + Commands::Install { + package_names, + mpr_url, + } => { if *util::sudo::NORMAL_UID == 0 { return message::error(&format!( "This command cannot be ran as root, as it needs to call '{}', which is required to run under a non-root user.\n", @@ -84,24 +96,37 @@ fn main() { } install::install(package_names, mpr_url.url.clone()) - }, - Commands::List { package_names, mode, mpr_url, name_only } => { - list::list(package_names, &mpr_url.url, mode, name_only) - }, - Commands::ListComments { package_name, mpr_url, paging } => { - list_comments::list_comments(package_name, &mpr_url.url, paging) - }, - Commands::Remove { package_names, mpr_url, purge, autoremove } => { + } + Commands::List { + package_names, + mode, + mpr_url, + name_only, + } => list::list(package_names, &mpr_url.url, mode, name_only), + Commands::ListComments { + package_name, + mpr_url, + paging, + } => list_comments::list_comments(package_name, &mpr_url.url, paging), + Commands::Remove { + package_names, + mpr_url, + purge, + autoremove, + } => { util::sudo::check_perms(); remove::remove(package_names, &mpr_url.url, *purge, *autoremove) - }, - Commands::Search { query, mode, mpr_url, name_only } => { - search::search(query, &mpr_url.url, mode, *name_only) - }, + } + Commands::Search { + query, + mode, + mpr_url, + name_only, + } => search::search(query, &mpr_url.url, mode, *name_only), Commands::Update { mpr_url } => { util::sudo::check_perms(); update::update(&mpr_url.url) - }, + } Commands::Upgrade { mpr_url, mode } => { if *util::sudo::NORMAL_UID == 0 { return message::error(&format!( @@ -111,9 +136,9 @@ fn main() { } upgrade::upgrade(&mpr_url.url, mode) - }, + } Commands::Whoami { mpr_url, mpr_token } => { whoami::whoami(mpr_token.token.clone(), mpr_url.url.clone()) - }, + } }; } diff --git a/src/remove.rs b/src/remove.rs index f6b2e56..a1de4da 100644 --- a/src/remove.rs +++ b/src/remove.rs @@ -5,7 +5,7 @@ use crate::{ }; use rust_apt::cache::{Cache as AptCache, PackageSort}; -pub fn remove(pkglist: &Vec, mpr_url: &String, purge: bool, autoremove: bool) { +pub fn remove(pkglist: &Vec, mpr_url: &str, purge: bool, autoremove: bool) { let cache = Cache::new(AptCache::new(), MprCache::new()); // Lock the cache. diff --git a/src/search.rs b/src/search.rs index 56e44a8..28709e4 100644 --- a/src/search.rs +++ b/src/search.rs @@ -1,6 +1,6 @@ use crate::{ - cache::{Cache, CachePackage, MprCache}, args::SearchMode, + cache::{Cache, CachePackage, MprCache}, style, }; use rust_apt::cache::Cache as AptCache; @@ -39,11 +39,6 @@ pub fn search(query_list: &Vec, _: &String, mode: &SearchMode, name_only print!( "{}", - style::generate_pkginfo_entries( - &candidates, - &cache, - mode, - name_only - ) + style::generate_pkginfo_entries(&candidates, &cache, mode, name_only) ); } diff --git a/src/style.rs b/src/style.rs index 8af897e..d61d9b4 100644 --- a/src/style.rs +++ b/src/style.rs @@ -166,12 +166,12 @@ pub fn generate_pkginfo_entries( match mode { SearchMode::None | SearchMode::AptOnly => { - if cache.get_apt_pkg(&pkgname).is_some() { + if cache.get_apt_pkg(pkgname).is_some() { matches.push(pkg_group) } }, SearchMode::MprOnly => { - if cache.get_mpr_pkg(&pkgname).is_some() { + if cache.get_mpr_pkg(pkgname).is_some() { matches.push(pkg_group) } }, diff --git a/src/upgrade.rs b/src/upgrade.rs index 2c27fb8..75c86be 100644 --- a/src/upgrade.rs +++ b/src/upgrade.rs @@ -9,7 +9,7 @@ use rust_apt::{ }; use std::{collections::HashMap, fs}; -pub fn upgrade(mpr_url: &String, upgrade_mode: &UpgradeMode) { +pub fn upgrade(mpr_url: &str, upgrade_mode: &UpgradeMode) { let (apt_only, mpr_only) = match upgrade_mode { UpgradeMode::Both => (false, false), UpgradeMode::AptOnly => (true, false), diff --git a/src/whoami.rs b/src/whoami.rs index 21f3e3d..cb15d86 100644 --- a/src/whoami.rs +++ b/src/whoami.rs @@ -1,4 +1,4 @@ -use crate::{util}; +use crate::util; use serde::Deserialize; #[derive(Deserialize)] From 7960890225dcaffa03bb8b9d0f9725b0cc67c887 Mon Sep 17 00:00:00 2001 From: Igor Tonkopryadchenko Date: Mon, 10 Oct 2022 21:36:45 +0300 Subject: [PATCH 5/5] fix sudo checks, added missing permission check, fixed '--installed' filter for 'search' and 'list' --- Cargo.lock | 10 ++++++++ Cargo.toml | 3 ++- src/args.rs | 12 ++++++--- src/cache.rs | 27 +++++++++++++++++--- src/list.rs | 66 +++++++++++++++++++++++++++++++++++------------ src/main.rs | 49 ++++++++++++++++++++++++++--------- src/search.rs | 53 ++++++++------------------------------ src/style.rs | 71 +++------------------------------------------------ 8 files changed, 145 insertions(+), 146 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eab460e..e7174ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -776,6 +776,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.3" @@ -891,6 +900,7 @@ dependencies = [ "edit", "exitcode", "flate2", + "itertools", "lazy_static", "makedeb-srcinfo", "quit", diff --git a/Cargo.toml b/Cargo.toml index 52aa884..cb6157b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,11 +14,12 @@ categories = ["command-line-utilities"] bat = { version = "0.21.0", default-features = false, features = ["paging", "regex-fancy"]} colored = { git = "https://github.com/mackwic/colored" } chrono = "0.4.19" -clap = { version = "4", features = ["derive", "env", "cargo"] } +clap = { version = "4", features = ["cargo", "derive", "env"] } dirs = "4.0.0" edit = "0.1.4" exitcode = "1.1.2" flate2 = "1.0.24" +itertools = "0.10.5" lazy_static = "1.4.0" makedeb-srcinfo = "0.8.0" quit = "1.1.4" diff --git a/src/args.rs b/src/args.rs index 4b7cd91..010634c 100644 --- a/src/args.rs +++ b/src/args.rs @@ -53,11 +53,15 @@ pub enum Commands { #[arg(long = "name-only", default_value_t = false)] name_only: bool, + /// Only installed packages + #[arg(long = "installed-only", default_value_t = false)] + installed_only: bool, + #[arg(long, value_enum, default_value_t=SearchMode::None)] mode: SearchMode, /// The package(s) to get information for - #[arg(required = true)] + #[arg(required = false)] package_names: Vec, }, /// List the comments on a package @@ -102,6 +106,10 @@ pub enum Commands { #[arg(long, value_enum, default_value_t=SearchMode::None)] mode: SearchMode, + /// Only installed packages + #[arg(long = "installed-only", default_value_t = false)] + installed_only: bool, + /// The package(s) to get information for #[arg(required = true)] query: Vec, @@ -177,6 +185,4 @@ pub enum SearchMode { MprOnly, /// Only packages available via APT AptOnly, - /// Only installed packages - Installed, } diff --git a/src/cache.rs b/src/cache.rs index 533289b..7cc79f5 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -239,7 +239,7 @@ pub enum CachePackageSource { Mpr, } -#[derive(Clone, PartialEq)] +#[derive(Clone)] pub struct CachePackage { pub pkgname: String, pub pkgbase: Option, @@ -251,6 +251,7 @@ pub struct CachePackage { pub popularity: Option, pub ood: Option, pub source: CachePackageSource, + pub is_installed: bool, } pub struct Cache { @@ -259,7 +260,7 @@ pub struct Cache { /// The underlying MPR cache struct. mpr_cache: MprCache, /// A combined list of all packages in the cache. - //pkglist: Vec, + pub pkglist: Vec, /// A map for getting all packages with a certain pkgname. Can be quicker /// than looping over [`Self::pkglist`]. pkgmap: HashMap>, @@ -303,6 +304,7 @@ impl Cache { popularity: None, ood: None, source: CachePackageSource::Apt, + is_installed: pkg.is_installed(), }); } @@ -318,6 +320,11 @@ impl Cache { popularity: Some(pkg.popularity), ood: pkg.ood, source: CachePackageSource::Mpr, + is_installed: if let Some(apkg) = apt_cache.get(&pkg.pkgname) { + apkg.is_installed() + } else { + false + }, }); } @@ -338,7 +345,7 @@ impl Cache { Self { apt_cache, mpr_cache, - //pkglist, + pkglist, pkgmap, } } @@ -727,6 +734,10 @@ impl Cache { &self.pkgmap } + pub fn pkglist(&self) -> Vec<&CachePackage> { + self.pkglist.iter().collect() + } + // Get the APT variant of a package. pub fn get_apt_pkg(&self, pkgname: &str) -> Option<&CachePackage> { if let Some(pkglist) = self.pkgmap().get(&pkgname.to_owned()) { @@ -751,6 +762,16 @@ impl Cache { None } + pub fn search(&self, pkgname: &str) -> Vec<&CachePackage> { + self.pkglist + .iter() + .filter(|pkg| { + pkg.pkgname.contains(pkgname) + || pkg.pkgdesc.as_deref().unwrap_or_default().contains(pkgname) + }) + .collect() + } + // Find the pkgbase of a given MPR package's pkgname. pub fn find_pkgbase(&self, pkgname: &str) -> Option { for pkg in self.mpr_cache().packages().values() { diff --git a/src/list.rs b/src/list.rs index a2a99ed..f3171b0 100644 --- a/src/list.rs +++ b/src/list.rs @@ -1,28 +1,60 @@ use crate::{ args::SearchMode, - cache::{Cache, CachePackage, MprCache}, + cache::{Cache, CachePackage, CachePackageSource, MprCache}, style, }; +use itertools::Itertools; use rust_apt::cache::Cache as AptCache; -pub fn list(pkglist: &Vec, _: &String, mode: &SearchMode, name_only: &bool) { +pub fn list( + query_list: &Vec, + _: &String, + mode: &SearchMode, + name_only: bool, + installed_only: bool, +) -> String { let cache = Cache::new(AptCache::new(), MprCache::new()); - let mut candidates: Vec<&Vec> = Vec::new(); - if !pkglist.is_empty() { - for pkg in pkglist { - if let Some(pkg_group) = cache.pkgmap().get(pkg) { - candidates.push(pkg_group); - } - } + let packages = if query_list.is_empty() { + cache.pkglist() } else { - for pkg_group in cache.pkgmap().values() { - candidates.push(pkg_group); - } - } + query_list + .iter() + .flat_map(|query| cache.search(query)) + .collect() + }; + + let pkgs = if installed_only { + packages + .into_iter() + .filter(|pkg| pkg.is_installed) + .collect() + } else { + packages + }; - print!( - "{}", - style::generate_pkginfo_entries(&candidates, &cache, mode, *name_only) - ); + let pkgs = match mode { + SearchMode::None => pkgs, + SearchMode::AptOnly => pkgs + .into_iter() + .filter(|pkg| pkg.source == CachePackageSource::Apt) + .collect(), + SearchMode::MprOnly => pkgs + .into_iter() + .filter(|pkg| pkg.source == CachePackageSource::Mpr) + .collect(), + }; + + let pkgs: Vec<&CachePackage> = pkgs + .into_iter() + .unique_by(|pkg| pkg.pkgname.clone()) + .collect(); + + match name_only { + true => pkgs.iter().map(|pkg| &pkg.pkgname).join("\n"), + false => pkgs + .iter() + .map(|pkg| style::generate_pkginfo_entry(&pkg.pkgname, &cache)) + .join("\n\n"), + } } diff --git a/src/main.rs b/src/main.rs index 637dbce..1b2c306 100644 --- a/src/main.rs +++ b/src/main.rs @@ -88,11 +88,10 @@ fn main() { package_names, mpr_url, } => { - if *util::sudo::NORMAL_UID == 0 { - return message::error(&format!( - "This command cannot be ran as root, as it needs to call '{}', which is required to run under a non-root user.\n", - "makedeb".bold().green() - )); + util::sudo::check_perms(); + + if is_running_as_sudo() { + return; } install::install(package_names, mpr_url.url.clone()) @@ -102,7 +101,17 @@ fn main() { mode, mpr_url, name_only, - } => list::list(package_names, &mpr_url.url, mode, name_only), + installed_only, + } => println!( + "{}", + list::list( + package_names, + &mpr_url.url, + mode, + *name_only, + *installed_only + ) + ), Commands::ListComments { package_name, mpr_url, @@ -122,17 +131,20 @@ fn main() { mode, mpr_url, name_only, - } => search::search(query, &mpr_url.url, mode, *name_only), + installed_only, + } => println!( + "{}", + search::search(query, &mpr_url.url, mode, *name_only, *installed_only) + ), Commands::Update { mpr_url } => { util::sudo::check_perms(); update::update(&mpr_url.url) } Commands::Upgrade { mpr_url, mode } => { - if *util::sudo::NORMAL_UID == 0 { - return message::error(&format!( - "This command cannot be ran as root, as it needs to call '{}', which is required to run under a non-root user.\n", - "makedeb".bold().green() - )); + util::sudo::check_perms(); + + if is_running_as_sudo() { + return; } upgrade::upgrade(&mpr_url.url, mode) @@ -142,3 +154,16 @@ fn main() { } }; } + +fn is_running_as_sudo() -> bool { + if *util::sudo::NORMAL_UID == 0 { + message::error(&format!( + "This command cannot be ran as root, as it needs to call '{}', which is required to run under a non-root user.\n", + "makedeb".bold().green() + )); + + true + } else { + false + } +} diff --git a/src/search.rs b/src/search.rs index 28709e4..fe01377 100644 --- a/src/search.rs +++ b/src/search.rs @@ -1,44 +1,11 @@ -use crate::{ - args::SearchMode, - cache::{Cache, CachePackage, MprCache}, - style, -}; -use rust_apt::cache::Cache as AptCache; - -pub fn search(query_list: &Vec, _: &String, mode: &SearchMode, name_only: bool) { - let cache = Cache::new(AptCache::new(), MprCache::new()); - let mut candidates: Vec<&Vec> = Vec::new(); - - for query in query_list { - for (pkgname, pkg_group) in cache.pkgmap().iter() { - let mut pkgs = Vec::new(); - let apt_pkg = cache.get_apt_pkg(pkgname); - let mpr_pkg = cache.get_mpr_pkg(pkgname); - - if let Some(pkg) = apt_pkg { - pkgs.push(pkg); - } - if let Some(pkg) = mpr_pkg { - pkgs.push(pkg); - } - - for pkg in pkgs { - if (pkg.pkgname.contains(query) - || pkg - .pkgdesc - .as_ref() - .unwrap_or(&"".to_owned()) - .contains(query)) - && !candidates.contains(&pkg_group) - { - candidates.push(pkg_group); - } - } - } - } - - print!( - "{}", - style::generate_pkginfo_entries(&candidates, &cache, mode, name_only) - ); +use crate::args::SearchMode; + +pub fn search( + query_list: &Vec, + mpr_url: &String, + mode: &SearchMode, + name_only: bool, + installed_only: bool, +) -> String { + crate::list::list(query_list, mpr_url, mode, name_only, installed_only) } diff --git a/src/style.rs b/src/style.rs index d61d9b4..8c29a5a 100644 --- a/src/style.rs +++ b/src/style.rs @@ -4,11 +4,7 @@ use lazy_static::lazy_static; use chrono::{TimeZone, Utc}; -use crate::{ - apt_util, - args::SearchMode, - cache::{Cache, CachePackage}, -}; +use crate::{apt_util, cache::Cache}; use std::{cmp::Ordering, fmt::Write}; lazy_static! { @@ -18,17 +14,7 @@ lazy_static! { /// Generate a colored package information entry. /// If `name_only` is [`true`], the package name will be returned by itself. -pub fn generate_pkginfo_entry( - pkg_group: &[CachePackage], - cache: &Cache, - name_only: bool, -) -> String { - let pkgname = pkg_group.get(0).unwrap().pkgname.clone(); - - if name_only { - return pkgname; - } - +pub fn generate_pkginfo_entry(pkgname: &String, cache: &Cache) -> String { // Set up the string we'll return at the end of the function. let mut return_string = String::new(); @@ -36,8 +22,8 @@ pub fn generate_pkginfo_entry( write!(return_string, "{}", pkgname.custom_color(*UBUNTU_ORANGE)).unwrap(); // Get the APT and MPR packages. - let apt_pkg = cache.get_apt_pkg(&pkgname); - let mpr_pkg = cache.get_mpr_pkg(&pkgname); + let apt_pkg = cache.get_apt_pkg(pkgname); + let mpr_pkg = cache.get_mpr_pkg(pkgname); // Get the package sources. let mut src_str = String::new(); @@ -151,52 +137,3 @@ pub fn generate_pkginfo_entry( return_string } - -pub fn generate_pkginfo_entries( - pkgs: &Vec<&Vec>, - cache: &Cache, - mode: &SearchMode, - name_only: bool, -) -> String { - let mut matches = Vec::new(); - let mut result_string = String::new(); - - for pkg_group in pkgs { - let pkgname = &pkg_group.get(0).unwrap().pkgname; - - match mode { - SearchMode::None | SearchMode::AptOnly => { - if cache.get_apt_pkg(pkgname).is_some() { - matches.push(pkg_group) - } - }, - SearchMode::MprOnly => { - if cache.get_mpr_pkg(pkgname).is_some() { - matches.push(pkg_group) - } - }, - SearchMode::Installed => { - if let Some(pkg) = cache.apt_cache().get(pkgname) && !pkg.is_installed() { - matches.push(pkg_group) - } - } - } - } - - let matches_len = matches.len(); - - for (index, pkg_group) in matches.iter().enumerate() { - if name_only { - result_string.push_str(&pkg_group.get(0).unwrap().pkgname); - result_string.push('\n'); - } else if index == matches_len - 1 { - result_string.push_str(&generate_pkginfo_entry(pkg_group, cache, name_only)); - result_string.push('\n'); - } else { - result_string.push_str(&generate_pkginfo_entry(pkg_group, cache, name_only)); - result_string.push_str("\n\n"); - } - } - - result_string -}