Make fs and command operations asynchronous

Signed-off-by: trivernis <trivernis@protonmail.com>
i18n
trivernis 2 years ago
parent 33a68d654a
commit 21e4d66968
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

97
Cargo.lock generated

@ -4,8 +4,9 @@ version = 3
[[package]]
name = "Amethyst"
version = "3.3.0"
version = "4.0.0"
dependencies = [
"async-recursion",
"clap",
"colored",
"libc",
@ -14,6 +15,7 @@ dependencies = [
"regex",
"rusqlite",
"serde",
"tokio",
"ureq",
]
@ -28,6 +30,17 @@ dependencies = [
"version_check",
]
[[package]]
name = "async-recursion"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "atty"
version = "0.2.14"
@ -57,6 +70,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cc"
version = "1.0.73"
@ -335,6 +354,18 @@ dependencies = [
"libmimalloc-sys",
]
[[package]]
name = "mio"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "native-tls"
version = "0.2.10"
@ -416,6 +447,12 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pkg-config"
version = "0.3.25"
@ -582,6 +619,15 @@ dependencies = [
"serde",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.9.0"
@ -662,6 +708,55 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439"
dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"once_cell",
"pin-project-lite",
"signal-hook-registry",
"tokio-macros",
"tracing",
"winapi",
]
[[package]]
name = "tokio-macros"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
dependencies = [
"once_cell",
]
[[package]]
name = "unicode-bidi"
version = "0.3.8"

@ -1,6 +1,6 @@
[package]
name = "Amethyst"
version = "3.3.0"
version = "4.0.0"
authors = ["michal <michal@tar.black>", "axtlos <axtlos@tar.black>"]
edition = "2021"
description = "A fast and efficient AUR helper"
@ -60,3 +60,8 @@ ureq = { version = "2.4.0", default-features = false, features = [ "native-tls",
serde = { version = "1.0.138", default-features = false, features = [ "derive", "serde_derive" ] }
native-tls = { version = "0.2.10", default-features = false }
libc = { version = "0.2.126", default-features = false }
async-recursion = "1.0.0"
[dependencies.tokio]
version = "1.19.2"
features = ["rt", "io-std", "io-util", "process", "time", "macros", "tracing", "fs"]

@ -1,5 +1,6 @@
use std::ffi::{OsStr, OsString};
use std::process::{Child, Command, ExitStatus, Stdio};
use std::process::{ExitStatus, Stdio};
use tokio::process::{Child, Command};
use crate::internal::error::{AppError, AppResult};
use crate::internal::is_tty;
@ -84,8 +85,8 @@ impl ShellCommand {
}
/// Waits for the child to exit but returns an error when it exists with a non-zero status code
pub fn wait_success(self) -> AppResult<()> {
let status = self.wait()?;
pub async fn wait_success(self) -> AppResult<()> {
let status = self.wait().await?;
if status.success() {
Ok(())
} else {
@ -94,17 +95,17 @@ impl ShellCommand {
}
/// Waits for the child to exit and returns the output status
pub fn wait(self) -> AppResult<ExitStatus> {
pub async fn wait(self) -> AppResult<ExitStatus> {
let mut child = self.spawn(false)?;
child.wait().map_err(AppError::from)
child.wait().await.map_err(AppError::from)
}
/// Waits with output until the program completed and
/// returns the string output object
pub fn wait_with_output(self) -> AppResult<StringOutput> {
pub async fn wait_with_output(self) -> AppResult<StringOutput> {
let child = self.spawn(true)?;
let output = child.wait_with_output()?;
let output = child.wait_with_output().await?;
let stdout = String::from_utf8(output.stdout).map_err(|e| AppError::from(e.to_string()))?;
let stderr = String::from_utf8(output.stderr).map_err(|e| AppError::from(e.to_string()))?;

@ -3,7 +3,7 @@ use crate::internal::error::SilentUnwrap;
use crate::internal::exit_code::AppExitCode;
use crate::{prompt, warn};
pub fn detect() {
pub async fn detect() {
let mut pacnew = vec![];
for entry in std::fs::read_dir("/etc").unwrap() {
@ -24,6 +24,7 @@ pub fn detect() {
ShellCommand::pacdiff()
.elevated()
.wait()
.await
.silent_unwrap(AppExitCode::PacmanError);
}
}

@ -30,7 +30,7 @@ pub struct InfoResults {
pub const URL: &str = "https://aur.archlinux.org/";
pub fn rpcinfo(pkg: String) -> InfoResults {
pub fn rpcinfo(pkg: &String) -> InfoResults {
let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap());
let agent = ureq::AgentBuilder::new()
.tls_connector(tls_connector)

@ -4,42 +4,42 @@ use crate::internal::{clean, rpc, structs};
use crate::{log, Options};
pub fn sort(input: &[String], options: Options) -> structs::Sorted {
let mut repo: Vec<String> = vec![];
let mut aur: Vec<String> = vec![];
let mut nf: Vec<String> = vec![];
let mut repo_packages: Vec<String> = vec![];
let mut aur_packages: Vec<String> = vec![];
let mut missing_packages: Vec<String> = vec![];
let verbosity = options.verbosity;
let a = clean(input, options);
let packages = clean(input, options);
if verbosity >= 1 {
log!("Sorting: {:?}", a.join(" "));
log!("Sorting: {:?}", packages.join(" "));
}
for b in a {
for package in packages {
let rs = Command::new("pacman")
.arg("-Ss")
.arg(format!("^{}$", &b))
.arg(format!("^{}$", &package))
.stdout(Stdio::null())
.status()
.expect("Something has gone wrong");
if let Some(0) = rs.code() {
if verbosity >= 1 {
log!("{} found in repos", b);
log!("{} found in repos", package);
}
repo.push(b.to_string());
} else if rpc::rpcinfo(b.to_string()).found {
repo_packages.push(package.to_string());
} else if rpc::rpcinfo(&package).found {
if verbosity >= 1 {
log!("{} found in AUR", b);
log!("{} found in AUR", package);
}
aur.push(b.to_string());
aur_packages.push(package.to_string());
} else {
if verbosity >= 1 {
log!("{} not found", b);
log!("{} not found", package);
}
nf.push(b.to_string());
missing_packages.push(package.to_string());
}
}
structs::Sorted::new(repo, aur, nf)
structs::Sorted::new(repo_packages, aur_packages, missing_packages)
}

@ -1,16 +1,18 @@
use crate::ShellCommand;
use std::thread;
use std::time::Duration;
use crate::ShellCommand;
/// 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))
pub async fn start_sudoloop() {
prompt_sudo().await;
tokio::task::spawn(async move {
loop {
prompt_sudo().await;
tokio::time::sleep(Duration::from_secs(3 * 60)).await;
}
});
}
fn prompt_sudo() {
while ShellCommand::sudo().arg("-v").wait_success().is_err() {}
async fn prompt_sudo() {
while ShellCommand::sudo().arg("-v").wait_success().await.is_err() {}
}

@ -16,7 +16,8 @@ mod database;
mod internal;
mod operations;
fn main() {
#[tokio::main(flavor = "current_thread")]
async fn main() {
if unsafe { libc::geteuid() } == 0 {
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");
}
@ -35,38 +36,38 @@ fn main() {
init(options);
if args.sudoloop {
start_sudoloop();
start_sudoloop().await;
}
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::Install(install_args) => cmd_install(install_args, options).await,
Operation::Remove(remove_args) => cmd_remove(remove_args, options).await,
Operation::Search(search_args) => cmd_search(search_args, options).await,
Operation::Query(query_args) => cmd_query(query_args).await,
Operation::Upgrade => {
info!("Performing system upgrade");
operations::upgrade(options);
operations::upgrade(options).await;
}
Operation::Clean => {
info!("Removing orphaned packages");
operations::clean(options);
operations::clean(options).await;
}
}
detect();
detect().await;
}
fn cmd_install(args: InstallArgs, options: Options) {
async fn cmd_install(args: InstallArgs, options: Options) {
let packages = args.packages;
let sorted = sort(&packages, options);
info!("Attempting to install packages: {}", packages.join(", "));
if !sorted.repo.is_empty() {
operations::install(sorted.repo, options);
operations::install(sorted.repo, options).await;
}
if !sorted.aur.is_empty() {
operations::aur_install(sorted.aur, options);
operations::aur_install(sorted.aur, options).await;
}
if !sorted.nf.is_empty() {
crash!(
@ -80,6 +81,7 @@ fn cmd_install(args: InstallArgs, options: Options) {
.arg("-c")
.arg("sudo find /etc -name *.pacnew")
.wait_with_output()
.await
.silent_unwrap(AppExitCode::Other)
.stdout;
@ -92,51 +94,55 @@ fn cmd_install(args: InstallArgs, options: Options) {
}
}
fn cmd_remove(args: RemoveArgs, options: Options) {
async fn cmd_remove(args: RemoveArgs, options: Options) {
let packages = args.packages;
info!("Uninstalling packages: {}", &packages.join(", "));
operations::uninstall(packages, options);
operations::uninstall(packages, options).await;
}
fn cmd_search(args: SearchArgs, options: Options) {
async fn cmd_search(args: SearchArgs, options: Options) {
let query_string = args.search.join(" ");
if args.aur {
info!("Searching AUR for {}", &query_string);
operations::aur_search(&query_string, options);
operations::aur_search(&query_string, options).await;
}
if args.repo {
info!("Searching repos for {}", &query_string);
operations::search(&query_string, options);
operations::search(&query_string, options).await;
}
if !args.aur && !args.repo {
info!("Searching AUR and repos for {}", &query_string);
operations::search(&query_string, options);
operations::aur_search(&query_string, options);
operations::search(&query_string, options).await;
operations::aur_search(&query_string, options).await;
}
}
fn cmd_query(args: QueryArgs) {
async fn cmd_query(args: QueryArgs) {
if args.aur {
ShellCommand::pacman()
.arg("-Qm")
.wait_success()
.await
.silent_unwrap(AppExitCode::PacmanError);
}
if args.repo {
ShellCommand::pacman()
.arg("-Qn")
.wait_success()
.await
.silent_unwrap(AppExitCode::PacmanError);
}
if !args.repo && !args.aur {
ShellCommand::pacman()
.arg("-Qn")
.wait_success()
.await
.silent_unwrap(AppExitCode::PacmanError);
ShellCommand::pacman()
.arg("-Qm")
.wait_success()
.await
.silent_unwrap(AppExitCode::PacmanError);
}
}

@ -1,8 +1,9 @@
use async_recursion::async_recursion;
use std::env;
use std::env::set_current_dir;
use std::fs::remove_dir_all;
use std::path::Path;
use std::process::Command;
use std::{env, fs};
use tokio::fs;
use crate::internal::commands::ShellCommand;
use crate::internal::error::SilentUnwrap;
@ -10,20 +11,22 @@ use crate::internal::exit_code::AppExitCode;
use crate::internal::rpc::rpcinfo;
use crate::{crash, info, log, prompt, Options};
pub fn aur_install(a: Vec<String>, options: Options) {
/// Installs a given list of packages from the aur
#[async_recursion]
pub async fn aur_install(packages: Vec<String>, options: Options) {
let url = crate::internal::rpc::URL;
let cachedir = format!("{}/.cache/ame/", env::var("HOME").unwrap());
let verbosity = options.verbosity;
let noconfirm = options.noconfirm;
if verbosity >= 1 {
log!("Installing from AUR: {:?}", &a);
log!("Installing from AUR: {:?}", &packages);
}
info!("Installing packages {} from the AUR", a.join(", "));
info!("Installing packages {} from the AUR", packages.join(", "));
for package in a {
let rpcres = rpcinfo(package);
for package in packages {
let rpcres = rpcinfo(&package);
if !rpcres.found {
break;
@ -42,6 +45,7 @@ pub fn aur_install(a: Vec<String>, options: Options) {
.arg("clone")
.arg(format!("{}/{}", url, pkg))
.wait()
.await
.silent_unwrap(AppExitCode::GitError);
if verbosity >= 1 {
@ -107,6 +111,7 @@ pub fn aur_install(a: Vec<String>, options: Options) {
.arg("-c")
.arg(format!("ls {}/*.install &> /dev/null", pkg))
.wait()
.await
.silent_unwrap(AppExitCode::Other);
if status.success() {
@ -114,12 +119,15 @@ pub fn aur_install(a: Vec<String>, options: Options) {
.arg("-c")
.arg(format!("{} {}/*.install", editor, pkg))
.wait()
.await
.silent_unwrap(AppExitCode::Other);
}
let p2 = prompt!(default true, "Would you still like to install {}?", pkg);
if !p2 {
fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap();
fs::remove_dir_all(format!("{}/{}", cachedir, pkg))
.await
.unwrap();
crash!(AppExitCode::UserCancellation, "Not proceeding");
}
}
@ -129,12 +137,12 @@ pub fn aur_install(a: Vec<String>, options: Options) {
info!("Moving on to install dependencies");
if !sorted.repo.is_empty() {
crate::operations::install(sorted.repo, newopts);
crate::operations::install(md_sorted.repo, newopts);
crate::operations::install(sorted.repo, newopts).await;
crate::operations::install(md_sorted.repo, newopts).await;
}
if !sorted.aur.is_empty() {
crate::operations::aur_install(sorted.aur, newopts);
crate::operations::aur_install(md_sorted.aur, newopts);
crate::operations::aur_install(sorted.aur, newopts).await;
crate::operations::aur_install(md_sorted.aur, newopts).await;
}
let mut makepkg_args = vec!["-rsci", "--skippgp"];
@ -151,10 +159,13 @@ pub fn aur_install(a: Vec<String>, options: Options) {
let status = ShellCommand::makepkg()
.args(makepkg_args)
.wait()
.await
.silent_unwrap(AppExitCode::MakePkgError);
if !status.success() {
fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap();
fs::remove_dir_all(format!("{}/{}", cachedir, pkg))
.await
.unwrap();
crash!(
AppExitCode::PacmanError,
"Error encountered while installing {}, aborting",
@ -163,7 +174,9 @@ pub fn aur_install(a: Vec<String>, options: Options) {
}
set_current_dir(&cachedir).unwrap();
remove_dir_all(format!("{}/{}", cachedir, &pkg)).unwrap();
fs::remove_dir_all(format!("{}/{}", cachedir, &pkg))
.await
.unwrap();
// pushes package to database
crate::database::add(rpcres.package.unwrap(), options);

@ -1,4 +1,4 @@
use std::process::Command;
use tokio::process::Command;
use crate::crash;
use crate::info;
@ -9,13 +9,15 @@ use crate::log;
use crate::prompt;
use crate::Options;
pub fn clean(options: Options) {
/// Removes orphaned packages and cache
pub async fn clean(options: Options) {
let verbosity = options.verbosity;
let noconfirm = options.noconfirm;
let orphaned_packages = ShellCommand::pacman()
.arg("-Qdtq")
.wait_with_output()
.await
.silent_unwrap(AppExitCode::PacmanError);
if orphaned_packages.stdout.as_str() == "" {
@ -51,6 +53,7 @@ pub fn clean(options: Options) {
.elevated()
.args(pacman_args)
.wait()
.await
.silent_unwrap(AppExitCode::PacmanError);
if pacman_result.success() {
@ -92,6 +95,7 @@ pub fn clean(options: Options) {
)
})
.wait()
.await
.unwrap();
if verbosity >= 1 {
@ -102,6 +106,7 @@ pub fn clean(options: Options) {
.elevated()
.args(pacman_args)
.wait()
.await
.silent_unwrap(AppExitCode::PacmanError);
if pacman_result.success() {

@ -3,7 +3,7 @@ use crate::internal::error::SilentUnwrap;
use crate::internal::exit_code::AppExitCode;
use crate::{crash, info, log, Options};
pub fn install(packages: Vec<String>, options: Options) {
pub async fn install(packages: Vec<String>, options: Options) {
info!("Installing packages {} from repos", &packages.join(", "));
let mut opers = vec!["-S", "--needed"];
if options.noconfirm {
@ -24,6 +24,7 @@ pub fn install(packages: Vec<String>, options: Options) {
.args(opers)
.args(&packages)
.wait()
.await
.silent_unwrap(AppExitCode::PacmanError);
if !status.success() {
crash!(

@ -4,7 +4,7 @@ use crate::internal::exit_code::AppExitCode;
use crate::internal::rpc::rpcsearch;
use crate::{log, Options};
pub fn aur_search(query: &str, options: Options) {
pub async fn aur_search(query: &str, options: Options) {
let verbosity = options.verbosity;
let res = rpcsearch(query.to_string());
@ -25,12 +25,13 @@ pub fn aur_search(query: &str, options: Options) {
}
}
pub fn repo_search(query: &str, options: Options) {
pub async fn repo_search(query: &str, options: Options) {
let verbosity = options.verbosity;
let output = ShellCommand::pacman()
.arg("-Ss")
.arg(query)
.wait_with_output()
.await
.silent_unwrap(AppExitCode::PacmanError)
.stdout;

@ -1,12 +1,13 @@
use std::env;
use std::path::Path;
use std::{env, fs};
use tokio::fs;
use crate::internal::commands::ShellCommand;
use crate::internal::error::SilentUnwrap;
use crate::internal::exit_code::AppExitCode;
use crate::{log, Options};
pub fn uninstall(packages: Vec<String>, options: Options) {
pub async fn uninstall(packages: Vec<String>, options: Options) {
let mut pacman_args = vec!["-Rs"];
pacman_args.append(&mut packages.iter().map(|s| s.as_str()).collect());
@ -22,6 +23,7 @@ pub fn uninstall(packages: Vec<String>, options: Options) {
.elevated()
.args(pacman_args)
.wait_success()
.await
.silent_unwrap(AppExitCode::PacmanError);
if verbosity >= 1 {
@ -45,6 +47,7 @@ pub fn uninstall(packages: Vec<String>, options: Options) {
env::var("HOME").unwrap(),
package
)))
.await
.unwrap();
}
}

@ -5,7 +5,8 @@ use crate::internal::rpc::rpcinfo;
use crate::operations::aur_install::aur_install;
use crate::{info, log, prompt, Options};
pub fn upgrade(options: Options) {
/// Upgrades all installed packages
pub async fn upgrade(options: Options) {
let verbosity = options.verbosity;
let noconfirm = options.noconfirm;
@ -22,15 +23,16 @@ pub fn upgrade(options: Options) {
.elevated()
.args(pacman_args)
.wait()
.await
.silent_unwrap(AppExitCode::PacmanError);
if pacman_result.success() {
info!("Successfully upgraded repo packages");
} else {
let cont = prompt!(default false,
let continue_upgrading = prompt!(default false,
"Failed to upgrade repo packages, continue to upgrading AUR packages?",
);
if !cont {
if !continue_upgrading {
info!("Exiting");
std::process::exit(AppExitCode::PacmanError as i32);
}
@ -40,23 +42,23 @@ pub fn upgrade(options: Options) {
log!("Upgrading AUR packages");
}
let res = crate::database::query(options);
let packages = crate::database::query(options);
if verbosity >= 1 {
log!("{:?}", &res);
log!("{:?}", &packages);
}
let mut aur_upgrades = vec![];
for r in res {
let re = r.clone();
let ver = rpcinfo(r.name);
if ver.package.unwrap().version != r.version {
aur_upgrades.push(re.name);
for package in packages {
let remote_package = rpcinfo(&package.name);
if remote_package.package.unwrap().version != package.version {
aur_upgrades.push(package.name);
}
}
if !aur_upgrades.is_empty() {
aur_install(aur_upgrades, options);
aur_install(aur_upgrades, options).await;
} else {
info!("No upgrades available for installed AUR packages");
}

Loading…
Cancel
Save