Merge branch 'refactoring' into 'main'
Encapsulate command executions See merge request Trivernis/amethyst!1i18n
commit
99e5db51d9
@ -0,0 +1,125 @@
|
|||||||
|
use crate::internal::error::{AppError, AppResult};
|
||||||
|
use std::ffi::{OsStr, OsString};
|
||||||
|
use std::process::{Child, Command, ExitStatus, Stdio};
|
||||||
|
|
||||||
|
pub struct StringOutput {
|
||||||
|
pub stdout: String,
|
||||||
|
pub stderr: String,
|
||||||
|
pub status: ExitStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper around [std::process::Command] with predefined
|
||||||
|
/// commands used in this project as well as elevated access.
|
||||||
|
pub struct ShellCommand {
|
||||||
|
command: String,
|
||||||
|
args: Vec<OsString>,
|
||||||
|
elevated: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShellCommand {
|
||||||
|
pub fn pacman() -> Self {
|
||||||
|
Self::new("pacman")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn makepkg() -> Self {
|
||||||
|
Self::new("makepkg")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn git() -> Self {
|
||||||
|
Self::new("git")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bash() -> Self {
|
||||||
|
Self::new("bash")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new<S: ToString>(command: S) -> Self {
|
||||||
|
Self {
|
||||||
|
command: command.to_string(),
|
||||||
|
args: Vec::new(),
|
||||||
|
elevated: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds one argument
|
||||||
|
pub fn arg<S: AsRef<OsStr>>(mut self, arg: S) -> Self {
|
||||||
|
self.args.push(arg.as_ref().to_os_string());
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a list of arguments
|
||||||
|
pub fn args<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(mut self, args: I) -> Self {
|
||||||
|
self.args.append(
|
||||||
|
&mut args
|
||||||
|
.into_iter()
|
||||||
|
.map(|a: S| a.as_ref().to_os_string())
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the command with sudo
|
||||||
|
pub fn elevated(mut self) -> Self {
|
||||||
|
self.elevated = true;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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()?;
|
||||||
|
if status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(AppError::NonZeroExit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Waits for the child to exit and returns the output status
|
||||||
|
pub fn wait(self) -> AppResult<ExitStatus> {
|
||||||
|
let mut child = self.spawn(false)?;
|
||||||
|
|
||||||
|
child.wait().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> {
|
||||||
|
let child = self.spawn(true)?;
|
||||||
|
let output = child.wait_with_output()?;
|
||||||
|
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()))?;
|
||||||
|
|
||||||
|
Ok(StringOutput {
|
||||||
|
status: output.status,
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn(self, piped: bool) -> AppResult<Child> {
|
||||||
|
let (stdout, stderr) = if piped {
|
||||||
|
(Stdio::piped(), Stdio::piped())
|
||||||
|
} else {
|
||||||
|
(Stdio::inherit(), Stdio::inherit())
|
||||||
|
};
|
||||||
|
let child = if self.elevated {
|
||||||
|
Command::new("sudo")
|
||||||
|
.arg(self.command)
|
||||||
|
.args(self.args)
|
||||||
|
.stdout(stdout)
|
||||||
|
.stderr(stderr)
|
||||||
|
.spawn()?
|
||||||
|
} else {
|
||||||
|
Command::new(self.command)
|
||||||
|
.args(self.args)
|
||||||
|
.stdout(stdout)
|
||||||
|
.stderr(stderr)
|
||||||
|
.spawn()?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(child)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
use crate::crash;
|
||||||
|
use crate::internal::exit_code::AppExitCode;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
pub type AppResult<T> = Result<T, AppError>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AppError {
|
||||||
|
Io(std::io::Error),
|
||||||
|
Other(String),
|
||||||
|
NonZeroExit,
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
AppError::NonZeroExit => Display::fmt("exited with non zero code", f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for AppError {}
|
||||||
|
|
||||||
|
impl From<io::Error> for AppError {
|
||||||
|
fn from(e: io::Error) -> Self {
|
||||||
|
Self::Io(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> 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<T> {
|
||||||
|
fn silent_unwrap(self, error_code: AppExitCode) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SilentUnwrap<T> for AppResult<T> {
|
||||||
|
fn silent_unwrap(self, exit_code: AppExitCode) -> T {
|
||||||
|
match self {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(_) => crash("an error occurred", exit_code),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
pub enum AppExitCode {
|
||||||
|
RunAsRoot = 1,
|
||||||
|
FailedAddingPkg = 2,
|
||||||
|
FailedInitDb = 3,
|
||||||
|
FailedCreatingPaths = 4,
|
||||||
|
MissingDeps = 5,
|
||||||
|
UserCancellation = 6,
|
||||||
|
PacmanError = 7,
|
||||||
|
GitError = 8,
|
||||||
|
MakePkgError = 9,
|
||||||
|
Other = 102,
|
||||||
|
}
|
@ -1,40 +1,48 @@
|
|||||||
|
use crate::internal::commands::ShellCommand;
|
||||||
|
use crate::internal::error::SilentUnwrap;
|
||||||
|
use crate::internal::exit_code::AppExitCode;
|
||||||
use crate::{crash, info, log, Options};
|
use crate::{crash, info, log, Options};
|
||||||
|
|
||||||
pub fn install(a: Vec<String>, options: Options) {
|
pub fn install(packages: Vec<String>, options: Options) {
|
||||||
info(format!("Installing packages {} from repos", &a.join(", ")));
|
info(format!(
|
||||||
let mut opers = vec![];
|
"Installing packages {} from repos",
|
||||||
|
&packages.join(", ")
|
||||||
|
));
|
||||||
|
let mut opers = vec!["-S", "--needed"];
|
||||||
if options.noconfirm {
|
if options.noconfirm {
|
||||||
opers.push("--noconfirm".to_string());
|
opers.push("--noconfirm");
|
||||||
}
|
}
|
||||||
if options.asdeps {
|
if options.asdeps {
|
||||||
opers.push("--asdeps".to_string());
|
opers.push("--asdeps");
|
||||||
}
|
}
|
||||||
let verbosity = options.verbosity;
|
let verbosity = options.verbosity;
|
||||||
if !a.is_empty() {
|
|
||||||
|
if !packages.is_empty() {
|
||||||
if verbosity >= 1 {
|
if verbosity >= 1 {
|
||||||
log(format!("Installing from repos: {:?}", &a));
|
log(format!("Installing from repos: {:?}", &packages));
|
||||||
}
|
}
|
||||||
|
|
||||||
let r = runas::Command::new("pacman")
|
let status = ShellCommand::pacman()
|
||||||
.arg("-S")
|
.elevated()
|
||||||
.arg("--needed")
|
.args(opers)
|
||||||
.args(&a)
|
.args(&packages)
|
||||||
.args(&opers)
|
.wait()
|
||||||
.status()
|
.silent_unwrap(AppExitCode::PacmanError);
|
||||||
.expect("Something has gone wrong");
|
if !status.success() {
|
||||||
|
|
||||||
if r.code() != Some(0) {
|
|
||||||
crash(
|
crash(
|
||||||
format!(
|
format!(
|
||||||
"An error occured while installing packages: {}, aborting",
|
"An error occured while installing packages: {}, aborting",
|
||||||
a.join(", ")
|
packages.join(", ")
|
||||||
),
|
),
|
||||||
7,
|
AppExitCode::PacmanError,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if verbosity >= 1 {
|
if verbosity >= 1 {
|
||||||
log(format!("Installing packages: {:?} was successful", &a));
|
log(format!(
|
||||||
|
"Installing packages: {:?} was successful",
|
||||||
|
&packages
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,49 @@
|
|||||||
use std::process::Command;
|
use crate::internal::commands::ShellCommand;
|
||||||
|
use crate::internal::error::SilentUnwrap;
|
||||||
|
use crate::internal::exit_code::AppExitCode;
|
||||||
use crate::internal::rpc::rpcsearch;
|
use crate::internal::rpc::rpcsearch;
|
||||||
use crate::{log, Options};
|
use crate::{log, Options};
|
||||||
|
|
||||||
pub fn aur_search(a: &str, options: Options) {
|
pub fn aur_search(query: &str, options: Options) {
|
||||||
let verbosity = options.verbosity;
|
let verbosity = options.verbosity;
|
||||||
let res = rpcsearch(a.to_string());
|
let res = rpcsearch(query.to_string());
|
||||||
|
|
||||||
if verbosity >= 1 {
|
|
||||||
log(format!(
|
|
||||||
"Found {} resuls for \"{}\" in AUR",
|
|
||||||
res.resultcount, a
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
for r in &res.results {
|
for package in &res.results {
|
||||||
println!(
|
println!(
|
||||||
"aur/{} {}\n {}",
|
"aur/{} {}\n {}",
|
||||||
r.name,
|
package.name,
|
||||||
r.version,
|
package.version,
|
||||||
r.description
|
package
|
||||||
|
.description
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(&"No description".to_string())
|
.unwrap_or(&"No description".to_string())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if verbosity >= 1 {
|
||||||
|
log(format!(
|
||||||
|
"Found {} resuls for \"{}\" in AUR",
|
||||||
|
res.resultcount, query
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repo_search(a: &str, options: Options) {
|
pub fn repo_search(query: &str, options: Options) {
|
||||||
let verbosity = options.verbosity;
|
let verbosity = options.verbosity;
|
||||||
let rs = Command::new("pacman")
|
let output = ShellCommand::pacman()
|
||||||
.arg("-Ss")
|
.arg("-Ss")
|
||||||
.arg(&a)
|
.arg(query)
|
||||||
.output()
|
.wait_with_output()
|
||||||
.expect("Something has gone wrong");
|
.silent_unwrap(AppExitCode::PacmanError)
|
||||||
|
.stdout;
|
||||||
let str = String::from_utf8(rs.stdout).unwrap();
|
|
||||||
|
|
||||||
if verbosity >= 1 {
|
if verbosity >= 1 {
|
||||||
log(format!(
|
log(format!(
|
||||||
"Found {} results for \"{}\" in repos",
|
"Found {} results for \"{}\" in repos",
|
||||||
&str.split('\n').count() / 2,
|
&output.split('\n').count() / 2,
|
||||||
&a
|
&query
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
print!("{}", str);
|
println!("{}", output)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue