diff --git a/src/apis/mod.rs b/src/apis/mod.rs new file mode 100644 index 0000000..f4da7e8 --- /dev/null +++ b/src/apis/mod.rs @@ -0,0 +1 @@ +pub mod pacman; diff --git a/src/apis/pacman.rs b/src/apis/pacman.rs new file mode 100644 index 0000000..0f7f4a0 --- /dev/null +++ b/src/apis/pacman.rs @@ -0,0 +1,98 @@ +/* + Rust Arch Linux Updater + Copyright (C) 2024 Luke Harding + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU General Public License along with this program. If not, see . +*/ + +/* + pacman_api.rs + This module provides an api to make working with Pacman much easier. +*/ + +use std::{error, fmt, result}; +use std::fmt::Formatter; +use std::path::Path; + +use crate::shell_commands; + +type Result = result::Result>; + +#[derive(Debug, Clone)] +enum PacmanError { + NotInstalled, + ExitCode(i32), + Other(&'static str), +} + +impl fmt::Display for PacmanError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let out = match self { + PacmanError::NotInstalled => "Pacman not installed.".to_string(), + PacmanError::ExitCode(code) => format!("Pacman exited with an exit code: {}", code), + PacmanError::Other(msg) => msg.to_string(), + }; + + write!(f, "PacmanError: {}", out) + } +} + +impl error::Error for PacmanError {} + +pub fn check() -> Result<()> { + let out = shell_commands::execute_quiet("which", ["pacman"])?; + + let path = if out.status.success() { + let mut stdout = out.stdout; + stdout.pop(); // Remove \n from stdout + + String::from_utf8(stdout)? + } else { + String::from("/usr/bin/pacman") + }; + + let path = Path::new(&path); + + if !path.exists() { + return Err(PacmanError::NotInstalled.into()); + } + + Ok(()) +} + +pub fn update_all() -> Result<()> { + let exit_status = shell_commands::execute_in_sh("sudo pacman -Syu")?; + + if !exit_status.success() { + let exit_code = match exit_status.code() { + Some(exit_code) => exit_code, + None => return Err(PacmanError::Other("No Exit Code Found").into()), + }; + + return Err(PacmanError::ExitCode(exit_code).into()); + } + + Ok(()) +} + +pub fn get_unused() -> Result> { + let out = shell_commands::execute_quiet("pacman", ["-Qtdq"])?; + + let result = String::from_utf8(out.stdout)?; + + let pkgs_to_remove: Vec = result.lines().map(|x| x.to_string()).collect(); + + Ok(pkgs_to_remove) +} + +pub fn remove_unused(packages: Vec) -> Result<()> { + let package_string = packages.join(" "); + let command = format!("sudo pacman -Rns {}", package_string); + + let out = shell_commands::execute_in_sh(command)?; + + dbg!(&out); + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 7fb8085..fe7dec9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,13 +13,15 @@ use colored::{ColoredString, Colorize}; -mod pacman_api; +use apis::pacman; + +mod apis; mod shell_commands; fn main() { println!("{}", copyright_notice()); - if let Err(e) = pacman_api::check() { + if let Err(e) = pacman::check() { error_println(e.to_string()); return; } else { @@ -27,13 +29,13 @@ fn main() { } notice_println("Running pacman update"); - if let Err(e) = pacman_api::update_all() { + if let Err(e) = pacman::update_all() { error_println(e.to_string()); return; } notice_println("Getting orphaned packages"); - let result = match pacman_api::get_unused() { + let result = match pacman::get_unused() { Ok(result) => result, Err(e) => { error_println(e.to_string()); @@ -43,12 +45,12 @@ fn main() { if result.is_empty() { println!("No Orphaned Packages Found.") - } else if let Err(e) = pacman_api::remove_unused(result) { + } else if let Err(e) = pacman::remove_unused(result) { error_println(e.to_string()); return; } - notice_println("Do Something Else"); + notice_println("\nUpdate process complete!"); } fn copyright_notice() -> ColoredString { diff --git a/src/pacman_api.rs b/src/pacman_api.rs deleted file mode 100644 index cec405e..0000000 --- a/src/pacman_api.rs +++ /dev/null @@ -1,124 +0,0 @@ -/* - Rust Arch Linux Updater - Copyright (C) 2024 Luke Harding - This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . -*/ - -/* - pacman_api.rs - This module provides an api to make working with Pacman much easier. -*/ - -use std::{error, fmt, io}; -use std::fmt::Formatter; -use std::io::Read; -use std::path::Path; - -use crate::shell_commands; - -#[derive(Debug)] -pub enum PacmanError { - InsufficientPrivilege, - NotInstalled, - IoError(io::Error), - Other(String), -} - -impl fmt::Display for PacmanError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let output = match self { - PacmanError::InsufficientPrivilege => "Failed to run pacman. Maybe try sudo?", - PacmanError::NotInstalled => "Unable to find pacman. Is this system arch based?", - PacmanError::Other(msg) => msg, - _ => "Unexpected Error Occurred", - }; - - write!(f, "{}", output) - } -} - -impl error::Error for PacmanError {} - -pub fn check() -> Result<(), PacmanError> { - let out = match shell_commands::execute_quiet("which", ["pacman"]) { - Ok(out) => out, - Err(e) => return Err(PacmanError::IoError(e)), - }; - - let path = if out.status.success() { - let mut stdout = out.stdout; - stdout.pop(); // Remove \n from stdout - - match String::from_utf8(stdout) { - Ok(result) => result, - Err(e) => return Err(PacmanError::Other(e.to_string())), - } - } else { - String::from("/usr/bin/pacman") - }; - - let path = Path::new(&path); - - if !path.exists() { - return Err(PacmanError::NotInstalled); - } - - Ok(()) -} - -pub fn update_all() -> Result<(), PacmanError> { - let out = match shell_commands::execute_in_sh("sudo pacman -Syu") { - Ok(out) => out, - Err(e) => return Err(PacmanError::IoError(e)), - }; - - let mut child_stderr = match out.stderr { - Some(stderr) => stderr, - None => return Ok(()), - }; - - let mut stderr_out = String::new(); - if let Err(e) = child_stderr.read_to_string(&mut stderr_out) { - return Err(PacmanError::Other(e.to_string())); - } - - if stderr_out.is_empty() { - Ok(()) - } else if stderr_out == "error: you cannot perform this operation unless you are root.\n" { - Err(PacmanError::InsufficientPrivilege) - } else { - Err(PacmanError::Other(stderr_out)) - } -} - -#[derive(Debug)] -struct GetUnusedStatus {} - -pub fn get_unused() -> Result { - let out = match shell_commands::execute_quiet("pacman", ["-Qtdq"]) { - Ok(out) => out, - Err(e) => return Err(PacmanError::IoError(e)), - }; - - let result = match String::from_utf8(out.stdout) { - Ok(out) => out, - Err(e) => return Err(PacmanError::Other(e.to_string())), - }; - - Ok(result) -} - -pub fn remove_unused(packages: String) -> Result<(), PacmanError> { - let command = format!("sudo pacman -Rns {}", packages); - - let out = match shell_commands::execute_in_sh(command) { - Ok(out) => out, - Err(e) => return Err(PacmanError::IoError(e)), - }; - - dbg!(&out); - - Ok(()) -} diff --git a/src/shell_commands.rs b/src/shell_commands.rs index 472055b..4346e93 100644 --- a/src/shell_commands.rs +++ b/src/shell_commands.rs @@ -13,24 +13,21 @@ use std::ffi::OsStr; use std::io; -use std::process::{Child, Command, Output, Stdio}; +use std::process::{Command, ExitStatus, Output}; -pub fn execute_and_display, I>(cmd: S, args: I) -> io::Result +pub fn execute_and_display, I>(cmd: S, args: I) -> io::Result where I: IntoIterator, I::Item: AsRef, { - let mut child = Command::new(cmd) - .args(args) - .stderr(Stdio::piped()) - .spawn()?; + let mut child = Command::new(cmd).args(args).spawn()?; - child.wait()?; + let exit_status = child.wait()?; - Ok(child) + Ok(exit_status) } -pub fn execute_in_sh(cmd: impl Into) -> io::Result { +pub fn execute_in_sh(cmd: impl Into) -> io::Result { execute_and_display("sh", ["-c", &cmd.into()]) }