diff --git a/Cargo.toml b/Cargo.toml index 84e6926..9398ff4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "ame" version = "3.0.0" authors = [ "jnats ", "axtlos " ] -edition = "2018" +edition = "2021" description = "A fast and efficient aur helper." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,5 +10,6 @@ description = "A fast and efficient aur helper." [dependencies] clap = "2.34.0" regex = "1.5.4" +runas = "0.2.1" reqwest = { version = "0.11.7", default-features = false, features = [ "blocking", "json", "default-tls" ] } -serde = { version = "1.0.90", default-features = false, features = [ "derive" ] } \ No newline at end of file +serde = { version = "1.0.90", default-features = false, features = [ "derive", "serde_derive" ] } \ No newline at end of file diff --git a/src/internal/clean.rs b/src/internal/clean.rs index 5df6056..a6bf46a 100644 --- a/src/internal/clean.rs +++ b/src/internal/clean.rs @@ -1,9 +1,10 @@ +use crate::Options; use regex::Regex; -pub fn clean(a: &[String], verbosity: i32) -> Vec { +pub fn clean(a: &[String], options: Options) -> Vec { let r = Regex::new(r"(\S+)((?:>=|<=|>|<)\S+$)").unwrap(); - let mut cleaned: Vec = vec![]; + let verbosity = options.verbosity; for b in a { if r.captures_iter(b).count() > 0 { diff --git a/src/internal/mod.rs b/src/internal/mod.rs index 27f2af2..641289b 100644 --- a/src/internal/mod.rs +++ b/src/internal/mod.rs @@ -1,12 +1,14 @@ +use crate::Options; + mod clean; pub mod rpc; mod sort; pub mod structs; -pub fn sort(a: &[String], verbosity: i32) -> structs::Sorted { - sort::sort(a, verbosity) +pub fn sort(a: &[String], options: Options) -> structs::Sorted { + sort::sort(a, options) } -pub fn clean(a: &[String], verbosity: i32) -> Vec { - clean::clean(a, verbosity) +pub fn clean(a: &[String], options: Options) -> Vec { + clean::clean(a, options) } diff --git a/src/internal/sort.rs b/src/internal/sort.rs index 4b87157..d8050d8 100644 --- a/src/internal/sort.rs +++ b/src/internal/sort.rs @@ -1,13 +1,14 @@ use crate::internal::{clean, rpc, structs}; +use crate::Options; use std::process::{Command, Stdio}; -pub fn sort(input: &[String], verbosity: i32) -> structs::Sorted { - #[allow(unused_mut)] +pub fn sort(input: &[String], options: Options) -> structs::Sorted { let mut repo: Vec = vec![]; let mut aur: Vec = vec![]; let mut nf: Vec = vec![]; + let verbosity = options.verbosity; - let a = clean(input, verbosity); + let a = clean(input, options); match verbosity { 0 => {} diff --git a/src/internal/structs.rs b/src/internal/structs.rs index 78ff67b..39c2a3e 100644 --- a/src/internal/structs.rs +++ b/src/internal/structs.rs @@ -1,20 +1,33 @@ -#[derive(Debug)] +#[derive(Debug, serde::Serialize)] pub struct Sorted { #[allow(dead_code)] pub repo: Vec, #[allow(dead_code)] pub aur: Vec, #[allow(dead_code)] - pub nf: Vec + pub nf: Vec, } impl Sorted { pub fn new(repo: Vec, aur: Vec, nf: Vec) -> Self { - let a: Sorted = Sorted { - repo, - aur, - nf + let a: Sorted = Sorted { repo, aur, nf }; + a + } +} + +#[derive(Clone, Copy)] +pub struct Options { + pub verbosity: i32, + pub noconfirm: bool, +} + +impl Options { + #[allow(dead_code)] + pub fn new(verbosity: i32, noconfirm: bool) -> Self { + let a: Options = Options { + verbosity, + noconfirm, }; a } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 9f22af5..41484dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ mod internal; mod operations; -use crate::internal::sort; -use clap::{App, Arg, SubCommand}; +use crate::internal::{sort, structs::Options}; +use clap::{App, AppSettings, Arg, ArgSettings, SubCommand}; +use std::process::exit; fn main() { let matches = App::new("Amethyst") @@ -11,19 +12,21 @@ fn main() { .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("noconfirm") - .short("y") - .long("noconfirm") - .help("Do not ask for confirmation before installing packages"), - ) .arg( Arg::with_name("package(s)") .help("The name of the package(s) to install") @@ -35,33 +38,57 @@ fn main() { .subcommand( SubCommand::with_name("remove") .about("Removes a previously installed package") - .aliases(&["-R", "rm"]) + .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("noconfirm") - .short("y") - .long("noconfirm") - .help("Do not ask for confirmation before removing packages"), + Arg::with_name("aur") + .short("a") + .long("aur") + .help("Search only the AUR for the package"), ) .arg( - Arg::with_name("recursive") - .short("s") - .long("recursive") - .help("Recursively uninstall orphaned dependencies"), + 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(s) to remove") + .help("The name of the package to search for") .required(true) - .multiple(true) + .multiple(false) .index(1), ), ) + .settings(&[ + AppSettings::GlobalVersion, + AppSettings::VersionlessSubcommands, + AppSettings::ArgRequiredElseHelp, + ]) .get_matches(); let verbosity: i32 = matches.occurrences_of("verbose") as i32; + let noconfirm: bool = matches.is_present("noconfirm"); + + let options = Options { + verbosity, + noconfirm, + }; let packages: Vec = matches - .subcommand_matches("install") + .subcommand() + .1 .unwrap() .values_of("package(s)") .unwrap() @@ -70,13 +97,52 @@ fn main() { .collect(); if let true = matches.is_present("install") { - let sorted = sort(&packages, verbosity); + let sorted = sort(&packages, options); - operations::install(sorted.repo, verbosity); - operations::aur_install(sorted.aur, verbosity); - eprintln!( - "Couldn't find packages: {} in repos or the AUR.", - sorted.nf.join(", ") - ) + operations::install(sorted.repo, options); + operations::aur_install(sorted.aur, options); + if !sorted.nf.is_empty() { + eprintln!( + "Couldn't find packages: {} in repos or the AUR.", + sorted.nf.join(", ") + ); + } + exit(0); + } + + if let true = matches.is_present("remove") { + operations::uninstall(packages, options); + exit(0); + } + + if let true = matches.is_present("search") { + if matches + .subcommand_matches("search") + .unwrap() + .is_present("aur") + { + operations::aur_search(&packages[0], options); + } + if matches + .subcommand_matches("search") + .unwrap() + .is_present("repo") + { + operations::search(&packages[0], options); + } + + if !matches + .subcommand_matches("search") + .unwrap() + .is_present("repo") + && !matches + .subcommand_matches("search") + .unwrap() + .is_present("aur") + { + operations::search(&packages[0], options); + operations::aur_search(&packages[0], options); + } + exit(0); } } diff --git a/src/operations/aur_install.rs b/src/operations/aur_install.rs index 9389007..4f82c10 100644 --- a/src/operations/aur_install.rs +++ b/src/operations/aur_install.rs @@ -1,4 +1,7 @@ -pub fn aur_install(a: Vec, verbosity: i32) { +use crate::Options; + +pub fn aur_install(a: Vec, options: Options) { + let verbosity = options.verbosity; match verbosity { 0 => {} 1 => { diff --git a/src/operations/install.rs b/src/operations/install.rs index b69dd38..115b8a3 100644 --- a/src/operations/install.rs +++ b/src/operations/install.rs @@ -1,15 +1,34 @@ -pub fn install(a: Vec, verbosity: i32) { +use crate::Options; + +pub fn install(mut a: Vec, options: Options) { + let b = a.clone(); + if options.noconfirm { + a.push("--noconfirm".to_string()); + } + let verbosity = options.verbosity; match verbosity { - 0 => {}, + 0 => {} 1 => { eprintln!("Installing from repos:"); - eprintln!("{:?}", &a); + eprintln!("{:?}", &b); } _ => { eprintln!("Installing from repos:"); - for b in a { + for b in &a { eprintln!("{:?}", b); } } } -} \ No newline at end of file + + let r = runas::Command::new("pacman") + .arg("-S") + .args(&a) + .status() + .expect("Something has gone wrong."); + + if let Some(x) = r.code() { + if verbosity >= 1 { + eprintln!("Installing packages: {:?} exited with code {}.", &b, x) + } + } +} diff --git a/src/operations/mod.rs b/src/operations/mod.rs index 632a742..7021e82 100644 --- a/src/operations/mod.rs +++ b/src/operations/mod.rs @@ -1,12 +1,26 @@ +use crate::Options; + mod aur_install; mod install; -mod query; +mod search; mod uninstall; -pub fn install(a: Vec, verbosity: i32) { - install::install(a, verbosity); +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_install(a: Vec, verbosity: i32) { - aur_install::aur_install(a, verbosity); +pub fn aur_search(a: &str, options: Options) { + search::aur_search(a, options); } diff --git a/src/operations/query.rs b/src/operations/query.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/operations/search.rs b/src/operations/search.rs new file mode 100644 index 0000000..28d7a55 --- /dev/null +++ b/src/operations/search.rs @@ -0,0 +1,44 @@ +use crate::internal::rpc::rpcsearch; +use crate::Options; +use std::process::Command; + +pub fn aur_search(a: &str, options: Options) { + let verbosity = options.verbosity; + let res = rpcsearch(a.to_string()); + + if verbosity >= 1 { + eprintln!("Found {} results for \"{}\" in AUR.", res.resultcount, a); + } + + for r in &res.results { + println!( + "aur/{} {}\n {}", + r.name, + r.version, + r.description + .as_ref() + .unwrap_or(&"No description.".to_string()) + ) + } +} + +pub fn repo_search(a: &str, options: Options) { + let verbosity = options.verbosity; + let rs = Command::new("pacman") + .arg("-Ss") + .arg(format!("^{}$", &a)) + .output() + .expect("Something has gone wrong."); + + let str = String::from_utf8(rs.stdout).unwrap(); + + if verbosity >= 1 { + eprintln!( + "Found {} results for \"{}\" in repos.", + &str.split('\n').count() / 2, + &a + ); + } + + print!("{}", str); +} diff --git a/src/operations/uninstall.rs b/src/operations/uninstall.rs index e69de29..7fa4864 100644 --- a/src/operations/uninstall.rs +++ b/src/operations/uninstall.rs @@ -0,0 +1,34 @@ +use crate::Options; + +pub fn uninstall(mut a: Vec, options: Options) { + let b = a.clone(); + if options.noconfirm { + a.push("--noconfirm".to_string()); + } + let verbosity = options.verbosity; + match verbosity { + 0 => {} + 1 => { + eprintln!("Uninstalling:"); + eprintln!("{:?}", &b); + } + _ => { + eprintln!("Uninstalling:"); + for b in &a { + eprintln!("{}", b); + } + } + } + + let r = runas::Command::new("pacman") + .arg("-Rs") + .args(&a) + .status() + .expect("Something has gone wrong."); + + if let Some(x) = r.code() { + if verbosity >= 1 { + eprintln!("Uninstalling packages: {:?} exited with code {}.", &b, x) + } + } +}