diff --git a/src/database/add.rs b/src/database/add.rs index bf4620b..e96324f 100644 --- a/src/database/add.rs +++ b/src/database/add.rs @@ -19,8 +19,7 @@ pub fn add(pkg: Package, options: Options) { 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(" ")], - ).unwrap_or_else(|e| { - crash(format!("Failed adding package {} to the database: {}", pkg.name, e), 2); - 1 - }); + ).unwrap_or_else(|e| + crash(format!("Failed adding package {} to the database: {}", pkg.name, e), 2) + ); } diff --git a/src/database/initialise.rs b/src/database/initialise.rs index 01d92b9..a5cef65 100644 --- a/src/database/initialise.rs +++ b/src/database/initialise.rs @@ -31,8 +31,5 @@ pub fn init(options: Options) { )", [], ) - .unwrap_or_else(|e| { - crash(format!("Couldn't initialise database: {}", e), 3); - 1 - }); + .unwrap_or_else(|e| crash(format!("Couldn't initialise database: {}", e), 3)); } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..621323a --- /dev/null +++ b/src/error.rs @@ -0,0 +1,54 @@ +use crate::crash; +use std::error::Error; +use std::fmt::{Debug, Display, Formatter}; +use std::io; + +pub type AppResult = Result; + +#[derive(Debug)] +pub enum AppError { + Io(std::io::Error), + Other(String), +} + +impl Display for AppError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + AppError::Io(io) => Display::fmt(io, f), + AppError::Other(s) => Display::fmt(s, f), + } + } +} + +impl Error for AppError {} + +impl From for AppError { + fn from(e: io::Error) -> Self { + Self::Io(e) + } +} + +impl From for AppError { + fn from(string: String) -> Self { + Self::Other(string) + } +} + +impl From<&str> for AppError { + fn from(string: &str) -> Self { + Self::from(string.to_string()) + } +} + +pub trait SilentUnwrap { + fn silent_unwrap(self) -> T; +} + +impl SilentUnwrap for AppResult { + fn silent_unwrap(self) -> T { + match self { + Ok(val) => val, + Err(_) => crash("an error occurred", 1), + } + } +} diff --git a/src/internal/commands.rs b/src/internal/commands.rs new file mode 100644 index 0000000..91a5dbd --- /dev/null +++ b/src/internal/commands.rs @@ -0,0 +1,105 @@ +use crate::error::{AppError, AppResult}; +use crate::internal::uwu_enabled; +use crate::uwu; +use std::ffi::{OsStr, OsString}; +use std::io::{BufRead, BufReader}; +use std::process::{ChildStderr, ChildStdout, Command, Stdio}; + +/// Executes a makepkg command +#[inline] +pub fn makepkg, S: AsRef>(args: I) -> AppResult { + run_command("makepkg", args) +} + +/// Executes a git command +#[inline] +pub fn git, S: AsRef>(args: I) -> AppResult { + run_command("git", args) +} + +/// Executes a bash command +#[inline] +pub fn bash, S: AsRef>(args: I) -> AppResult { + run_command("bash", args) +} + +/// Runs pacman with sudo +pub fn sudo_pacman, S: AsRef>(args: I) -> AppResult { + let mut pacman_args = args + .into_iter() + .map(|i: S| OsString::from(i.as_ref())) + .collect::>(); + let mut sudo_args = vec![OsString::from("pacman")]; + sudo_args.append(&mut pacman_args); + sudo(sudo_args) +} + +/// Executes a pacman command +#[inline] +pub fn pacman, S: AsRef>(args: I) -> AppResult { + run_command("pacman", args) +} + +#[inline] +pub fn sudo, S: AsRef>(args: I) -> AppResult { + run_command("sudo", args) +} + +/// Runs a command and parses its output as string +fn run_command, I: IntoIterator, S2: AsRef>( + command: S1, + args: I, +) -> AppResult { + let mut child = Command::new(command) + .args(args) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + let stdout = child.stdout.as_mut().unwrap(); + let stderr = child.stderr.as_mut().unwrap(); + let stdout_str = read_stdout(stdout)?; + let stderr_str = read_stderr(stderr)?; + + let status = child.wait()?; + if status.success() { + Ok(stdout_str) + } else { + Err(AppError::from(stderr_str)) + } +} + +fn read_stdout(stdout: &mut ChildStdout) -> AppResult { + let mut stdout_str = String::new(); + let stdout_reader = BufReader::new(stdout); + + for line in stdout_reader.lines() { + let line = line?; + if uwu_enabled() { + println!("{}", uwu!(&*line)) + } else { + println!("{}", &line); + } + stdout_str.push_str(&line); + stdout_str.push_str("\n"); + } + + Ok(stdout_str) +} + +fn read_stderr(stderr: &mut ChildStderr) -> AppResult { + let mut stderr_str = String::new(); + let stderr_reader = BufReader::new(stderr); + + for line in stderr_reader.lines() { + let line = line?; + if uwu_enabled() { + eprintln!("{}", uwu!(&line)) + } else { + eprintln!("{}", &line); + } + stderr_str.push_str(&line); + stderr_str.push_str("\n"); + } + + Ok(stderr_str) +} diff --git a/src/internal/mod.rs b/src/internal/mod.rs index 04b9645..87bf5d6 100644 --- a/src/internal/mod.rs +++ b/src/internal/mod.rs @@ -1,4 +1,5 @@ mod clean; +mod commands; mod initialise; pub mod rpc; mod sort; @@ -6,14 +7,16 @@ mod strings; pub mod structs; pub use clean::*; +pub use commands::*; pub use initialise::*; pub use sort::*; +use std::env; pub use strings::*; #[macro_export] macro_rules! uwu { ($x:expr) => {{ - let uwu: String = String::from_str($x).unwrap(); + let uwu: String = String::from($x); let uwu = uwu.replace("l", "w"); let uwu = uwu.replace("L", "W"); @@ -26,3 +29,11 @@ macro_rules! uwu { uwu }}; } + +fn uwu_enabled() -> bool { + env::var("AME_UWU").unwrap_or_else(|_| "".to_string()) == "true" +} + +fn uwu_debug_enabled() -> bool { + env::var("AME_UWU_DEBUG").unwrap_or_else(|_| "".to_string()) == "true" +} diff --git a/src/internal/strings.rs b/src/internal/strings.rs index dd2662d..892bb15 100644 --- a/src/internal/strings.rs +++ b/src/internal/strings.rs @@ -1,36 +1,28 @@ +use std::io; use std::io::Write; use std::process::exit; -use std::str::FromStr; use std::time::UNIX_EPOCH; -use std::{env, io}; -use crate::uwu; +use crate::{internal, uwu}; -pub fn info(a: String) { - let a = if env::var("AME_UWU").unwrap_or_else(|_| "".to_string()) == "true" { - uwu!(&a) - } else { - a - }; +pub fn info(a: S) { + let a = a.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 crash(a: String, b: i32) { - let a = if env::var("AME_UWU").unwrap_or_else(|_| "".to_string()) == "true" { - uwu!(&a) - } else { - a - }; +pub fn crash(a: S, b: i32) -> ! { + let a = a.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(b); } -pub fn log(a: String) { - let a = if env::var("AME_UWU").unwrap_or_else(|_| "".to_string()) == "true" - && env::var("AME_UWU_DEBUG").unwrap_or_else(|_| "".to_string()) == "true" - { +pub fn log(a: S) { + let a = a.to_string(); + let a = if internal::uwu_enabled() && internal::uwu_debug_enabled() { uwu!(&a) } else { a @@ -46,15 +38,12 @@ pub fn log(a: String) { ); } -pub fn prompt(a: String, b: bool) -> bool { +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 env::var("AME_UWU").unwrap_or_else(|_| "".to_string()) == "true" { - uwu!(&a) - } else { - a - }; + 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: ", diff --git a/src/main.rs b/src/main.rs index 24b1c28..91471af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,16 @@ use clap::Parser; -use std::process; -use std::process::Command; use crate::args::{InstallArgs, Operation, QueryArgs, RemoveArgs, SearchArgs}; use args::Args; -use crate::internal::{crash, info, init, log, sort, structs::Options}; +use crate::internal::{bash, crash, info, init, log, pacman, sort, structs::Options}; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; mod args; mod database; +mod error; mod internal; mod operations; @@ -71,14 +70,14 @@ fn cmd_install(args: InstallArgs, options: Options) { )); } - let out = process::Command::new("bash") - .args(&["-c", "sudo find /etc -name *.pacnew"]) - .output() - .expect("Something has gone wrong") - .stdout; + let bash_output = bash(&["-c", "sudo find /etc -name *.pacnew"]).unwrap(); - 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 !bash_output.is_empty() { + let pacnew_files = bash_output + .split_whitespace() + .collect::>() + .join(", "); + info(format!("You have .pacnew files in /etc ({pacnew_files}) that you haven't removed or acted upon, it is recommended you do that now", )); } } @@ -108,33 +107,13 @@ fn cmd_search(args: SearchArgs, options: Options) { fn cmd_query(args: QueryArgs) { if args.aur { - Command::new("pacman") - .arg("-Qm") - .spawn() - .expect("Something has gone wrong") - .wait() - .unwrap(); + pacman(&["-Qm"]).unwrap(); } if args.repo { - Command::new("pacman") - .arg("-Qn") - .spawn() - .expect("Something has gone wrong") - .wait() - .unwrap(); + pacman(&["-Qn"]).unwrap(); } 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(); + pacman(&["-Qn"]).unwrap(); + pacman(&["-Qm"]).unwrap(); } } diff --git a/src/operations/aur_install.rs b/src/operations/aur_install.rs index 130f541..659f1c2 100644 --- a/src/operations/aur_install.rs +++ b/src/operations/aur_install.rs @@ -1,12 +1,13 @@ use std::env::set_current_dir; use std::fs::remove_dir_all; use std::path::Path; -use std::process::{Command, Stdio}; +use std::process::Command; use std::{env, fs}; +use crate::error::SilentUnwrap; use crate::internal::rpc::rpcinfo; -use crate::internal::{crash, prompt}; -use crate::{info, log, Options}; +use crate::internal::{crash, git, makepkg, prompt}; +use crate::{bash, info, log, Options}; pub fn aur_install(a: Vec, options: Options) { let url = crate::internal::rpc::URL; @@ -36,12 +37,7 @@ pub fn aur_install(a: Vec, options: Options) { info("Cloning package source".to_string()); set_current_dir(Path::new(&cachedir)).unwrap(); - Command::new("git") - .arg("clone") - .arg(format!("{}/{}", url, pkg)) - .stdout(Stdio::null()) - .output() - .expect("Something has gone wrong"); + git(&["clone", &format!("{}/{}", url, pkg)]).silent_unwrap(); if verbosity >= 1 { log(format!( @@ -113,18 +109,10 @@ pub fn aur_install(a: Vec, options: Options) { .wait() .unwrap(); - let out = Command::new("bash") - .args(&["-c", &format!("ls {}/*.install &> /dev/null", pkg)]) - .status() - .unwrap(); + let result = bash(&["-c", &format!("ls {}/*.install &> /dev/null", pkg)]); - if out.code() == Some(0) { - Command::new("bash") - .args(&["-c", &format!("{} {}/*.install", editor, pkg)]) - .spawn() - .unwrap() - .wait() - .unwrap(); + if result.is_ok() { + bash(&["-c", &format!("{} {}/*.install", editor, pkg)]).silent_unwrap(); } let p2 = prompt(format!("Would you still like to install {}?", pkg), true); @@ -157,12 +145,9 @@ pub fn aur_install(a: Vec, options: Options) { // package building and installing info("Building time!".to_string()); set_current_dir(format!("{}/{}", cachedir, pkg)).unwrap(); - let out = Command::new("makepkg") - .args(&makepkg_args) - .status() - .expect("Something has gone wrong"); + let result = makepkg(&makepkg_args); - if out.code() != Some(0) { + if result.is_ok() { fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap(); crash( format!("Error encountered while installing {}, aborting", pkg), diff --git a/src/operations/install.rs b/src/operations/install.rs index cb0aa38..5c19056 100644 --- a/src/operations/install.rs +++ b/src/operations/install.rs @@ -1,13 +1,14 @@ +use crate::internal::sudo_pacman; use crate::{crash, info, log, Options}; pub fn install(a: Vec, options: Options) { info(format!("Installing packages {} from repos", &a.join(", "))); - let mut opers = vec![]; + let mut opers = vec!["-S", "--needed"]; if options.noconfirm { - opers.push("--noconfirm".to_string()); + opers.push("--noconfirm"); } if options.asdeps { - opers.push("--asdeps".to_string()); + opers.push("--asdeps"); } let verbosity = options.verbosity; if !a.is_empty() { @@ -15,15 +16,7 @@ pub fn install(a: Vec, options: Options) { log(format!("Installing from repos: {:?}", &a)); } - let r = runas::Command::new("pacman") - .arg("-S") - .arg("--needed") - .args(&a) - .args(&opers) - .status() - .expect("Something has gone wrong"); - - if r.code() != Some(0) { + if let Err(_e) = sudo_pacman(&opers) { crash( format!( "An error occured while installing packages: {}, aborting", diff --git a/src/operations/search.rs b/src/operations/search.rs index 2056f67..6b0c035 100644 --- a/src/operations/search.rs +++ b/src/operations/search.rs @@ -1,19 +1,11 @@ -use std::process::Command; - +use crate::error::SilentUnwrap; use crate::internal::rpc::rpcsearch; -use crate::{log, Options}; +use crate::{log, pacman, Options}; pub fn aur_search(a: &str, options: Options) { let verbosity = options.verbosity; let res = rpcsearch(a.to_string()); - if verbosity >= 1 { - log(format!( - "Found {} resuls for \"{}\" in AUR", - res.resultcount, a - )); - } - for r in &res.results { println!( "aur/{} {}\n {}", @@ -24,25 +16,24 @@ pub fn aur_search(a: &str, options: Options) { .unwrap_or(&"No description".to_string()) ) } + + if verbosity >= 1 { + log(format!( + "Found {} resuls for \"{}\" in AUR", + res.resultcount, a + )); + } } pub fn repo_search(a: &str, options: Options) { let verbosity = options.verbosity; - let rs = Command::new("pacman") - .arg("-Ss") - .arg(&a) - .output() - .expect("Something has gone wrong"); - - let str = String::from_utf8(rs.stdout).unwrap(); + let output = pacman(&["-Ss", a]).silent_unwrap(); if verbosity >= 1 { log(format!( "Found {} results for \"{}\" in repos", - &str.split('\n').count() / 2, + &output.split('\n').count() / 2, &a )); } - - print!("{}", str); } diff --git a/src/operations/uninstall.rs b/src/operations/uninstall.rs index 0e2ef05..1d1700a 100644 --- a/src/operations/uninstall.rs +++ b/src/operations/uninstall.rs @@ -1,43 +1,47 @@ use std::path::Path; use std::{env, fs}; +use crate::error::SilentUnwrap; +use crate::internal::sudo_pacman; use crate::{log, Options}; -pub fn uninstall(mut a: Vec, options: Options) { - let b = a.clone(); +pub fn uninstall(packages: Vec, options: Options) { + let mut pacman_args = vec!["-Rs"]; + pacman_args.append(&mut packages.iter().map(|s| s.as_str()).collect()); + if options.noconfirm { - a.push("--noconfirm".to_string()); + pacman_args.push("--noconfirm"); } let verbosity = options.verbosity; if verbosity >= 1 { - log(format!("Uninstalling: {:?}", &b)); + log(format!("Uninstalling: {:?}", &packages)); } - let r = runas::Command::new("pacman") - .arg("-Rs") - .args(&a) - .status() - .expect("Something has gone wrong"); + sudo_pacman(pacman_args).silent_unwrap(); - if let Some(x) = r.code() { - if verbosity >= 1 { - log(format!( - "Uninstalling packages: {:?} exited with code {}", - &b, x - )); - } + if verbosity >= 1 { + log(format!( + "Uninstalling packages: {:?} exited with code 0", + &packages + )); } - for b in a { - crate::database::remove(&b, options); - if Path::new(&format!("{}/.cache/ame/{}", env::var("HOME").unwrap(), b)).exists() { + for package in packages { + crate::database::remove(&package, options); + if Path::new(&format!( + "{}/.cache/ame/{}", + env::var("HOME").unwrap(), + package + )) + .exists() + { if verbosity >= 1 { log("Old cache directory found, deleting".to_string()); } fs::remove_dir_all(Path::new(&format!( "{}/.cache/ame/{}", env::var("HOME").unwrap(), - b + package ))) .unwrap(); } diff --git a/src/operations/upgrade.rs b/src/operations/upgrade.rs index d80c824..a7b8f03 100644 --- a/src/operations/upgrade.rs +++ b/src/operations/upgrade.rs @@ -1,6 +1,6 @@ -use runas::Command; - +use crate::error::SilentUnwrap; use crate::internal::rpc::rpcinfo; +use crate::internal::sudo_pacman; use crate::operations::aur_install::aur_install; use crate::{info, log, Options}; @@ -17,10 +17,7 @@ pub fn upgrade(options: Options) { log("Upgrading repo packages".to_string()); } - Command::new("pacman") - .args(&pacman_args) - .status() - .expect("Something has gone wrong"); + sudo_pacman(pacman_args).silent_unwrap(); if verbosity >= 1 { log("Upgrading AUR packages".to_string());