diff --git a/.gitignore b/.gitignore index b7f9a44..9b9f24f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ target/ -ame -ame.exe .vscode .idea result diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 161f3a0..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,28 +0,0 @@ -# Crystal Linux Contributing Guidelines - -#### !! Always make sure to `git pull` before doing any work to avoid commit hell !! - -### Pre-Commit Checks - -- Make sure to `cargo fmt` your code before every commit push -- Unless in specific edge cases, don't push code that doesn't pass `cargo check` -- Try to correct any code with `cargo clippy` before you push - -### Formatting - -- UNIX line endings (LF instead of CRLF) -- 4 spaces per TAB - -### Good Practices - -- Try to use .unwrap() as little as possible -- Try to never use panic!() in production code, always try to have a possible way to resolve errors, even if it's just - unwrap_or/_else() -- Never use println!() or eprintln!() in finalised code. Using string functions (e.g. info() in Amethyst v3.0.0) is - preferred -- Compartmentalise as much as you can, avoid writing the exact same line of code 50 times if you can turn it into a - function - -### Examples of these guidelines in practice - -- https://git.getcryst.al/crystal/ame/src/branch/rewrite \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 4fc82ac..d4d8ea9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,22 +12,11 @@ dependencies = [ "mimalloc", "native-tls", "regex", - "rusqlite", + "rm_rf", "serde", "ureq", ] -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "atty" version = "0.2.14" @@ -142,18 +131,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - [[package]] name = "fastrand" version = "1.7.0" @@ -188,41 +165,12 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "getrandom" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" -[[package]] -name = "hashlink" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" -dependencies = [ - "hashbrown 0.11.2", -] - [[package]] name = "heck" version = "0.4.0" @@ -256,7 +204,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", - "hashbrown 0.12.1", + "hashbrown", ] [[package]] @@ -295,16 +243,6 @@ dependencies = [ "cc", ] -[[package]] -name = "libsqlite3-sys" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58" -dependencies = [ - "pkg-config", - "vcpkg", -] - [[package]] name = "log" version = "0.4.17" @@ -320,12 +258,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - [[package]] name = "mimalloc" version = "0.1.29" @@ -455,6 +387,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f446d0a6efba22928558c4fb4ce0b3fd6c89b0061343e390bf01a703742b8125" +dependencies = [ + "cc", +] + [[package]] name = "quote" version = "1.0.20" @@ -498,18 +439,12 @@ dependencies = [ ] [[package]] -name = "rusqlite" -version = "0.26.3" +name = "rm_rf" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7" +checksum = "3443b7a35aa12ed2e99edfc0ecbefe6a53b4848305cc83e29981dfa1aea1f71e" dependencies = [ - "bitflags", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "memchr", - "smallvec", + "stacker", ] [[package]] @@ -583,10 +518,17 @@ dependencies = [ ] [[package]] -name = "smallvec" -version = "1.9.0" +name = "stacker" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] [[package]] name = "strsim" @@ -723,12 +665,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index fffaa8c..aee3c3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "Amethyst" -version = "3.3.0" +version = "3.4.0" authors = ["michal ", "axtlos "] edition = "2021" description = "A fast and efficient AUR helper" @@ -21,9 +21,9 @@ codegen-units = 1 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 } libc = { version = "0.2.126", default-features = false } +rm_rf = { version = "0.6.2", default-features = false } \ No newline at end of file diff --git a/PKGBUILD b/PKGBUILD index 72f8546..2e038c0 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -3,8 +3,8 @@ # Developer: Michal S pkgname=amethyst -pkgver=3.3.0 -pkgrel=9 +pkgver=3.4.0 +pkgrel=1 pkgdesc="A fast and efficient AUR helper" arch=('x86_64') url="https://github.com/crystal-linux/amethyst" diff --git a/README.md b/README.md index 4d1675b..c24707d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- Logo + Logo

