From 2c2677ae88da23631d3f076a7159bb889723024d Mon Sep 17 00:00:00 2001 From: Trivernis Date: Sat, 16 Apr 2022 20:28:36 +0200 Subject: [PATCH 1/4] Change argument parsing to clap v3 derives With this change the arguments are parsed with the new clap v3 derive syntax. This commit also sets the default command to Upgrade similar to the behaviour of other aur helpers. Signed-off-by: Trivernis --- Cargo.toml | 3 +- src/args.rs | 85 +++++++++++++ src/main.rs | 361 ++++++++++++++-------------------------------------- 3 files changed, 180 insertions(+), 269 deletions(-) create mode 100644 src/args.rs diff --git a/Cargo.toml b/Cargo.toml index 20d3588..8bab9ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,8 @@ codegen-units = 1 [dependencies] mimalloc = { version = "0.1.27", default-features = false } -clap = { version = "2.34.0", default-features = false, features = ["suggestions"] } +clap = { version = "3.1.9", features = [ "derive", "wrap_help"] } +clap_complete = "3.1.1" regex = { version = "1.5.4", default-features = false, features = ["std", "unicode-perl"] } runas = "0.2.1" rusqlite = { version = "0.26.3", default-features = false } diff --git a/src/args.rs b/src/args.rs new file mode 100644 index 0000000..30de242 --- /dev/null +++ b/src/args.rs @@ -0,0 +1,85 @@ +use clap::{Parser, Subcommand}; + +#[derive(Debug, Clone, Parser)] +#[clap(name="Amethyst", version=env!("CARGO_PKG_VERSION"), about=env!("CARGO_PKG_DESCRIPTION"))] +pub struct Args { + #[clap(subcommand)] + pub subcommand: Option, + + /// Sets the level of verbosity + #[clap(long, short, parse(from_occurrences))] + pub verbose: usize, + + /// Complete operation without prompting user + #[clap(long = "noconfirm")] + pub no_confirm: bool, +} + +#[derive(Debug, Clone, Subcommand)] +pub enum Operation { + /// Installs a package from either the AUR or the PacMan-defined repositories + #[clap(name="install", aliases=&["i", "-S"])] + Install(InstallArgs), + + /// Removes a previously installed package + #[clap(name="remove", aliases=&["rm", "-R", "Rs"])] + Remove(RemoveArgs), + + /// Searches for the relevant packages in both the AUR and repos + #[clap(name="search", aliases=&["sea", "-Ss"])] + Search(SearchArgs), + + /// Queries installed packages + #[clap(name="query", aliases=&["ls", "-Q"])] + Query(QueryArgs), + + /// Upgrades locally installed packages to their latest versions + #[clap(name="upgrade", aliases=&["upg", "-Syu"])] + Upgrade, +} + +impl Default for Operation { + fn default() -> Self { + Self::Upgrade + } +} + +#[derive(Default, Debug, Clone, Parser)] +pub struct InstallArgs { + /// The name of the package(s) to install + #[clap(required = true)] + pub packages: Vec, +} + +#[derive(Default, Debug, Clone, Parser)] +pub struct RemoveArgs { + /// The name of the package(s) to remove + #[clap(required = true)] + pub packages: Vec, +} + +#[derive(Default, Debug, Clone, Parser)] +pub struct SearchArgs { + /// Searches for the relevant packages in both the AUR and repos + #[clap(long, short)] + pub aur: bool, + + /// Searches only local repos for the package + #[clap(long, short)] + pub repo: bool, + + /// The string the package must match in the search + #[clap(required = true)] + pub search: Vec, +} + +#[derive(Default, Debug, Clone, Parser)] +pub struct QueryArgs { + /// Lists AUR/foreign packages + #[clap(long, short)] + pub aur: bool, + + /// Lists repo/native packages + #[clap(long, short)] + pub repo: bool, +} diff --git a/src/main.rs b/src/main.rs index 0953eef..24b1c28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,16 @@ -use std::process::{exit, Command}; -use std::{env, io, process}; +use clap::Parser; +use std::process; +use std::process::Command; -use clap::{App, AppSettings, Arg, ArgMatches, ArgSettings, Shell, SubCommand}; +use crate::args::{InstallArgs, Operation, QueryArgs, RemoveArgs, SearchArgs}; +use args::Args; use crate::internal::{crash, info, init, log, sort, structs::Options}; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; +mod args; mod database; mod internal; mod operations; @@ -21,118 +24,10 @@ fn main() { crash("Running amethyst as root is disallowed as it can lead to system breakage. Instead, amethyst will prompt you when it needs superuser permissions".to_string(), 1); } - fn build_app() -> App<'static, 'static> { - let app = App::new("Amethyst") - .version(env!("CARGO_PKG_VERSION")) - .about(env!("CARGO_PKG_DESCRIPTION")) - .arg( - Arg::with_name("verbose") - .short("v") - .long("verbose") - .multiple(true) - .set(ArgSettings::Global) - .help("Sets the level of verbosity"), - ) - .arg( - Arg::with_name("noconfirm") - .long("noconfirm") - .set(ArgSettings::Global) - .help("Complete operation without prompting user"), - ) - .subcommand( - SubCommand::with_name("install") - .about( - "Installs a package from either the AUR or the PacMan-defined repositories", - ) - .aliases(&["-S", "ins"]) - .arg( - Arg::with_name("package(s)") - .help("The name of the package(s) to install") - .required(true) - .multiple(true) - .index(1), - ), - ) - .subcommand( - SubCommand::with_name("remove") - .about("Removes a previously installed package") - .aliases(&["-R", "-Rs", "rm"]) - .arg( - Arg::with_name("package(s)") - .help("The name of the package(s) to remove") - .required(true) - .multiple(true) - .index(1), - ), - ) - .subcommand( - SubCommand::with_name("search") - .about("Searches for the relevant packages in both the AUR and repos") - .aliases(&["-Ss", "sea"]) - .arg( - Arg::with_name("aur") - .short("a") - .long("aur") - .help("Search only the AUR for the package"), - ) - .arg( - Arg::with_name("repo") - .short("r") - .long("repo") - .help("Searches only local repos for the package"), - ) - .arg( - Arg::with_name("package(s)") - .help("The name of the package to search for") - .required(true) - .multiple(false) - .index(1), - ), - ) - .subcommand( - SubCommand::with_name("query") - .about("Queries installed packages") - .aliases(&["-Q", "ls"]) - .arg( - Arg::with_name("aur") - .short("a") - .help("Lists AUR/foreign packages"), - ) - .arg( - Arg::with_name("repo") - .short("r") - .help("Lists repo/native packages"), - ), - ) - .subcommand( - SubCommand::with_name("upgrade") - .about("Upgrades locally installed packages to their latest versions") - .aliases(&["-Syu", "upg"]), - ) - .subcommand( - SubCommand::with_name("compgen") - .about("Generates shell completions for given shell (bash by default)") - .aliases(&["-G", "cg"]) - .arg( - Arg::with_name("shell") - .help("The name of the shell you want to generate completions for") - .possible_values(&["bash", "fish", "zsh", "pwsh", "elvish"]) - .required(true), - ), - ) - .settings(&[ - AppSettings::GlobalVersion, - AppSettings::VersionlessSubcommands, - AppSettings::ArgRequiredElseHelp, - AppSettings::InferSubcommands, - ]); - app - } - - let matches = build_app().get_matches(); + let args: Args = Args::parse(); - let verbosity: i32 = matches.occurrences_of("verbose") as i32; - let noconfirm: bool = matches.is_present("noconfirm"); + let verbosity = args.verbose as i32; + let noconfirm = args.no_confirm; let options = Options { verbosity, @@ -142,174 +37,104 @@ fn main() { init(options); - fn collect_matches(a: &ArgMatches) -> Vec { - a.subcommand() - .1 - .unwrap() - .values_of("package(s)") - .unwrap() - .into_iter() - .map(|s| s.to_string()) - .collect() + match args.subcommand.unwrap_or_default() { + Operation::Install(install_args) => cmd_install(install_args, options), + Operation::Remove(remove_args) => cmd_remove(remove_args, options), + Operation::Search(search_args) => cmd_search(search_args, options), + Operation::Query(query_args) => cmd_query(query_args), + Operation::Upgrade => { + info("Performing system upgrade".to_string()); + operations::upgrade(options); + } } +} - if let true = matches.is_present("install") { - let packages = collect_matches(&matches); - let sorted = sort(&packages, options); +fn cmd_install(args: InstallArgs, options: Options) { + let packages = args.packages; + let sorted = sort(&packages, options); - info(format!( - "Attempting to install packages: {}", - packages.join(", ") + info(format!( + "Attempting to install packages: {}", + packages.join(", ") + )); + + if !sorted.repo.is_empty() { + operations::install(sorted.repo, options); + } + if !sorted.aur.is_empty() { + operations::aur_install(sorted.aur, options); + } + if !sorted.nf.is_empty() { + log(format!( + "Couldn't find packages: {} in repos or the AUR", + sorted.nf.join(", ") )); + } - if !sorted.repo.is_empty() { - operations::install(sorted.repo, options); - } - if !sorted.aur.is_empty() { - operations::aur_install(sorted.aur, options); - } - if !sorted.nf.is_empty() { - log(format!( - "Couldn't find packages: {} in repos or the AUR", - sorted.nf.join(", ") - )); - } + let out = process::Command::new("bash") + .args(&["-c", "sudo find /etc -name *.pacnew"]) + .output() + .expect("Something has gone wrong") + .stdout; - let out = process::Command::new("bash") - .args(&["-c", "sudo find /etc -name *.pacnew"]) - .output() - .expect("Something has gone wrong") - .stdout; + if !String::from_utf8((*out).to_owned()).unwrap().is_empty() { + info(format!("You have .pacnew files in /etc ({}) that you haven't removed or acted upon, it is recommended you do that now", String::from_utf8((*out).to_owned()).unwrap().split_whitespace().collect::>().join(", "))); + } +} - if !String::from_utf8((*out).to_owned()).unwrap().is_empty() { - info(format!("You have .pacnew files in /etc ({}) that you haven't removed or acted upon, it is recommended you do that now", String::from_utf8((*out).to_owned()).unwrap().split_whitespace().collect::>().join(", "))); - } +fn cmd_remove(args: RemoveArgs, options: Options) { + let packages = args.packages; + info(format!("Uninstalling packages: {}", &packages.join(", "))); + operations::uninstall(packages, options); +} - exit(0); +fn cmd_search(args: SearchArgs, options: Options) { + let query_string = args.search.join(" "); + if args.aur { + info(format!("Searching AUR for {}", &query_string)); + operations::aur_search(&query_string, options); } - - if let true = matches.is_present("remove") { - let packages = collect_matches(&matches); - info(format!("Uninstalling packages: {}", &packages.join(", "))); - operations::uninstall(packages, options); - exit(0); + if args.repo { + info(format!("Searching repos for {}", &query_string)); + operations::search(&query_string, options); } - if let true = matches.is_present("upgrade") { - info("Performing system upgrade".to_string()); - operations::upgrade(options); - exit(0); + if !args.aur && !args.repo { + info(format!("Searching AUR and repos for {}", &query_string)); + operations::search(&query_string, options); + operations::aur_search(&query_string, options); } +} - if let true = matches.is_present("search") { - let packages = collect_matches(&matches); - if matches - .subcommand_matches("search") - .unwrap() - .is_present("aur") - { - info(format!("Searching AUR for {}", &packages[0])); - operations::aur_search(&packages[0], options); - } - if matches - .subcommand_matches("search") - .unwrap() - .is_present("repo") - { - info(format!("Searching repos for {}", &packages[0])); - operations::search(&packages[0], options); - } - - if !matches - .subcommand_matches("search") - .unwrap() - .is_present("repo") - && !matches - .subcommand_matches("search") - .unwrap() - .is_present("aur") - { - info(format!("Searching AUR and repos for {}", &packages[0])); - operations::search(&packages[0], options); - operations::aur_search(&packages[0], options); - } - exit(0); +fn cmd_query(args: QueryArgs) { + if args.aur { + Command::new("pacman") + .arg("-Qm") + .spawn() + .expect("Something has gone wrong") + .wait() + .unwrap(); } - - if let true = matches.is_present("query") { - if matches - .subcommand_matches("query") - .unwrap() - .is_present("aur") - { - Command::new("pacman") - .arg("-Qm") - .spawn() - .expect("Something has gone wrong") - .wait() - .unwrap(); - } - if matches - .subcommand_matches("query") - .unwrap() - .is_present("repo") - { - Command::new("pacman") - .arg("-Qn") - .spawn() - .expect("Something has gone wrong") - .wait() - .unwrap(); - } - if !matches - .subcommand_matches("query") - .unwrap() - .is_present("aur") - && !matches - .subcommand_matches("query") - .unwrap() - .is_present("repo") - { - Command::new("pacman") - .arg("-Qn") - .spawn() - .expect("Something has gone wrong") - .wait() - .unwrap(); - Command::new("pacman") - .arg("-Qm") - .spawn() - .expect("Something has gone wrong") - .wait() - .unwrap(); - } - exit(0); + if args.repo { + Command::new("pacman") + .arg("-Qn") + .spawn() + .expect("Something has gone wrong") + .wait() + .unwrap(); } - - if let true = &matches.is_present("compgen") { - let mut app = build_app(); - match matches - .subcommand_matches("compgen") - .unwrap() - .value_of("shell") - .unwrap() - { - "bash" => { - app.gen_completions_to("ame", Shell::Bash, &mut io::stdout()); - } - "fish" => { - app.gen_completions_to("ame", Shell::Fish, &mut io::stdout()); - } - "zsh" => { - app.gen_completions_to("ame", Shell::Zsh, &mut io::stdout()); - } - "pwsh" => { - app.gen_completions_to("ame", Shell::PowerShell, &mut io::stdout()); - } - "elvish" => { - app.gen_completions_to("ame", Shell::Elvish, &mut io::stdout()); - } - _ => {} - } + if !args.repo && !args.aur { + Command::new("pacman") + .arg("-Qn") + .spawn() + .expect("Something has gone wrong") + .wait() + .unwrap(); + Command::new("pacman") + .arg("-Qm") + .spawn() + .expect("Something has gone wrong") + .wait() + .unwrap(); } } From 2d797c9e9afaebff78cd43322c2f135f37331a96 Mon Sep 17 00:00:00 2001 From: Trivernis Date: Sat, 16 Apr 2022 20:40:42 +0200 Subject: [PATCH 2/4] Remove clap_complete as it currently isn't used Signed-off-by: Trivernis --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8bab9ae..0ef869c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,6 @@ codegen-units = 1 [dependencies] mimalloc = { version = "0.1.27", default-features = false } clap = { version = "3.1.9", features = [ "derive", "wrap_help"] } -clap_complete = "3.1.1" regex = { version = "1.5.4", default-features = false, features = ["std", "unicode-perl"] } runas = "0.2.1" rusqlite = { version = "0.26.3", default-features = false } From f4ac74909a504ca6ab70007cd166169a14dd6acf Mon Sep 17 00:00:00 2001 From: Trivernis Date: Sat, 16 Apr 2022 20:47:41 +0200 Subject: [PATCH 3/4] Fix missing dash on -Rs alias to Remove operation Signed-off-by: Trivernis --- src/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/args.rs b/src/args.rs index 30de242..93a7fe7 100644 --- a/src/args.rs +++ b/src/args.rs @@ -22,7 +22,7 @@ pub enum Operation { Install(InstallArgs), /// Removes a previously installed package - #[clap(name="remove", aliases=&["rm", "-R", "Rs"])] + #[clap(name="remove", aliases=&["rm", "-R", "-Rs"])] Remove(RemoveArgs), /// Searches for the relevant packages in both the AUR and repos From 247659b7baa97bcdb37d1c6cf36a8fdb7541bba2 Mon Sep 17 00:00:00 2001 From: Trivernis Date: Sat, 16 Apr 2022 21:08:16 +0200 Subject: [PATCH 4/4] Replace redefinitions in mod.rs with reexports of submodule functions Signed-off-by: Trivernis --- src/database/mod.rs | 22 ++++------------------ src/internal/mod.rs | 33 ++++----------------------------- src/operations/mod.rs | 30 +++++------------------------- 3 files changed, 13 insertions(+), 72 deletions(-) diff --git a/src/database/mod.rs b/src/database/mod.rs index 0e79ed6..705843c 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -1,23 +1,9 @@ -use crate::internal::rpc::Package; -use crate::Options; - mod add; mod initialise; mod query; mod remove; -pub fn add(a: Package, options: Options) { - add::add(a, options); -} - -pub fn remove(a: &str, options: Options) { - remove::remove(a, options); -} - -pub fn init(options: Options) { - initialise::init(options); -} - -pub fn query(options: Options) -> Vec { - query::query(options) -} +pub use add::*; +pub use initialise::*; +pub use query::*; +pub use remove::*; diff --git a/src/internal/mod.rs b/src/internal/mod.rs index 06c7006..04b9645 100644 --- a/src/internal/mod.rs +++ b/src/internal/mod.rs @@ -1,5 +1,3 @@ -use crate::Options; - mod clean; mod initialise; pub mod rpc; @@ -7,33 +5,10 @@ mod sort; mod strings; pub mod structs; -pub fn sort(a: &[String], options: Options) -> structs::Sorted { - sort::sort(a, options) -} - -pub fn clean(a: &[String], options: Options) -> Vec { - clean::clean(a, options) -} - -pub fn init(options: Options) { - initialise::init(options); -} - -pub fn info(a: String) { - strings::info(a); -} - -pub fn crash(a: String, b: i32) { - strings::crash(a, b); -} - -pub fn log(a: String) { - strings::log(a); -} - -pub fn prompt(a: String, b: bool) -> bool { - strings::prompt(a, b) -} +pub use clean::*; +pub use initialise::*; +pub use sort::*; +pub use strings::*; #[macro_export] macro_rules! uwu { diff --git a/src/operations/mod.rs b/src/operations/mod.rs index cb6f241..7782641 100644 --- a/src/operations/mod.rs +++ b/src/operations/mod.rs @@ -1,31 +1,11 @@ -use crate::Options; - mod aur_install; mod install; mod search; mod uninstall; mod upgrade; -pub fn install(a: Vec, options: Options) { - install::install(a, options); -} - -pub fn uninstall(a: Vec, options: Options) { - uninstall::uninstall(a, options); -} - -pub fn search(a: &str, options: Options) { - search::repo_search(a, options); -} - -pub fn aur_install(a: Vec, options: Options) { - aur_install::aur_install(a, options); -} - -pub fn aur_search(a: &str, options: Options) { - search::aur_search(a, options); -} - -pub fn upgrade(options: Options) { - upgrade::upgrade(options); -} +pub use aur_install::*; +pub use install::*; +pub use search::{aur_search, repo_search as search}; +pub use uninstall::*; +pub use upgrade::*;