diff --git a/Cargo.lock b/Cargo.lock index 7f8776b..4fc82ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,7 @@ name = "Amethyst" version = "3.3.0" dependencies = [ "clap", + "colored", "libc", "mimalloc", "native-tls", @@ -114,6 +115,17 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + [[package]] name = "core-foundation" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index 4b33430..45548c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ mimalloc = { version = "0.1.29", default-features = false } clap = { version = "3.2.8", features = [ "derive", "wrap_help" ] } regex = { version = "1.5.6", default-features = false, features = [ "std", "unicode-perl" ] } rusqlite = { version = "0.26.3", default-features = false } +colored = "2.0.0" ureq = { version = "2.4.0", default-features = false, features = [ "native-tls", "json" ] } serde = { version = "1.0.138", default-features = false, features = [ "derive", "serde_derive" ] } native-tls = { version = "0.2.10", default-features = false } diff --git a/PKGBUILD b/PKGBUILD index 0d2c390..3198600 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -4,14 +4,14 @@ pkgname=amethyst pkgver=3.3.0 -pkgrel=1 +pkgrel=2 pkgdesc="A fast and efficient AUR helper" arch=('x86_64') url="https://github.com/crystal-linux/amethyst" license=('GPL3') source=("git+$url") sha256sums=('SKIP') -depends=('git' 'binutils' 'fakeroot' 'pacman-contrib') +depends=('git' 'binutils' 'fakeroot' 'pacman-contrib' 'vim') makedepends=('cargo') conflicts=('ame') diff --git a/src/args.rs b/src/args.rs index 3d1e667..ac1827c 100644 --- a/src/args.rs +++ b/src/args.rs @@ -13,6 +13,10 @@ pub struct Args { /// Complete operation without prompting user #[clap(long = "noconfirm", global = true)] pub no_confirm: bool, + + /// Loops sudo in the background to ensure it doesn't time out during long builds + #[clap(long = "sudoloop")] + pub sudoloop: bool, } #[derive(Debug, Clone, Subcommand)] @@ -22,7 +26,7 @@ pub enum Operation { Install(InstallArgs), /// Removes a previously installed package - #[clap(name = "remove", aliases = & ["rm", "r", "-R", "-Rs"])] + #[clap(name = "remove", aliases = & ["rm", "rem", "r", "-R", "-Rs"])] Remove(RemoveArgs), /// Searches for the relevant packages in both the AUR and repos @@ -80,10 +84,10 @@ pub struct SearchArgs { #[derive(Default, Debug, Clone, Parser)] pub struct QueryArgs { /// Lists AUR/foreign packages - #[clap(long, short)] + #[clap(long, short, from_global)] pub aur: bool, /// Lists repo/native packages - #[clap(long, short)] + #[clap(long, short, from_global)] pub repo: bool, } diff --git a/src/database/add.rs b/src/database/add.rs index b722099..ebcbf70 100644 --- a/src/database/add.rs +++ b/src/database/add.rs @@ -1,25 +1,21 @@ -use rusqlite::Connection; -use std::env; -use std::path::Path; - use crate::internal::exit_code::AppExitCode; use crate::internal::rpc::Package; use crate::{crash, log, Options}; +use super::get_database_connection; + pub fn add(pkg: Package, options: Options) { - let conn = Connection::open(Path::new(&format!( - "{}/.local/share/ame/db.sqlite", - env::var("HOME").unwrap() - ))) - .expect("Couldn't connect to database"); + let conn = get_database_connection(); if options.verbosity >= 1 { - log(format!("Adding package {} to database", pkg.name)); + log!("Adding package {} to database", pkg.name); } - + let pkg_description = pkg + .description + .unwrap_or_else(|| "No description found.".parse().unwrap()); conn.execute("INSERT OR REPLACE INTO packages (name, version, description, depends, make_depends) VALUES (?1, ?2, ?3, ?4, ?5)", - [&pkg.name, &pkg.version, &pkg.description.unwrap_or_else(|| "No description found.".parse().unwrap()), &pkg.depends.join(" "), &pkg.make_depends.join(" ")], + [&pkg.name, &pkg.version, &pkg_description, &pkg.depends.join(" "), &pkg.make_depends.join(" ")], ).unwrap_or_else(|e| - crash(format!("Failed adding package {} to the database: {}", pkg.name, e), AppExitCode::FailedAddingPkg) + crash!(AppExitCode::FailedAddingPkg, "Failed adding package {} to the database: {}", pkg.name, e) ); } diff --git a/src/database/initialise.rs b/src/database/initialise.rs index b0e272a..e7e1214 100644 --- a/src/database/initialise.rs +++ b/src/database/initialise.rs @@ -11,14 +11,14 @@ pub fn init(options: Options) { let verbosity = options.verbosity; if verbosity >= 1 { - log(format!("Creating database at {}", &path)); + log!("Creating database at {}", &path); } let conn = Connection::open(dbpath).expect("Couldn't create database at ~/.local/share/ame/db.sqlite"); if verbosity >= 1 { - log("Populating database with table".to_string()); + log!("Populating database with table"); } conn.execute( @@ -32,9 +32,10 @@ pub fn init(options: Options) { [], ) .unwrap_or_else(|e| { - crash( - format!("Couldn't initialise database: {}", e), + crash!( AppExitCode::FailedInitDb, + "Couldn't initialise database: {}", + e, ) }); } diff --git a/src/database/mod.rs b/src/database/mod.rs index bbdfd08..4eb9d9a 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -1,9 +1,17 @@ +use std::{env, path::PathBuf}; + +pub mod add; +pub mod initialise; +pub mod query; +pub mod remove; + pub use add::*; pub use initialise::*; pub use query::*; pub use remove::*; +use rusqlite::Connection; -mod add; -mod initialise; -mod query; -mod remove; +fn get_database_connection() -> Connection { + let db_path = format!("{}/.local/share/ame/db.sqlite", env::var("HOME").unwrap()); + Connection::open(PathBuf::from(db_path)).expect("Couldn't connect to database") +} diff --git a/src/database/query.rs b/src/database/query.rs index 112db3a..91e644e 100644 --- a/src/database/query.rs +++ b/src/database/query.rs @@ -9,7 +9,7 @@ pub fn query(options: Options) -> Vec { let verbosity = options.verbosity; if verbosity >= 1 { - log("Connecting to database".to_string()); + log!("Connecting to database"); } let conn = Connection::open(Path::new(&format!( @@ -19,7 +19,7 @@ pub fn query(options: Options) -> Vec { .expect("Couldn't connect to database"); if verbosity >= 1 { - log("Querying database for input".to_string()); + log!("Querying database for input"); } let mut rs = conn.prepare("SELECT * FROM packages;").unwrap(); @@ -46,7 +46,7 @@ pub fn query(options: Options) -> Vec { .expect("Couldn't query database for packages"); if verbosity >= 1 { - log("Retrieved results".to_string()); + log!("Retrieved results"); } let mut results: Vec = vec![]; @@ -56,7 +56,7 @@ pub fn query(options: Options) -> Vec { } if verbosity >= 1 { - log("Collected results".to_string()); + log!("Collected results"); } results diff --git a/src/database/remove.rs b/src/database/remove.rs index 3b5de42..ef1e5da 100644 --- a/src/database/remove.rs +++ b/src/database/remove.rs @@ -1,20 +1,14 @@ -use rusqlite::Connection; -use std::env; -use std::path::Path; - use crate::{log, Options}; +use super::get_database_connection; + pub fn remove(pkg: &str, options: Options) { - let conn = Connection::open(Path::new(&format!( - "{}/.local/share/ame/db.sqlite", - env::var("HOME").unwrap() - ))) - .expect("Couldn't connect to database"); + let conn = get_database_connection(); let verbosity = options.verbosity; if verbosity >= 1 { - log(format!("Removing package {} from database", pkg)); + log!("Removing package {} from database", pkg); } conn.execute( diff --git a/src/internal/clean.rs b/src/internal/clean.rs index f6fbcc5..5d3f0f3 100644 --- a/src/internal/clean.rs +++ b/src/internal/clean.rs @@ -1,7 +1,6 @@ use regex::Regex; -use crate::internal::strings::log; -use crate::Options; +use crate::{log, Options}; pub fn clean(a: &[String], options: Options) -> Vec { let r = Regex::new(r"(\S+)((?:>=|<=|>|<)\S+$)").unwrap(); @@ -18,7 +17,7 @@ pub fn clean(a: &[String], options: Options) -> Vec { } if verbosity >= 1 { - log(format!("Cleaned: {:?}\nInto: {:?}", a, cleaned)); + log!("Cleaned: {:?}\nInto: {:?}", a, cleaned); } cleaned diff --git a/src/internal/commands.rs b/src/internal/commands.rs index 660e314..f2df391 100644 --- a/src/internal/commands.rs +++ b/src/internal/commands.rs @@ -45,6 +45,10 @@ impl ShellCommand { Self::new("bash") } + pub fn sudo() -> Self { + Self::new("sudo") + } + fn new(command: S) -> Self { Self { command: command.to_string(), diff --git a/src/internal/detect.rs b/src/internal/detect.rs index ba3f1b7..2f64a28 100644 --- a/src/internal/detect.rs +++ b/src/internal/detect.rs @@ -1,7 +1,7 @@ use crate::internal::commands::ShellCommand; use crate::internal::error::SilentUnwrap; use crate::internal::exit_code::AppExitCode; -use crate::internal::strings::{prompt, warn}; +use crate::{prompt, warn}; pub fn detect() { let mut pacnew = vec![]; @@ -16,10 +16,10 @@ pub fn detect() { } if !pacnew.is_empty() { - let choice = prompt("It appears that at least one program you have installed / upgraded has installed a .pacnew/.pacsave config file. Would you like to run pacdiff to deal with this? You can always deal with this later by running `sudo pacdiff`".to_string(), false); + let choice = prompt!(default false, "It appears that at least one program you have installed / upgraded has installed a .pacnew/.pacsave config file. Would you like to run pacdiff to deal with this? You can always deal with this later by running `sudo pacdiff`"); if choice { - warn("Unless you've set an alternative using the DIFFPROG environment variable, pacdiff uses `vimdiff` by default to edit files for merging. Make sure you know how to exit vim before proceeding".to_string()); - let cont = prompt("Continue?".to_string(), false); + warn!("Unless you've set an alternative using the DIFFPROG environment variable, pacdiff uses `vimdiff` by default to edit files for merging. Make sure you know how to exit vim before proceeding"); + let cont = prompt!(default false, "Continue?"); if cont { ShellCommand::pacdiff() .elevated() diff --git a/src/internal/error.rs b/src/internal/error.rs index 957603c..99977ad 100644 --- a/src/internal/error.rs +++ b/src/internal/error.rs @@ -1,9 +1,9 @@ +use crate::internal::exit_code::AppExitCode; use std::error::Error; use std::fmt::{Debug, Display, Formatter}; use std::io; use crate::crash; -use crate::internal::exit_code::AppExitCode; pub type AppResult = Result; @@ -52,7 +52,7 @@ impl SilentUnwrap for AppResult { fn silent_unwrap(self, exit_code: AppExitCode) -> T { match self { Ok(val) => val, - Err(_) => crash("an error occurred", exit_code), + Err(_) => crash!(exit_code, "an error occurred"), } } } diff --git a/src/internal/initialise.rs b/src/internal/initialise.rs index 71a9df5..ae62244 100644 --- a/src/internal/initialise.rs +++ b/src/internal/initialise.rs @@ -2,9 +2,7 @@ use std::env; use std::path::Path; use std::process::Command; -use crate::internal::exit_code::AppExitCode; -use crate::internal::strings::{crash, log}; -use crate::Options; +use crate::{crash, internal::exit_code::AppExitCode, log, Options}; pub fn init(options: Options) { let verbosity = options.verbosity; @@ -15,13 +13,15 @@ pub fn init(options: Options) { match r { Ok(_) => { if verbosity >= 1 { - log(format!("Created path: {}/.local/share/ame", homedir)); + log!("Created path: {}/.local/share/ame", homedir); } } Err(e) => { - crash( - format!("Couldn't create path: {}/.local/share/ame: {}", homedir, e), + crash!( AppExitCode::FailedCreatingPaths, + "Couldn't create path: {}/.local/share/ame: {}", + homedir, + e, ); } } @@ -36,13 +36,15 @@ pub fn init(options: Options) { match r { Ok(_) => { if verbosity >= 1 { - log(format!("Created path: {}/.cache/ame", homedir)); + log!("Created path: {}/.cache/ame", homedir); } } Err(e) => { - crash( - format!("Couldn't create path: {}/.cache/ame: {}", homedir, e), + crash!( AppExitCode::FailedCreatingPaths, + "Couldn't create path: {}/.cache/ame: {}", + homedir, + e, ); } } @@ -51,13 +53,15 @@ pub fn init(options: Options) { match r { Ok(_) => { if verbosity >= 1 { - log(format!("Removing cache: {}/.cache/ame", homedir)); + log!("Removing cache: {}/.cache/ame", homedir); } } Err(e) => { - crash( - format!("Couldn't remove path: {}/.cache/ame: {}", homedir, e), + crash!( AppExitCode::FailedCreatingPaths, + "Couldn't remove path: {}/.cache/ame: {}", + homedir, + e, ); } } @@ -65,13 +69,15 @@ pub fn init(options: Options) { match r2 { Ok(_) => { if verbosity >= 1 { - log(format!("Created path: {}/.cache/ame", homedir)); + log!("Created path: {}/.cache/ame", homedir); } } Err(e2) => { - crash( - format!("Couldn't create path: {}/.cache/ame: {}", homedir, e2), + crash!( AppExitCode::FailedCreatingPaths, + "Couldn't create path: {}/.cache/ame: {}", + homedir, + e2, ); } } @@ -85,19 +91,15 @@ pub fn init(options: Options) { match r { Ok(_) => { if verbosity >= 1 { - log(format!( - "Set correct permissions for path: {}/.cache/ame", - homedir - )); + log!("Set correct permissions for path: {}/.cache/ame", homedir); } } Err(e) => { - crash( - format!( - "Couldn't set permissions for path: {}/.cache/ame: {}", - homedir, e - ), + crash!( AppExitCode::FailedCreatingPaths, + "Couldn't set permissions for path: {}/.cache/ame: {}", + homedir, + e, ); } }; @@ -109,19 +111,18 @@ pub fn init(options: Options) { match r { Ok(_) => { if verbosity >= 1 { - log(format!( + log!( "Set correct permissions for path: {}/.local/share/ame", homedir - )); + ); } } Err(e) => { - crash( - format!( - "Couldn't set permissions for path: {}/.local/share/ame: {}", - homedir, e - ), + crash!( AppExitCode::FailedCreatingPaths, + "Couldn't set permissions for path: {}/.local/share/ame: {}", + homedir, + e, ); } }; diff --git a/src/internal/mod.rs b/src/internal/mod.rs index f0a69f6..87befd6 100644 --- a/src/internal/mod.rs +++ b/src/internal/mod.rs @@ -3,7 +3,6 @@ pub use detect::*; pub use initialise::*; pub use sort::*; use std::env; -pub use strings::*; mod clean; pub mod commands; @@ -13,8 +12,15 @@ pub mod exit_code; mod initialise; pub mod rpc; mod sort; -mod strings; pub mod structs; +#[macro_use] +pub(crate) mod utils; +mod sudoloop; + +pub use clean::*; +pub use initialise::*; +pub use sort::*; +pub use sudoloop::*; #[macro_export] macro_rules! uwu { diff --git a/src/internal/sort.rs b/src/internal/sort.rs index 93b6896..d64747c 100644 --- a/src/internal/sort.rs +++ b/src/internal/sort.rs @@ -1,8 +1,7 @@ use std::process::{Command, Stdio}; -use crate::internal::strings::log; use crate::internal::{clean, rpc, structs}; -use crate::Options; +use crate::{log, Options}; pub fn sort(input: &[String], options: Options) -> structs::Sorted { let mut repo: Vec = vec![]; @@ -13,7 +12,7 @@ pub fn sort(input: &[String], options: Options) -> structs::Sorted { let a = clean(input, options); if verbosity >= 1 { - log(format!("Sorting: {:?}", a.join(" "))); + log!("Sorting: {:?}", a.join(" ")); } for b in a { @@ -26,17 +25,17 @@ pub fn sort(input: &[String], options: Options) -> structs::Sorted { if let Some(0) = rs.code() { if verbosity >= 1 { - log(format!("{} found in repos", b)); + log!("{} found in repos", b); } repo.push(b.to_string()); } else if rpc::rpcinfo(b.to_string()).found { if verbosity >= 1 { - log(format!("{} found in AUR", b)); + log!("{} found in AUR", b); } aur.push(b.to_string()); } else { if verbosity >= 1 { - log(format!("{} not found", b)); + log!("{} not found", b); } nf.push(b.to_string()); } diff --git a/src/internal/strings.rs b/src/internal/strings.rs deleted file mode 100644 index ea48672..0000000 --- a/src/internal/strings.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::io; -use std::io::Write; -use std::process::exit; -use std::time::UNIX_EPOCH; - -use crate::internal::exit_code::AppExitCode; -use crate::{internal, uwu}; - -pub fn info(msg: S) { - let a = msg.to_string(); - let a = if internal::uwu_enabled() { uwu!(&a) } else { a }; - - println!("\x1b[2;22;35m❖\x1b[0m \x1b[1;37m{}\x1b[0m", a) -} - -pub fn warn(msg: S) { - let a = msg.to_string(); - let a = if internal::uwu_enabled() { uwu!(&a) } else { a }; - - println!("\x1b[2;22;33m!\x1b[0m \x1b[1;37m{}\x1b[0m", a) -} - -pub fn crash(msg: S, exit_code: AppExitCode) -> ! { - let a = msg.to_string(); - let a = if internal::uwu_enabled() { uwu!(&a) } else { a }; - - println!("\x1b[2;22;31m❌:\x1b[0m \x1b[1;91m{}\x1b[0m", a); - exit(exit_code as i32); -} - -pub fn log(msg: S) { - let a = msg.to_string(); - let a = if internal::uwu_enabled() && internal::uwu_debug_enabled() { - uwu!(&a) - } else { - a - }; - - eprintln!( - "{} {}", - std::time::SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(), - a - ); -} - -pub fn prompt(a: S, b: bool) -> bool { - let a = a.to_string(); - let default = ["[Y/n]", "[y/N]"]; - let i = if b { 0 } else { 1 }; - - let a = if internal::uwu_enabled() { uwu!(&a) } else { a }; - - print!( - "\x1b[2;22;35m?\x1b[0m \x1b[1;37m{}\x1b[0m \x1b[2;22;37m{}\x1b[0m: ", - a, default[i] - ); - - let mut yn: String = String::new(); - - io::stdout().flush().ok(); - let _ = std::io::stdin().read_line(&mut yn); - - if yn.trim().to_lowercase() == "n" || yn.trim().to_lowercase() == "no" { - false - } else if yn.trim().to_lowercase() == "y" || yn.trim().to_lowercase() == "yes" { - true - } else { - b - } -} diff --git a/src/internal/sudoloop.rs b/src/internal/sudoloop.rs new file mode 100644 index 0000000..a4f6b3a --- /dev/null +++ b/src/internal/sudoloop.rs @@ -0,0 +1,16 @@ +use crate::ShellCommand; +use std::thread; +use std::time::Duration; + +/// Loop sudo so it doesn't time out +pub fn start_sudoloop() { + prompt_sudo(); + std::thread::spawn(|| loop { + prompt_sudo(); + thread::sleep(Duration::from_secs(3 * 60)) + }); +} + +fn prompt_sudo() { + while ShellCommand::sudo().arg("-v").wait_success().is_err() {} +} diff --git a/src/internal/utils.rs b/src/internal/utils.rs new file mode 100644 index 0000000..c96485a --- /dev/null +++ b/src/internal/utils.rs @@ -0,0 +1,139 @@ +use std::io; +use std::io::Write; +use std::process::exit; +use std::time::UNIX_EPOCH; + +use crate::internal::exit_code::AppExitCode; +use crate::{internal, uwu}; +use colored::*; + +const OK_SYMBOL: &str = "❖"; +const ERR_SYMBOL: &str = "❌"; +const WARN_SYMBOL: &str = "!"; +const PROMPT_SYMBOL: &str = "?"; + +const PROMPT_YN_DEFAULT_TRUE: &str = "[Y/n]"; +const PROMPT_YN_DEFAULT_FALSE: &str = "[y/N]"; + +#[macro_export] +macro_rules! info { + ($($arg:tt)+) => { + $crate::internal::utils::log_info(format!($($arg)+)) + } +} + +#[macro_export] +macro_rules! warn { + ($($arg:tt)+) => { + $crate::internal::utils::log_warn(format!($($arg)+)) + } +} + +#[macro_export] +macro_rules! crash { + ($exit_code:expr, $($arg:tt)+) => { + $crate::internal::utils::log_and_crash(format!($($arg)+), $exit_code) + } +} + +#[macro_export] +macro_rules! log { + ($($arg:tt)+) => { + $crate::internal::utils::log_debug(format!($($arg)+)) + } +} + +#[macro_export] +macro_rules! prompt { + (default $default:expr, $($arg:tt)+) => { + $crate::internal::utils::prompt_yn(format!($($arg)+), $default) + } +} + +pub fn log_info(msg: S) { + let msg = msg.to_string(); + let msg = if internal::uwu_enabled() { + uwu!(&msg) + } else { + msg + }; + + println!("{} {}", OK_SYMBOL.purple(), msg.bold()) +} + +pub fn log_warn(msg: S) { + let msg = msg.to_string(); + let msg = if internal::uwu_enabled() { + uwu!(&msg) + } else { + msg + }; + + println!("{} {}", WARN_SYMBOL.yellow(), msg.bold()) +} + +pub fn log_and_crash(msg: S, exit_code: AppExitCode) -> ! { + let msg = msg.to_string(); + let msg = if internal::uwu_enabled() { + uwu!(&msg) + } else { + msg + }; + + println!("{}: {}", ERR_SYMBOL.red().bold(), msg.red().bold()); + exit(exit_code as i32); +} + +pub fn log_debug(msg: S) { + let msg = msg.to_string(); + let msg = if internal::uwu_enabled() && internal::uwu_debug_enabled() { + uwu!(&msg) + } else { + msg + }; + + eprintln!( + "{} {}", + std::time::SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(), + msg + ); +} + +pub fn prompt_yn(question: S, default_true: bool) -> bool { + let question = question.to_string(); + + let yn_prompt = if default_true { + PROMPT_YN_DEFAULT_TRUE + } else { + PROMPT_YN_DEFAULT_FALSE + }; + + let question = if internal::uwu_enabled() { + uwu!(&question) + } else { + question + }; + + print!( + "{} {} {}: ", + PROMPT_SYMBOL.purple(), + question.bold(), + yn_prompt + ); + + let mut yn: String = String::new(); + + io::stdout().flush().ok(); + let _ = std::io::stdin().read_line(&mut yn); + + if yn.trim().to_lowercase() == "n" || yn.trim().to_lowercase() == "no" { + false + } else if yn.trim().to_lowercase() == "y" || yn.trim().to_lowercase() == "yes" { + true + } else { + default_true + } +} diff --git a/src/main.rs b/src/main.rs index b163739..ee61b35 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use internal::error::SilentUnwrap; use crate::args::{InstallArgs, Operation, QueryArgs, RemoveArgs, SearchArgs}; use crate::internal::detect; use crate::internal::exit_code::AppExitCode; -use crate::internal::{crash, info, init, log, prompt, sort, structs::Options}; +use crate::internal::{init, sort, start_sudoloop, structs::Options}; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; @@ -18,7 +18,7 @@ mod operations; fn main() { if unsafe { libc::geteuid() } == 0 { - 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(), AppExitCode::RunAsRoot); + crash!( AppExitCode::RunAsRoot, "Running amethyst as root is disallowed as it can lead to system breakage. Instead, amethyst will prompt you when it needs superuser permissions"); } let args: Args = Args::parse(); @@ -34,17 +34,21 @@ fn main() { init(options); + if args.sudoloop { + start_sudoloop(); + } + 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()); + info!("Performing system upgrade"); operations::upgrade(options); } Operation::Clean => { - info("Removing orphaned packages".to_string()); + info!("Removing orphaned packages"); operations::clean(options); } } @@ -56,10 +60,7 @@ 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!("Attempting to install packages: {}", packages.join(", ")); if !sorted.repo.is_empty() { operations::install(sorted.repo, options); @@ -68,35 +69,48 @@ fn cmd_install(args: InstallArgs, options: Options) { operations::aur_install(sorted.aur, options); } if !sorted.nf.is_empty() { - crash( - format!( - "Couldn't find packages: {} in repos or the AUR", - sorted.nf.join(", ") - ), + crash!( AppExitCode::PacmanError, + "Couldn't find packages: {} in repos or the AUR", + sorted.nf.join(", ") ); } + + let bash_output = ShellCommand::bash() + .arg("-c") + .arg("sudo find /etc -name *.pacnew") + .wait_with_output() + .silent_unwrap(AppExitCode::Other) + .stdout; + + if !bash_output.is_empty() { + let pacnew_files = bash_output + .split_whitespace() + .collect::>() + .join(", "); + info!("You have .pacnew files in /etc ({pacnew_files}) that you haven't removed or acted upon, it is recommended you do that now" ); + } } fn cmd_remove(args: RemoveArgs, options: Options) { let packages = args.packages; - info(format!("Uninstalling packages: {}", &packages.join(", "))); + info!("Uninstalling packages: {}", &packages.join(", ")); operations::uninstall(packages, options); } fn cmd_search(args: SearchArgs, options: Options) { let query_string = args.search.join(" "); if args.aur { - info(format!("Searching AUR for {}", &query_string)); + info!("Searching AUR for {}", &query_string); operations::aur_search(&query_string, options); } if args.repo { - info(format!("Searching repos for {}", &query_string)); + info!("Searching repos for {}", &query_string); operations::search(&query_string, options); } if !args.aur && !args.repo { - info(format!("Searching AUR and repos for {}", &query_string)); + info!("Searching AUR and repos for {}", &query_string); operations::search(&query_string, options); operations::aur_search(&query_string, options); } diff --git a/src/operations/aur_install.rs b/src/operations/aur_install.rs index 9d1be84..065f443 100644 --- a/src/operations/aur_install.rs +++ b/src/operations/aur_install.rs @@ -8,8 +8,7 @@ use crate::internal::commands::ShellCommand; use crate::internal::error::SilentUnwrap; use crate::internal::exit_code::AppExitCode; use crate::internal::rpc::rpcinfo; -use crate::internal::{crash, prompt}; -use crate::{info, log, Options}; +use crate::{crash, info, log, prompt, Options}; pub fn aur_install(a: Vec, options: Options) { let url = crate::internal::rpc::URL; @@ -18,10 +17,10 @@ pub fn aur_install(a: Vec, options: Options) { let noconfirm = options.noconfirm; if verbosity >= 1 { - log(format!("Installing from AUR: {:?}", &a)); + log!("Installing from AUR: {:?}", &a); } - info(format!("Installing packages {} from the AUR", a.join(", "))); + info!("Installing packages {} from the AUR", a.join(", ")); for package in a { let rpcres = rpcinfo(package); @@ -33,10 +32,10 @@ pub fn aur_install(a: Vec, options: Options) { let pkg = &rpcres.package.as_ref().unwrap().name; if verbosity >= 1 { - log(format!("Cloning {} into cachedir", pkg)); + log!("Cloning {} into cachedir", pkg); } - info("Cloning package source".to_string()); + info!("Cloning package source"); set_current_dir(Path::new(&cachedir)).unwrap(); ShellCommand::git() @@ -46,38 +45,32 @@ pub fn aur_install(a: Vec, options: Options) { .silent_unwrap(AppExitCode::GitError); if verbosity >= 1 { - log(format!( + log!( "Cloned {} into cachedir, moving on to resolving dependencies", pkg - )); - log(format!( + ); + log!( "Raw dependencies for package {} are:\n{:?}", pkg, rpcres.package.as_ref().unwrap().depends.join(", ") - )); - log(format!( + ); + log!( "Raw makedepends for package {} are:\n{:?}", pkg, rpcres.package.as_ref().unwrap().make_depends.join(", ") - )); + ); } // dep sorting - info("Sorting dependencies".to_string()); + log!("Sorting dependencies"); let sorted = crate::internal::sort(&rpcres.package.as_ref().unwrap().depends, options); - info("Sorting make dependencies".to_string()); + log!("Sorting make dependencies"); let md_sorted = crate::internal::sort(&rpcres.package.as_ref().unwrap().make_depends, options); if verbosity >= 1 { - log(format!( - "Sorted dependencies for {} are:\n{:?}", - pkg, &sorted - )); - log(format!( - "Sorted makedepends for {} are:\n{:?}", - pkg, &md_sorted - )); + log!("Sorted dependencies for {} are:\n{:?}", pkg, &sorted); + log!("Sorted makedepends for {} are:\n{:?}", pkg, &md_sorted); } let newopts = Options { @@ -87,23 +80,18 @@ pub fn aur_install(a: Vec, options: Options) { }; if !sorted.nf.is_empty() || !md_sorted.nf.is_empty() { - crash( - format!( - "Could not find dependencies {} for package {}, aborting", - sorted.nf.join(", "), - pkg - ), + crash!( AppExitCode::MissingDeps, + "Could not find dependencies {} for package {}, aborting", + sorted.nf.join(", "), + pkg, ); } if !noconfirm { - let p1 = prompt( - format!( - "Would you like to review {}'s PKGBUILD (and any .install files if present)?", - pkg - ), - false, + let p1 = prompt!(default false, + "Would you like to review {}'s PKGBUILD (and any .install files if present)?", + pkg ); let editor: &str = &env::var("PAGER").unwrap_or_else(|_| "less".parse().unwrap()); @@ -129,16 +117,17 @@ pub fn aur_install(a: Vec, options: Options) { .silent_unwrap(AppExitCode::Other); } - let p2 = prompt(format!("Would you still like to install {}?", pkg), true); + let p2 = prompt!(default true, "Would you still like to install {}?", pkg); if !p2 { fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap(); - crash("Not proceeding".to_string(), AppExitCode::UserCancellation); + crash!(AppExitCode::UserCancellation, "Not proceeding"); } } } // dep installing - info("Moving on to install dependencies".to_string()); + info!("Moving on to install dependencies"); + if !sorted.repo.is_empty() { crate::operations::install(sorted.repo, newopts); crate::operations::install(md_sorted.repo, newopts); @@ -148,7 +137,7 @@ pub fn aur_install(a: Vec, options: Options) { crate::operations::aur_install(md_sorted.aur, newopts); } - let mut makepkg_args = vec!["-rsic", "--skippgp"]; + let mut makepkg_args = vec!["-rsci", "--skippgp"]; if options.asdeps { makepkg_args.push("--asdeps") } @@ -157,7 +146,7 @@ pub fn aur_install(a: Vec, options: Options) { } // package building and installing - info("Building time!".to_string()); + info!("Building time!"); set_current_dir(format!("{}/{}", cachedir, pkg)).unwrap(); let status = ShellCommand::makepkg() .args(makepkg_args) @@ -166,9 +155,10 @@ pub fn aur_install(a: Vec, options: Options) { if !status.success() { fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap(); - crash( - format!("Error encountered while installing {}, aborting", pkg), + crash!( AppExitCode::PacmanError, + "Error encountered while installing {}, aborting", + pkg, ); } diff --git a/src/operations/clean.rs b/src/operations/clean.rs index d353736..a7dceeb 100644 --- a/src/operations/clean.rs +++ b/src/operations/clean.rs @@ -19,15 +19,15 @@ pub fn clean(options: Options) { .silent_unwrap(AppExitCode::PacmanError); if orphaned_packages.stdout.as_str() == "" { - info("No orphaned packages found".to_string()); + info!("No orphaned packages found"); } else { - info(format!( + info!( "Removing orphans would uninstall the following packages: \n{}", &orphaned_packages.stdout - )); - let cont = prompt("Continue?".to_string(), false); + ); + let cont = prompt!(default false, "Continue?"); if !cont { - info("Exiting".to_string()); + info!("Exiting"); std::process::exit(AppExitCode::PacmanError as i32); } @@ -44,7 +44,7 @@ pub fn clean(options: Options) { } if verbosity >= 1 { - log(format!("Removing orphans: {:?}", orphaned_packages_vec)); + log!("Removing orphans: {:?}", orphaned_packages_vec); } let pacman_result = ShellCommand::pacman() @@ -54,17 +54,14 @@ pub fn clean(options: Options) { .silent_unwrap(AppExitCode::PacmanError); if pacman_result.success() { - info("Successfully removed orphans".to_string()); + info!("Successfully removed orphans"); } else { - crash( - "Failed to remove orphans".to_string(), - AppExitCode::PacmanError, - ); + crash!(AppExitCode::PacmanError, "Failed to remove orphans",); } } let clear_cache = if !noconfirm { - prompt("Also clear pacman's package cache?".to_string(), false) + prompt!(default false, "Also clear pacman's package cache?") } else { true }; @@ -80,7 +77,7 @@ pub fn clean(options: Options) { } if verbosity >= 1 { - log("Clearing using `paccache -r`".to_string()); + log!("Clearing using `paccache -r`"); } Command::new("sudo") @@ -88,16 +85,17 @@ pub fn clean(options: Options) { .args(paccache_args) .spawn() .unwrap_or_else(|e| { - crash( - format!("Couldn't clear cache using `paccache -r`, {}", e), + crash!( AppExitCode::PacmanError, + "Couldn't clear cache using `paccache -r`, {}", + e, ) }) .wait() .unwrap(); if verbosity >= 1 { - log("Clearing using `pacman -Sc`".to_string()); + log!("Clearing using `pacman -Sc`"); } let pacman_result = ShellCommand::pacman() @@ -107,12 +105,9 @@ pub fn clean(options: Options) { .silent_unwrap(AppExitCode::PacmanError); if pacman_result.success() { - info("Successfully cleared package cache".to_string()); + info!("Successfully cleared package cache"); } else { - crash( - "Failed to clear package cache".to_string(), - AppExitCode::PacmanError, - ); + crash!(AppExitCode::PacmanError, "Failed to clear package cache",); } } } diff --git a/src/operations/install.rs b/src/operations/install.rs index d1f1c59..2de6272 100644 --- a/src/operations/install.rs +++ b/src/operations/install.rs @@ -4,10 +4,7 @@ use crate::internal::exit_code::AppExitCode; use crate::{crash, info, log, Options}; pub fn install(packages: Vec, options: Options) { - info(format!( - "Installing packages {} from repos", - &packages.join(", ") - )); + info!("Installing packages {} from repos", &packages.join(", ")); let mut opers = vec!["-S", "--needed"]; if options.noconfirm { opers.push("--noconfirm"); @@ -19,7 +16,7 @@ pub fn install(packages: Vec, options: Options) { if !packages.is_empty() { if verbosity >= 1 { - log(format!("Installing from repos: {:?}", &packages)); + log!("Installing from repos: {:?}", &packages); } let status = ShellCommand::pacman() @@ -29,20 +26,15 @@ pub fn install(packages: Vec, options: Options) { .wait() .silent_unwrap(AppExitCode::PacmanError); if !status.success() { - crash( - format!( - "An error occured while installing packages: {}, aborting", - packages.join(", ") - ), + crash!( AppExitCode::PacmanError, + "An error occured while installing packages: {}, aborting", + packages.join(", "), ); } if verbosity >= 1 { - log(format!( - "Installing packages: {:?} was successful", - &packages - )); + log!("Installing packages: {:?} was successful", &packages); } } } diff --git a/src/operations/search.rs b/src/operations/search.rs index b837133..0303602 100644 --- a/src/operations/search.rs +++ b/src/operations/search.rs @@ -21,10 +21,7 @@ pub fn aur_search(query: &str, options: Options) { } if verbosity >= 1 { - log(format!( - "Found {} resuls for \"{}\" in AUR", - res.resultcount, query - )); + log!("Found {} resuls for \"{}\" in AUR", res.resultcount, query); } } @@ -38,11 +35,11 @@ pub fn repo_search(query: &str, options: Options) { .stdout; if verbosity >= 1 { - log(format!( + log!( "Found {} results for \"{}\" in repos", &output.split('\n').count() / 2, &query - )); + ); } println!("{}", output) diff --git a/src/operations/uninstall.rs b/src/operations/uninstall.rs index e7e51a6..646e65a 100644 --- a/src/operations/uninstall.rs +++ b/src/operations/uninstall.rs @@ -15,7 +15,7 @@ pub fn uninstall(packages: Vec, options: Options) { } let verbosity = options.verbosity; if verbosity >= 1 { - log(format!("Uninstalling: {:?}", &packages)); + log!("Uninstalling: {:?}", &packages); } ShellCommand::pacman() @@ -25,10 +25,7 @@ pub fn uninstall(packages: Vec, options: Options) { .silent_unwrap(AppExitCode::PacmanError); if verbosity >= 1 { - log(format!( - "Uninstalling packages: {:?} exited with code 0", - &packages - )); + log!("Uninstalling packages: {:?} exited with code 0", &packages); } for package in packages { @@ -41,7 +38,7 @@ pub fn uninstall(packages: Vec, options: Options) { .exists() { if verbosity >= 1 { - log("Old cache directory found, deleting".to_string()); + log!("Old cache directory found, deleting"); } fs::remove_dir_all(Path::new(&format!( "{}/.cache/ame/{}", diff --git a/src/operations/upgrade.rs b/src/operations/upgrade.rs index c3542ef..3ba5b47 100644 --- a/src/operations/upgrade.rs +++ b/src/operations/upgrade.rs @@ -15,7 +15,7 @@ pub fn upgrade(options: Options) { } if verbosity >= 1 { - log("Upgrading repo packages".to_string()); + log!("Upgrading repo packages"); } let pacman_result = ShellCommand::pacman() @@ -25,26 +25,25 @@ pub fn upgrade(options: Options) { .silent_unwrap(AppExitCode::PacmanError); if pacman_result.success() { - info("Successfully upgraded repo packages".to_string()); + info!("Successfully upgraded repo packages"); } else { - let cont = prompt( - "Failed to upgrade repo packages, continue to upgrading AUR packages?".to_string(), - false, + let cont = prompt!(default false, + "Failed to upgrade repo packages, continue to upgrading AUR packages?", ); if !cont { - info("Exiting".to_string()); + info!("Exiting"); std::process::exit(AppExitCode::PacmanError as i32); } } if verbosity >= 1 { - log("Upgrading AUR packages".to_string()); + log!("Upgrading AUR packages"); } let res = crate::database::query(options); if verbosity >= 1 { - log(format!("{:?}", &res)); + log!("{:?}", &res); } let mut aur_upgrades = vec![]; @@ -59,6 +58,6 @@ pub fn upgrade(options: Options) { if !aur_upgrades.is_empty() { aur_install(aur_upgrades, options); } else { - info("No upgrades available for installed AUR packages".to_string()); + info!("No upgrades available for installed AUR packages"); } }