@@ -70,9 +70,13 @@ Tested on latest Cargo (1.60.0-nightly) - ~~Add clean function~~ - ~~Make flags global~~ - ~~Add pacdiff function~~ -- Squash any remaining bugs +- ~~Squash any remaining bugs~~ -#### v3.4.0 +#### v3.4.0 +- ~~New AUR upgrade checker~~ +- ~~Bugfixes, bugfixes, bugfixes!~~~ + +#### v3.5.0 - Implement some sort of spinner for longer operations - Allow editing of PKGBUILDs before install - Allow to choose provider of package diff --git a/flake.lock b/flake.lock index 3701dc5..d74e14e 100644 --- a/flake.lock +++ b/flake.lock @@ -1,8 +1,31 @@ { "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1658730563, + "narHash": "sha256-NDaSjaNdynCM02hRLOL76CKeD5Bxjxe8aNsD6AQ4U5I=", + "owner": "nix-community", + "repo": "fenix", + "rev": "80981ee71b32ce0747d22b1fd2dcd895219f5c1d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "naersk": { "inputs": { - "nixpkgs": "nixpkgs" + "nixpkgs": [ + "nixpkgs" + ] }, "locked": { "lastModified": 1655042882, @@ -20,40 +43,45 @@ }, "nixpkgs": { "locked": { - "lastModified": 1656755932, - "narHash": "sha256-TGThfOxr+HjFK464+UoUE6rClp2cwxjiKvHcBVdIGSQ=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "660ac43ff9ab1f12e28bfb31d4719795777fe152", - "type": "github" - }, - "original": { - "id": "nixpkgs", - "type": "indirect" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1656945861, - "narHash": "sha256-L41+pANwxKvhqqGRCKXzugOb81ewFJG95esLobRI1KY=", + "lastModified": 1658644204, + "narHash": "sha256-MWyfCH9K3eVTXJUxBi67OQSAh9jJAnvWklM6qm4j8w8=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4d26010c93091d4cb7fca36b8d3ecb33c89a6f23", + "rev": "2f0c3be57c348f4cfd8820f2d189e29a685d9c41", "type": "github" }, "original": { "owner": "nixos", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { + "fenix": "fenix", "naersk": "naersk", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs", "utils": "utils" } }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1658671895, + "narHash": "sha256-WFtdMN7WH5twFZEfBqpgc9PMCMHpgJnZyipDSPfui3U=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "7e2b983fd459977e11026683ee4afb9598960a4c", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, "utils": { "locked": { "lastModified": 1656928814, diff --git a/flake.nix b/flake.nix index f3e5eff..d362ddc 100644 --- a/flake.nix +++ b/flake.nix @@ -1,46 +1,89 @@ { inputs = { - nixpkgs.url = "github:nixos/nixpkgs"; + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; utils.url = "github:numtide/flake-utils"; - naersk.url = "github:nix-community/naersk"; + naersk = { + url = "github:nix-community/naersk"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { self, nixpkgs, utils, naersk }: - utils.lib.eachDefaultSystem (system: - let - pkgs = nixpkgs.legacyPackages."${system}"; - naersk-lib = naersk.lib."${system}"; - in rec - { - packages.amethyst = naersk-lib.buildPackage { - pname = "ame"; - root = ./.; - nativeBuildInputs = with pkgs; [ - openssl - sqlite - pkg-config - ]; - }; - - packages.default = packages.amethyst; - - apps.amethyst = utils.lib.mkApp { - drv = packages.amethyst; - }; - - apps.default = apps.amethyst; - - devShells.default = pkgs.mkShell { - nativeBuildInputs = with pkgs; [ - rustc - cargo - rustfmt - cargo-audit - clippy - openssl - sqlite - pkg-config - ]; - }; - }); + outputs = { + self, + nixpkgs, + utils, + naersk, + fenix, + }: + utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages."${system}"; + toolchain = with fenix.packages."${system}"; + combine [ + minimal.rustc + minimal.cargo + targets.x86_64-pc-windows-gnu.latest.rust-std + targets.x86_64-unknown-linux-gnu.latest.rust-std + ]; + naersk-lib = naersk.lib."${system}".override { + cargo = toolchain; + rustc = toolchain; + }; + in rec + { + packages.amethyst = naersk-lib.buildPackage { + pname = "Amethyst"; + root = ./.; + }; + + packages.amethyst-win = naersk-lib.buildPackage { + pname = "Amethyst"; + root = ./.; + strictDeps = true; + depsBuildBuild = with pkgs; [ + pkgsCross.mingwW64.stdenv.cc + pkgsCross.mingwW64.windows.pthreads + ]; + nativeBuildInputs = with pkgs; [ + ( + if system == "x86_64-linux" + then wineWowPackages.stable + else hello + ) + ]; + CARGO_BUILD_TARGET = "x86_64-pc-windows-gnu"; + CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUNNER = pkgs.writeScript "wine-wrapper" '' + export WINEPREFIX="$(mktemp -d)" + exec wine64 $@ + ''; + doCheck = true; + }; + + packages.default = packages.amethyst; + + apps.apod = utils.lib.mkApp { + drv = packages.amethyst; + }; + + apps.default = apps.amethyst; + + devShells.default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + rustc + cargo + cargo-audit + rustfmt + clippy + + openssl + sqlite + pkg-config + ]; + }; + + formatter = pkgs.alejandra; + }); } diff --git a/src/args.rs b/src/args.rs index ac1827c..f3d760e 100644 --- a/src/args.rs +++ b/src/args.rs @@ -57,6 +57,12 @@ pub struct InstallArgs { /// The name of the package(s) to install #[clap(required = true)] pub packages: Vec, + + /// Installs only from the AUR + pub aur: bool, + + /// Install the packages from the pacman-defined repositories + pub repo: bool, } #[derive(Default, Debug, Clone, Parser)] diff --git a/src/database/add.rs b/src/database/add.rs deleted file mode 100644 index ebcbf70..0000000 --- a/src/database/add.rs +++ /dev/null @@ -1,21 +0,0 @@ -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 = get_database_connection(); - - if options.verbosity >= 1 { - 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, &pkg.depends.join(" "), &pkg.make_depends.join(" ")], - ).unwrap_or_else(|e| - crash!(AppExitCode::FailedAddingPkg, "Failed adding package {} to the database: {}", pkg.name, e) - ); -} diff --git a/src/database/initialise.rs b/src/database/initialise.rs deleted file mode 100644 index e7e1214..0000000 --- a/src/database/initialise.rs +++ /dev/null @@ -1,41 +0,0 @@ -use rusqlite::Connection; -use std::env; -use std::path::Path; - -use crate::internal::exit_code::AppExitCode; -use crate::{crash, log, Options}; - -pub fn init(options: Options) { - let path = format!("{}/.local/share/ame/db.sqlite", env::var("HOME").unwrap()); - let dbpath = Path::new(&path); - let verbosity = options.verbosity; - - if verbosity >= 1 { - 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"); - } - - conn.execute( - "CREATE TABLE packages ( - name TEXT PRIMARY KEY NOT NULL, - version TEXT NOT NULL, - description TEXT, - depends BLOB, - make_depends BLOB - )", - [], - ) - .unwrap_or_else(|e| { - crash!( - AppExitCode::FailedInitDb, - "Couldn't initialise database: {}", - e, - ) - }); -} diff --git a/src/database/mod.rs b/src/database/mod.rs deleted file mode 100644 index 4eb9d9a..0000000 --- a/src/database/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -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; - -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 deleted file mode 100644 index 91e644e..0000000 --- a/src/database/query.rs +++ /dev/null @@ -1,63 +0,0 @@ -use rusqlite::Connection; -use std::env; -use std::path::Path; - -use crate::internal::rpc::Package; -use crate::{log, Options}; - -pub fn query(options: Options) -> Vec { - let verbosity = options.verbosity; - - if verbosity >= 1 { - log!("Connecting to database"); - } - - let conn = Connection::open(Path::new(&format!( - "{}/.local/share/ame/db.sqlite", - env::var("HOME").unwrap() - ))) - .expect("Couldn't connect to database"); - - if verbosity >= 1 { - log!("Querying database for input"); - } - - let mut rs = conn.prepare("SELECT * FROM packages;").unwrap(); - let packages_iter = rs - .query_map([], |row| { - Ok(Package { - name: row.get(0).unwrap(), - version: row.get(1).unwrap(), - description: row.get(2).unwrap(), - depends: row - .get::(3) - .unwrap() - .split(' ') - .map(|s| s.to_string()) - .collect::>(), - make_depends: row - .get::(4) - .unwrap() - .split(' ') - .map(|s| s.to_string()) - .collect::>(), - }) - }) - .expect("Couldn't query database for packages"); - - if verbosity >= 1 { - log!("Retrieved results"); - } - - let mut results: Vec = vec![]; - - for package in packages_iter { - results.push(package.unwrap()); - } - - if verbosity >= 1 { - log!("Collected results"); - } - - results -} diff --git a/src/database/remove.rs b/src/database/remove.rs deleted file mode 100644 index ef1e5da..0000000 --- a/src/database/remove.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::{log, Options}; - -use super::get_database_connection; - -pub fn remove(pkg: &str, options: Options) { - let conn = get_database_connection(); - - let verbosity = options.verbosity; - - if verbosity >= 1 { - log!("Removing package {} from database", pkg); - } - - conn.execute( - "DELETE FROM packages - WHERE EXISTS - (SELECT * - FROM packages - WHERE name = ?);", - [pkg], - ) - .expect("Couldn't delete package from database"); -} diff --git a/src/internal/clean.rs b/src/internal/clean.rs index ba51211..7ffe82a 100644 --- a/src/internal/clean.rs +++ b/src/internal/clean.rs @@ -3,10 +3,12 @@ use regex::Regex; use crate::{log, Options}; pub fn clean(a: &[String], options: Options) -> Vec { + // Strip versioning from package names let r = Regex::new(r"(\S+)((?:>=|<=|>|<|=\W)\S+$)").unwrap(); let mut cleaned: Vec = vec![]; let verbosity = options.verbosity; + // Push cleaned package names to vector for b in a { if r.captures_iter(b).count() > 0 { let c = r.captures(b).unwrap().get(1).map_or("", |m| m.as_str()); diff --git a/src/internal/detect.rs b/src/internal/detect.rs index 2f64a28..9553b56 100644 --- a/src/internal/detect.rs +++ b/src/internal/detect.rs @@ -1,20 +1,27 @@ use crate::internal::commands::ShellCommand; use crate::internal::error::SilentUnwrap; use crate::internal::exit_code::AppExitCode; -use crate::{prompt, warn}; +use crate::{info, prompt, warn}; pub fn detect() { + info!("Scanning for pacnew files"); + let mut pacnew = vec![]; - for entry in std::fs::read_dir("/etc").unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - if path.to_str().unwrap().contains(".pacnew") || path.to_str().unwrap().contains(".pacsave") - { - pacnew.push(path); + // Run `find` to find pacnew files and split by lines into a vec + let find = std::process::Command::new("sudo") + .arg("pacdiff") + .arg("-f") + .output() + .unwrap(); + let find_lines = std::str::from_utf8(&find.stdout).unwrap().split('\n'); + for line in find_lines { + if !line.is_empty() { + pacnew.push(line.to_string()); } } + // If pacnew files are found, warn the user and prompt to pacdiff if !pacnew.is_empty() { 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 { diff --git a/src/internal/error.rs b/src/internal/error.rs index 99977ad..b29a86d 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; diff --git a/src/internal/exit_code.rs b/src/internal/exit_code.rs index aa34dff..e04fde3 100644 --- a/src/internal/exit_code.rs +++ b/src/internal/exit_code.rs @@ -1,12 +1,10 @@ pub enum AppExitCode { RunAsRoot = 1, - FailedAddingPkg = 2, - FailedInitDb = 3, - FailedCreatingPaths = 4, - MissingDeps = 5, - UserCancellation = 6, - PacmanError = 7, - GitError = 8, - MakePkgError = 9, - Other = 102, + FailedCreatingPaths = 2, + MissingDeps = 3, + UserCancellation = 4, + PacmanError = 5, + GitError = 6, + MakePkgError = 7, + Other = 63, } diff --git a/src/internal/initialise.rs b/src/internal/initialise.rs index ae62244..bfafcbd 100644 --- a/src/internal/initialise.rs +++ b/src/internal/initialise.rs @@ -1,129 +1,62 @@ use std::env; use std::path::Path; -use std::process::Command; use crate::{crash, internal::exit_code::AppExitCode, log, Options}; pub fn init(options: Options) { + // Initialise variables let verbosity = options.verbosity; let homedir = env::var("HOME").unwrap(); + // Initialise stateful directory if !Path::new(&format!("{}/.local/share/ame", homedir)).exists() { - let r = std::fs::create_dir_all(format!("{}/.local/share/ame", homedir)); - match r { - Ok(_) => { - if verbosity >= 1 { - log!("Created path: {}/.local/share/ame", homedir); - } - } - Err(e) => { - crash!( - AppExitCode::FailedCreatingPaths, - "Couldn't create path: {}/.local/share/ame: {}", - homedir, - e, - ); - } + if verbosity >= 1 { + log!("Initialising stateful directory"); } + std::fs::create_dir_all(format!("{}/.local/share/ame", homedir)).unwrap_or_else(|e| { + crash!( + AppExitCode::FailedCreatingPaths, + "Failed to create stateful directory: {}", + e + ); + }); } - if !Path::new(&format!("{}/.local/share/ame/db.sqlite", homedir)).exists() { - crate::database::init(options); - } - + // If cache path doesn't exist, create it, if it does, delete it and recreate it if !Path::new(&format!("{}/.cache/ame/", homedir)).exists() { - let r = std::fs::create_dir_all(format!("{}/.cache/ame", homedir)); - match r { - Ok(_) => { - if verbosity >= 1 { - log!("Created path: {}/.cache/ame", homedir); - } - } - Err(e) => { - crash!( - AppExitCode::FailedCreatingPaths, - "Couldn't create path: {}/.cache/ame: {}", - homedir, - e, - ); - } + if verbosity >= 1 { + log!("Initialising cache directory"); } - } else { - let r = std::fs::remove_dir_all(format!("{}/.cache/ame", homedir)); - match r { - Ok(_) => { - if verbosity >= 1 { - log!("Removing cache: {}/.cache/ame", homedir); - } - } - Err(e) => { - crash!( - AppExitCode::FailedCreatingPaths, - "Couldn't remove path: {}/.cache/ame: {}", - homedir, - e, - ); - } - } - let r2 = std::fs::create_dir_all(format!("{}/.cache/ame", homedir)); - match r2 { - Ok(_) => { - if verbosity >= 1 { - log!("Created path: {}/.cache/ame", homedir); - } - } - Err(e2) => { - crash!( - AppExitCode::FailedCreatingPaths, - "Couldn't create path: {}/.cache/ame: {}", - homedir, - e2, - ); - } - } - } - - let r = Command::new("chmod") - .arg("-R") - .arg("770") - .arg(format!("{}/.cache/ame", homedir)) - .status(); - match r { - Ok(_) => { - if verbosity >= 1 { - log!("Set correct permissions for path: {}/.cache/ame", homedir); - } - } - Err(e) => { + std::fs::create_dir_all(format!("{}/.cache/ame", homedir)).unwrap_or_else(|e| { crash!( AppExitCode::FailedCreatingPaths, - "Couldn't set permissions for path: {}/.cache/ame: {}", + "Couldn't create path: {}/.cache/ame: {}", homedir, e, ); + }); + } else { + if verbosity >= 1 { + log!("Deleting cache directory"); } - }; - let r = Command::new("chmod") - .arg("-R") - .arg("770") - .arg(format!("{}/.local/share/ame", homedir)) - .status(); - match r { - Ok(_) => { - if verbosity >= 1 { - log!( - "Set correct permissions for path: {}/.local/share/ame", - homedir - ); - } - } - Err(e) => { + rm_rf::remove(format!("{}/.cache/ame", homedir)).unwrap_or_else(|e| { crash!( AppExitCode::FailedCreatingPaths, - "Couldn't set permissions for path: {}/.local/share/ame: {}", + "Couldn't remove path: {}/.cache/ame: {}", homedir, - e, - ); + e + ) + }); + if verbosity >= 1 { + log!("Creating cache directory"); } - }; + std::fs::create_dir_all(format!("{}/.cache/ame", homedir)).unwrap_or_else(|e| { + crash!( + AppExitCode::FailedCreatingPaths, + "Couldn't create path: {}/.cache/ame: {}", + homedir, + e + ) + }); + } } diff --git a/src/internal/mod.rs b/src/internal/mod.rs index 87befd6..91008f5 100644 --- a/src/internal/mod.rs +++ b/src/internal/mod.rs @@ -1,8 +1,12 @@ pub use clean::*; +pub use clean::*; pub use detect::*; pub use initialise::*; +pub use initialise::*; +pub use sort::*; pub use sort::*; use std::env; +pub use sudoloop::*; mod clean; pub mod commands; @@ -17,11 +21,6 @@ pub mod structs; pub(crate) mod utils; mod sudoloop; -pub use clean::*; -pub use initialise::*; -pub use sort::*; -pub use sudoloop::*; - #[macro_export] macro_rules! uwu { ($x:expr) => {{ diff --git a/src/internal/rpc.rs b/src/internal/rpc.rs index 341d32a..f71d6fe 100644 --- a/src/internal/rpc.rs +++ b/src/internal/rpc.rs @@ -31,10 +31,15 @@ pub struct InfoResults { pub const URL: &str = "https://aur.archlinux.org/"; pub fn rpcinfo(pkg: String) -> InfoResults { + // Initialise TLS connector let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap()); + + // Build request agent let agent = ureq::AgentBuilder::new() .tls_connector(tls_connector) .build(); + + // Send request and parse results into json let res: SearchResults = agent .get(&format!( "https://aur.archlinux.org/rpc/?v=5&type=info&arg={}", @@ -45,6 +50,7 @@ pub fn rpcinfo(pkg: String) -> InfoResults { .into_json() .unwrap(); + // Check if package was found if res.results.is_empty() { InfoResults { found: false, @@ -59,10 +65,15 @@ pub fn rpcinfo(pkg: String) -> InfoResults { } pub fn rpcsearch(pkg: String) -> SearchResults { + // Initialise TLS connector let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap()); + + // Build request agent let agent = ureq::AgentBuilder::new() .tls_connector(tls_connector) .build(); + + // Send request and parse results into json agent .get(&format!( "https://aur.archlinux.org/rpc/?v=5&type=search&arg={}", diff --git a/src/internal/sort.rs b/src/internal/sort.rs index d64747c..78ea61b 100644 --- a/src/internal/sort.rs +++ b/src/internal/sort.rs @@ -4,11 +4,13 @@ use crate::internal::{clean, rpc, structs}; use crate::{log, Options}; pub fn sort(input: &[String], options: Options) -> structs::Sorted { + // Initialise variables let mut repo: Vec = vec![]; let mut aur: Vec = vec![]; let mut nf: Vec = vec![]; let verbosity = options.verbosity; + // Sanitise all packages passed in let a = clean(input, options); if verbosity >= 1 { @@ -16,6 +18,7 @@ pub fn sort(input: &[String], options: Options) -> structs::Sorted { } for b in a { + // Check if package is in the repos let rs = Command::new("pacman") .arg("-Ss") .arg(format!("^{}$", &b)) @@ -24,16 +27,19 @@ pub fn sort(input: &[String], options: Options) -> structs::Sorted { .expect("Something has gone wrong"); if let Some(0) = rs.code() { + // If it is, add it to the repo vector if verbosity >= 1 { log!("{} found in repos", b); } repo.push(b.to_string()); } else if rpc::rpcinfo(b.to_string()).found { + // Otherwise, check if it is in the AUR, if it is, add it to the AUR vector if verbosity >= 1 { log!("{} found in AUR", b); } aur.push(b.to_string()); } else { + // Otherwise, add it to the not found vector if verbosity >= 1 { log!("{} not found", b); } diff --git a/src/internal/sudoloop.rs b/src/internal/sudoloop.rs index a4f6b3a..147a503 100644 --- a/src/internal/sudoloop.rs +++ b/src/internal/sudoloop.rs @@ -1,7 +1,8 @@ -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(); diff --git a/src/internal/utils.rs b/src/internal/utils.rs index c96485a..eadb0ec 100644 --- a/src/internal/utils.rs +++ b/src/internal/utils.rs @@ -1,3 +1,4 @@ +use colored::*; use std::io; use std::io::Write; use std::process::exit; @@ -5,7 +6,6 @@ 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 = "❌"; diff --git a/src/main.rs b/src/main.rs index ee61b35..670b3e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,12 @@ +// #![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] +// #![allow(clippy::too_many_lines)] + use args::Args; use clap::Parser; use internal::commands::ShellCommand; 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::{init, sort, start_sudoloop, structs::Options}; @@ -12,32 +14,38 @@ use crate::internal::{init, sort, start_sudoloop, structs::Options}; static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; mod args; -mod database; mod internal; mod operations; fn main() { + // Break if we are running as root 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"); } + // Parse arguments let args: Args = Args::parse(); + // Initialize variables let verbosity = args.verbose as i32; let noconfirm = args.no_confirm; + // Get options struct let options = Options { verbosity, noconfirm, asdeps: false, }; + // Ensure amethyst is initialized init(options); + // Start sudoloop if specified if args.sudoloop { start_sudoloop(); } + // Match args match args.subcommand.unwrap_or_default() { Operation::Install(install_args) => cmd_install(install_args, options), Operation::Remove(remove_args) => cmd_remove(remove_args, options), @@ -52,23 +60,17 @@ fn main() { operations::clean(options); } } - - detect(); } fn cmd_install(args: InstallArgs, options: Options) { + // Initialise variables 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); - } - if !sorted.aur.is_empty() { - operations::aur_install(sorted.aur, options); - } if !sorted.nf.is_empty() { + // If some packages are not found, crash crash!( AppExitCode::PacmanError, "Couldn't find packages: {} in repos or the AUR", @@ -76,41 +78,46 @@ fn cmd_install(args: InstallArgs, options: Options) { ); } - 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" ); + if !sorted.repo.is_empty() { + // If repo packages found, install them + operations::install(sorted.repo, options); + } + if !sorted.aur.is_empty() { + // If AUR packages found, install them + operations::aur_install(sorted.aur, options); } } fn cmd_remove(args: RemoveArgs, options: Options) { + // Initialise variables let packages = args.packages; + info!("Uninstalling packages: {}", &packages.join(", ")); + + // Remove packages operations::uninstall(packages, options); } fn cmd_search(args: SearchArgs, options: Options) { + // Initialise variables let query_string = args.search.join(" "); if args.aur { info!("Searching AUR for {}", &query_string); + + // Search AUR operations::aur_search(&query_string, options); } if args.repo { info!("Searching repos for {}", &query_string); + + // Search repos operations::search(&query_string, options); } if !args.aur && !args.repo { info!("Searching AUR and repos for {}", &query_string); + + // If no search type specified, search both operations::search(&query_string, options); operations::aur_search(&query_string, options); } @@ -118,18 +125,21 @@ fn cmd_search(args: SearchArgs, options: Options) { fn cmd_query(args: QueryArgs) { if args.aur { + // If AUR query, query AUR ShellCommand::pacman() .arg("-Qm") .wait_success() .silent_unwrap(AppExitCode::PacmanError); } if args.repo { + // If repo query, query repos ShellCommand::pacman() .arg("-Qn") .wait_success() .silent_unwrap(AppExitCode::PacmanError); } if !args.repo && !args.aur { + // If no query type specified, query both ShellCommand::pacman() .arg("-Qn") .wait_success() diff --git a/src/operations/aur_install.rs b/src/operations/aur_install.rs index 065f443..f8ed002 100644 --- a/src/operations/aur_install.rs +++ b/src/operations/aur_install.rs @@ -11,6 +11,7 @@ use crate::internal::rpc::rpcinfo; use crate::{crash, info, log, prompt, Options}; pub fn aur_install(a: Vec, options: Options) { + // Initialise variables let url = crate::internal::rpc::URL; let cachedir = format!("{}/.cache/ame/", env::var("HOME").unwrap()); let verbosity = options.verbosity; @@ -23,12 +24,15 @@ pub fn aur_install(a: Vec, options: Options) { info!("Installing packages {} from the AUR", a.join(", ")); for package in a { + // Query AUR for package info let rpcres = rpcinfo(package); if !rpcres.found { + // If package isn't found, break break; } + // Get package name let pkg = &rpcres.package.as_ref().unwrap().name; if verbosity >= 1 { @@ -37,6 +41,7 @@ pub fn aur_install(a: Vec, options: Options) { info!("Cloning package source"); + // Clone package into cachedir set_current_dir(Path::new(&cachedir)).unwrap(); ShellCommand::git() .arg("clone") @@ -61,7 +66,7 @@ pub fn aur_install(a: Vec, options: Options) { ); } - // dep sorting + // Sort dependencies and makedepends log!("Sorting dependencies"); let sorted = crate::internal::sort(&rpcres.package.as_ref().unwrap().depends, options); log!("Sorting make dependencies"); @@ -73,12 +78,14 @@ pub fn aur_install(a: Vec, options: Options) { log!("Sorted makedepends for {} are:\n{:?}", pkg, &md_sorted); } + // Create newopts struct for installing dependencies let newopts = Options { verbosity, noconfirm, asdeps: true, }; + // If dependencies are not found in AUR or repos, crash if !sorted.nf.is_empty() || !md_sorted.nf.is_empty() { crash!( AppExitCode::MissingDeps, @@ -89,6 +96,7 @@ pub fn aur_install(a: Vec, options: Options) { } if !noconfirm { + // Prompt user to view PKGBUILD let p1 = prompt!(default false, "Would you like to review {}'s PKGBUILD (and any .install files if present)?", pkg @@ -96,6 +104,7 @@ pub fn aur_install(a: Vec, options: Options) { let editor: &str = &env::var("PAGER").unwrap_or_else(|_| "less".parse().unwrap()); if p1 { + // Open PKGBUILD in pager Command::new(editor) .arg(format!("{}/PKGBUILD", pkg)) .spawn() @@ -103,6 +112,7 @@ pub fn aur_install(a: Vec, options: Options) { .wait() .unwrap(); + // Check if any .install files are present let status = ShellCommand::bash() .arg("-c") .arg(format!("ls {}/*.install &> /dev/null", pkg)) @@ -110,6 +120,7 @@ pub fn aur_install(a: Vec, options: Options) { .silent_unwrap(AppExitCode::Other); if status.success() { + // If so, open them too ShellCommand::bash() .arg("-c") .arg(format!("{} {}/*.install", editor, pkg)) @@ -117,17 +128,19 @@ pub fn aur_install(a: Vec, options: Options) { .silent_unwrap(AppExitCode::Other); } + // Prompt user to continue let p2 = prompt!(default true, "Would you still like to install {}?", pkg); if !p2 { + // If not, crash fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap(); crash!(AppExitCode::UserCancellation, "Not proceeding"); } } } - // dep installing info!("Moving on to install dependencies"); + // Install dependencies and makedepends if !sorted.repo.is_empty() { crate::operations::install(sorted.repo, newopts); crate::operations::install(md_sorted.repo, newopts); @@ -137,6 +150,7 @@ pub fn aur_install(a: Vec, options: Options) { crate::operations::aur_install(md_sorted.aur, newopts); } + // Build makepkg args let mut makepkg_args = vec!["-rsci", "--skippgp"]; if options.asdeps { makepkg_args.push("--asdeps") @@ -145,8 +159,9 @@ pub fn aur_install(a: Vec, options: Options) { makepkg_args.push("--noconfirm") } - // package building and installing info!("Building time!"); + + // Enter cachedir and build package set_current_dir(format!("{}/{}", cachedir, pkg)).unwrap(); let status = ShellCommand::makepkg() .args(makepkg_args) @@ -154,6 +169,7 @@ pub fn aur_install(a: Vec, options: Options) { .silent_unwrap(AppExitCode::MakePkgError); if !status.success() { + // If build failed, crash fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap(); crash!( AppExitCode::PacmanError, @@ -162,10 +178,10 @@ pub fn aur_install(a: Vec, options: Options) { ); } + // Return to cachedir set_current_dir(&cachedir).unwrap(); - remove_dir_all(format!("{}/{}", cachedir, &pkg)).unwrap(); - // pushes package to database - crate::database::add(rpcres.package.unwrap(), options); + // Remove package from cache + remove_dir_all(format!("{}/{}", cachedir, &pkg)).unwrap(); } } diff --git a/src/operations/clean.rs b/src/operations/clean.rs index a7dceeb..e8dfe1b 100644 --- a/src/operations/clean.rs +++ b/src/operations/clean.rs @@ -13,29 +13,35 @@ pub fn clean(options: Options) { let verbosity = options.verbosity; let noconfirm = options.noconfirm; + // Check for orphaned packages let orphaned_packages = ShellCommand::pacman() .arg("-Qdtq") .wait_with_output() .silent_unwrap(AppExitCode::PacmanError); - if orphaned_packages.stdout.as_str() == "" { + if orphaned_packages.stdout.as_str().is_empty() { + // If no orphaned packages found, do nothing info!("No orphaned packages found"); } else { + // Prompt users whether to remove orphaned packages info!( "Removing orphans would uninstall the following packages: \n{}", &orphaned_packages.stdout ); let cont = prompt!(default false, "Continue?"); if !cont { + // If user doesn't want to continue, break info!("Exiting"); std::process::exit(AppExitCode::PacmanError as i32); } + // Build pacman args let mut pacman_args = vec!["-Rns"]; if noconfirm { pacman_args.push("--noconfirm"); } + // Collect orphaned packages into a vector let orphaned_packages_vec = orphaned_packages.stdout.split('\n').collect::>(); for package in &orphaned_packages_vec { if !package.is_empty() { @@ -47,6 +53,7 @@ pub fn clean(options: Options) { log!("Removing orphans: {:?}", orphaned_packages_vec); } + // Remove orphaned packages let pacman_result = ShellCommand::pacman() .elevated() .args(pacman_args) @@ -54,23 +61,29 @@ pub fn clean(options: Options) { .silent_unwrap(AppExitCode::PacmanError); if pacman_result.success() { + // If pacman succeeded, notify user info!("Successfully removed orphans"); } else { + // If pacman failed, crash crash!(AppExitCode::PacmanError, "Failed to remove orphans",); } } + // Prompt the user whether to clear cache or not let clear_cache = if !noconfirm { prompt!(default false, "Also clear pacman's package cache?") } else { true }; + if clear_cache { + // Build pacman args let mut pacman_args = vec!["-Sc"]; if noconfirm { pacman_args.push("--noconfirm"); } + // Build paccache args let mut paccache_args = vec!["-r"]; if noconfirm { paccache_args.push("--noconfirm"); @@ -80,6 +93,7 @@ pub fn clean(options: Options) { log!("Clearing using `paccache -r`"); } + // Clear pacman's cache (keeping latest 3 versions of installed packages) Command::new("sudo") .arg("paccache") .args(paccache_args) @@ -98,6 +112,7 @@ pub fn clean(options: Options) { log!("Clearing using `pacman -Sc`"); } + // Clear pacman's cache (keeping only installed packages) let pacman_result = ShellCommand::pacman() .elevated() .args(pacman_args) @@ -105,8 +120,10 @@ pub fn clean(options: Options) { .silent_unwrap(AppExitCode::PacmanError); if pacman_result.success() { + // If pacman succeeded, notify user info!("Successfully cleared package cache"); } else { + // If pacman failed, crash crash!(AppExitCode::PacmanError, "Failed to clear package cache",); } } diff --git a/src/operations/install.rs b/src/operations/install.rs index 2de6272..d1b8ea2 100644 --- a/src/operations/install.rs +++ b/src/operations/install.rs @@ -5,6 +5,8 @@ use crate::{crash, info, log, Options}; pub fn install(packages: Vec, options: Options) { info!("Installing packages {} from repos", &packages.join(", ")); + + // Build pacman args let mut opers = vec!["-S", "--needed"]; if options.noconfirm { opers.push("--noconfirm"); @@ -19,6 +21,7 @@ pub fn install(packages: Vec, options: Options) { log!("Installing from repos: {:?}", &packages); } + // Install packages let status = ShellCommand::pacman() .elevated() .args(opers) @@ -26,6 +29,7 @@ pub fn install(packages: Vec, options: Options) { .wait() .silent_unwrap(AppExitCode::PacmanError); if !status.success() { + // If pacman failed, crash crash!( AppExitCode::PacmanError, "An error occured while installing packages: {}, aborting", diff --git a/src/operations/search.rs b/src/operations/search.rs index 0303602..1f4f786 100644 --- a/src/operations/search.rs +++ b/src/operations/search.rs @@ -5,9 +5,13 @@ use crate::internal::rpc::rpcsearch; use crate::{log, Options}; pub fn aur_search(query: &str, options: Options) { + // Initialise variables let verbosity = options.verbosity; + + // Query AUR for package info let res = rpcsearch(query.to_string()); + // Format output for package in &res.results { println!( "aur/{} {}\n {}", @@ -26,7 +30,10 @@ pub fn aur_search(query: &str, options: Options) { } pub fn repo_search(query: &str, options: Options) { + // Initialise variables let verbosity = options.verbosity; + + // Query pacman for package info let output = ShellCommand::pacman() .arg("-Ss") .arg(query) diff --git a/src/operations/uninstall.rs b/src/operations/uninstall.rs index 646e65a..1a392ff 100644 --- a/src/operations/uninstall.rs +++ b/src/operations/uninstall.rs @@ -7,9 +7,9 @@ use crate::internal::exit_code::AppExitCode; use crate::{log, Options}; pub fn uninstall(packages: Vec, options: Options) { + // Build pacman args let mut pacman_args = vec!["-Rs"]; pacman_args.append(&mut packages.iter().map(|s| s.as_str()).collect()); - if options.noconfirm { pacman_args.push("--noconfirm"); } @@ -18,6 +18,7 @@ pub fn uninstall(packages: Vec, options: Options) { log!("Uninstalling: {:?}", &packages); } + // Uninstall packages ShellCommand::pacman() .elevated() .args(pacman_args) @@ -29,7 +30,7 @@ pub fn uninstall(packages: Vec, options: Options) { } for package in packages { - crate::database::remove(&package, options); + // Remove old cache directory if Path::new(&format!( "{}/.cache/ame/{}", env::var("HOME").unwrap(), diff --git a/src/operations/upgrade.rs b/src/operations/upgrade.rs index 3ba5b47..0de1221 100644 --- a/src/operations/upgrade.rs +++ b/src/operations/upgrade.rs @@ -1,14 +1,23 @@ use crate::internal::commands::ShellCommand; +use crate::internal::detect; use crate::internal::error::SilentUnwrap; use crate::internal::exit_code::AppExitCode; use crate::internal::rpc::rpcinfo; use crate::operations::aur_install::aur_install; use crate::{info, log, prompt, Options}; +#[derive(Debug)] +struct QueriedPackage { + pub name: String, + pub version: String, +} + pub fn upgrade(options: Options) { + // Initialise variables let verbosity = options.verbosity; let noconfirm = options.noconfirm; + // Build pacman args let mut pacman_args = vec!["-Syu"]; if noconfirm { pacman_args.push("--noconfirm"); @@ -18,6 +27,7 @@ pub fn upgrade(options: Options) { log!("Upgrading repo packages"); } + // Upgrade repo packages let pacman_result = ShellCommand::pacman() .elevated() .args(pacman_args) @@ -25,39 +35,85 @@ pub fn upgrade(options: Options) { .silent_unwrap(AppExitCode::PacmanError); if pacman_result.success() { + // If pacman was successful, notify user info!("Successfully upgraded repo packages"); } else { + // Otherwise, prompt user whether to continue let cont = prompt!(default false, "Failed to upgrade repo packages, continue to upgrading AUR packages?", ); if !cont { + // If user doesn't want to continue, break info!("Exiting"); std::process::exit(AppExitCode::PacmanError as i32); } } if verbosity >= 1 { - log!("Upgrading AUR packages"); + log!("Checking AUR upgrades..."); } - let res = crate::database::query(options); + // List non-native packages using `pacman -Qm` and collect to a Vec + let non_native = ShellCommand::pacman() + .arg("-Qm") + .args(&["--color", "never"]) + .wait_with_output() + .silent_unwrap(AppExitCode::PacmanError); + + // Collect by lines to a Vec + let mut non_native = non_native.stdout.split('\n').collect::>(); + + // Remove last element, which is an empty line + non_native.pop(); + + // Parse non-native packages into a Vec + let mut parsed_non_native: Vec = vec![]; + for pkg in non_native { + // Split by space + let split = pkg.split(' ').collect::>(); + if verbosity >= 1 { + log!("{:?}", split); + } + // Create QueriedPackage and push it to parsed_non_native + let name = split[0].to_string(); + let version = split[1].to_string(); + parsed_non_native.push(QueriedPackage { name, version }); + } if verbosity >= 1 { - log!("{:?}", &res); + log!("{:?}", &parsed_non_native); } + // Check if AUR package versions are the same as installed 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 pkg in parsed_non_native { + // Query AUR + let rpc_result = rpcinfo((&*pkg.name).to_string()); + + if !rpc_result.found { + // If package not found, skip + continue; + } + + // If versions differ, push to a vector + if rpc_result.package.unwrap().version != pkg.version { + aur_upgrades.push(pkg.name); } } + // If vector isn't empty, prompt to install AUR packages from vector, effectively upgrading if !aur_upgrades.is_empty() { - aur_install(aur_upgrades, options); + let cont = prompt!(default false, + "Found AUR packages {} have new versions available, upgrade?", + aur_upgrades.join(", "), + ); + if cont { + aur_install(aur_upgrades, options); + }; } else { info!("No upgrades available for installed AUR packages"); } + + // Check for .pacnew files + detect(); }