Merge pull request #15 from crystal-linux/development

v3.4.0 Development -> Main
i18n
Michal 2 years ago committed by GitHub
commit ed89dccfa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

2
.gitignore vendored

@ -1,6 +1,4 @@
target/ target/
ame
ame.exe
.vscode .vscode
.idea .idea
result result

@ -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

114
Cargo.lock generated

@ -12,22 +12,11 @@ dependencies = [
"mimalloc", "mimalloc",
"native-tls", "native-tls",
"regex", "regex",
"rusqlite", "rm_rf",
"serde", "serde",
"ureq", "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]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.14"
@ -142,18 +131,6 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 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]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.7.0" version = "1.7.0"
@ -188,41 +165,12 @@ dependencies = [
"percent-encoding", "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]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.1" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" 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]] [[package]]
name = "heck" name = "heck"
version = "0.4.0" version = "0.4.0"
@ -256,7 +204,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown 0.12.1", "hashbrown",
] ]
[[package]] [[package]]
@ -295,16 +243,6 @@ dependencies = [
"cc", "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]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.17"
@ -320,12 +258,6 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]] [[package]]
name = "mimalloc" name = "mimalloc"
version = "0.1.29" version = "0.1.29"
@ -455,6 +387,15 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "psm"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f446d0a6efba22928558c4fb4ce0b3fd6c89b0061343e390bf01a703742b8125"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.20" version = "1.0.20"
@ -498,18 +439,12 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rusqlite" name = "rm_rf"
version = "0.26.3" version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7" checksum = "3443b7a35aa12ed2e99edfc0ecbefe6a53b4848305cc83e29981dfa1aea1f71e"
dependencies = [ dependencies = [
"bitflags", "stacker",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"memchr",
"smallvec",
] ]
[[package]] [[package]]
@ -583,10 +518,17 @@ dependencies = [
] ]
[[package]] [[package]]
name = "smallvec" name = "stacker"
version = "1.9.0" version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
dependencies = [
"cc",
"cfg-if",
"libc",
"psm",
"winapi",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
@ -723,12 +665,6 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 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]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

@ -1,6 +1,6 @@
[package] [package]
name = "Amethyst" name = "Amethyst"
version = "3.3.0" version = "3.4.0"
authors = ["michal <michal@tar.black>", "axtlos <axtlos@tar.black>"] authors = ["michal <michal@tar.black>", "axtlos <axtlos@tar.black>"]
edition = "2021" edition = "2021"
description = "A fast and efficient AUR helper" description = "A fast and efficient AUR helper"
@ -21,9 +21,9 @@ codegen-units = 1
mimalloc = { version = "0.1.29", default-features = false } mimalloc = { version = "0.1.29", default-features = false }
clap = { version = "3.2.8", features = [ "derive", "wrap_help" ] } clap = { version = "3.2.8", features = [ "derive", "wrap_help" ] }
regex = { version = "1.5.6", default-features = false, features = [ "std", "unicode-perl" ] } regex = { version = "1.5.6", default-features = false, features = [ "std", "unicode-perl" ] }
rusqlite = { version = "0.26.3", default-features = false }
colored = "2.0.0" colored = "2.0.0"
ureq = { version = "2.4.0", default-features = false, features = [ "native-tls", "json" ] } ureq = { version = "2.4.0", default-features = false, features = [ "native-tls", "json" ] }
serde = { version = "1.0.138", default-features = false, features = [ "derive", "serde_derive" ] } serde = { version = "1.0.138", default-features = false, features = [ "derive", "serde_derive" ] }
native-tls = { version = "0.2.10", default-features = false } native-tls = { version = "0.2.10", default-features = false }
libc = { version = "0.2.126", default-features = false } libc = { version = "0.2.126", default-features = false }
rm_rf = { version = "0.6.2", default-features = false }

@ -3,8 +3,8 @@
# Developer: Michal S <michal[at]tar[dot]black> # Developer: Michal S <michal[at]tar[dot]black>
pkgname=amethyst pkgname=amethyst
pkgver=3.3.0 pkgver=3.4.0
pkgrel=9 pkgrel=1
pkgdesc="A fast and efficient AUR helper" pkgdesc="A fast and efficient AUR helper"
arch=('x86_64') arch=('x86_64')
url="https://github.com/crystal-linux/amethyst" url="https://github.com/crystal-linux/amethyst"

@ -1,6 +1,6 @@
<p align="center"> <p align="center">
<a href="https://github.com/crystal-linux/amethyst/"> <a href="https://github.com/crystal-linux/amethyst/">
<img src="https://getcryst.al/site/assets/other/logo.png" alt="Logo" width="150" height="150"> <img src="https://getcryst.al/assets/img/crystal-logo-minimal.png" alt="Logo" width="150" height="150">
</a> </a>
</p> </p>
@ -70,9 +70,13 @@ Tested on latest Cargo (1.60.0-nightly)
- ~~Add clean function~~ - ~~Add clean function~~
- ~~Make flags global~~ - ~~Make flags global~~
- ~~Add pacdiff function~~ - ~~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 - Implement some sort of spinner for longer operations
- Allow editing of PKGBUILDs before install - Allow editing of PKGBUILDs before install
- Allow to choose provider of package - Allow to choose provider of package

@ -1,8 +1,31 @@
{ {
"nodes": { "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": { "naersk": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs" "nixpkgs": [
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1655042882, "lastModified": 1655042882,
@ -20,40 +43,45 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1656755932, "lastModified": 1658644204,
"narHash": "sha256-TGThfOxr+HjFK464+UoUE6rClp2cwxjiKvHcBVdIGSQ=", "narHash": "sha256-MWyfCH9K3eVTXJUxBi67OQSAh9jJAnvWklM6qm4j8w8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "660ac43ff9ab1f12e28bfb31d4719795777fe152",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1656945861,
"narHash": "sha256-L41+pANwxKvhqqGRCKXzugOb81ewFJG95esLobRI1KY=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "4d26010c93091d4cb7fca36b8d3ecb33c89a6f23", "rev": "2f0c3be57c348f4cfd8820f2d189e29a685d9c41",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nixos", "owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"root": { "root": {
"inputs": { "inputs": {
"fenix": "fenix",
"naersk": "naersk", "naersk": "naersk",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs",
"utils": "utils" "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": { "utils": {
"locked": { "locked": {
"lastModified": 1656928814, "lastModified": 1656928814,

@ -1,46 +1,89 @@
{ {
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs"; nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
utils.url = "github:numtide/flake-utils"; 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 }: outputs = {
utils.lib.eachDefaultSystem (system: self,
let nixpkgs,
pkgs = nixpkgs.legacyPackages."${system}"; utils,
naersk-lib = naersk.lib."${system}"; naersk,
in rec fenix,
{ }:
packages.amethyst = naersk-lib.buildPackage { utils.lib.eachDefaultSystem (system: let
pname = "ame"; pkgs = nixpkgs.legacyPackages."${system}";
root = ./.; toolchain = with fenix.packages."${system}";
nativeBuildInputs = with pkgs; [ combine [
openssl minimal.rustc
sqlite minimal.cargo
pkg-config targets.x86_64-pc-windows-gnu.latest.rust-std
]; targets.x86_64-unknown-linux-gnu.latest.rust-std
}; ];
naersk-lib = naersk.lib."${system}".override {
packages.default = packages.amethyst; cargo = toolchain;
rustc = toolchain;
apps.amethyst = utils.lib.mkApp { };
drv = packages.amethyst; in rec
}; {
packages.amethyst = naersk-lib.buildPackage {
apps.default = apps.amethyst; pname = "Amethyst";
root = ./.;
devShells.default = pkgs.mkShell { };
nativeBuildInputs = with pkgs; [
rustc packages.amethyst-win = naersk-lib.buildPackage {
cargo pname = "Amethyst";
rustfmt root = ./.;
cargo-audit strictDeps = true;
clippy depsBuildBuild = with pkgs; [
openssl pkgsCross.mingwW64.stdenv.cc
sqlite pkgsCross.mingwW64.windows.pthreads
pkg-config ];
]; 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;
});
} }

@ -57,6 +57,12 @@ pub struct InstallArgs {
/// The name of the package(s) to install /// The name of the package(s) to install
#[clap(required = true)] #[clap(required = true)]
pub packages: Vec<String>, pub packages: Vec<String>,
/// Installs only from the AUR
pub aur: bool,
/// Install the packages from the pacman-defined repositories
pub repo: bool,
} }
#[derive(Default, Debug, Clone, Parser)] #[derive(Default, Debug, Clone, Parser)]

@ -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)
);
}

@ -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,
)
});
}

@ -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")
}

@ -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<Package> {
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::<usize, String>(3)
.unwrap()
.split(' ')
.map(|s| s.to_string())
.collect::<Vec<String>>(),
make_depends: row
.get::<usize, String>(4)
.unwrap()
.split(' ')
.map(|s| s.to_string())
.collect::<Vec<String>>(),
})
})
.expect("Couldn't query database for packages");
if verbosity >= 1 {
log!("Retrieved results");
}
let mut results: Vec<Package> = vec![];
for package in packages_iter {
results.push(package.unwrap());
}
if verbosity >= 1 {
log!("Collected results");
}
results
}

@ -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");
}

@ -3,10 +3,12 @@ use regex::Regex;
use crate::{log, Options}; use crate::{log, Options};
pub fn clean(a: &[String], options: Options) -> Vec<String> { pub fn clean(a: &[String], options: Options) -> Vec<String> {
// Strip versioning from package names
let r = Regex::new(r"(\S+)((?:>=|<=|>|<|=\W)\S+$)").unwrap(); let r = Regex::new(r"(\S+)((?:>=|<=|>|<|=\W)\S+$)").unwrap();
let mut cleaned: Vec<String> = vec![]; let mut cleaned: Vec<String> = vec![];
let verbosity = options.verbosity; let verbosity = options.verbosity;
// Push cleaned package names to vector
for b in a { for b in a {
if r.captures_iter(b).count() > 0 { if r.captures_iter(b).count() > 0 {
let c = r.captures(b).unwrap().get(1).map_or("", |m| m.as_str()); let c = r.captures(b).unwrap().get(1).map_or("", |m| m.as_str());

@ -1,20 +1,27 @@
use crate::internal::commands::ShellCommand; use crate::internal::commands::ShellCommand;
use crate::internal::error::SilentUnwrap; use crate::internal::error::SilentUnwrap;
use crate::internal::exit_code::AppExitCode; use crate::internal::exit_code::AppExitCode;
use crate::{prompt, warn}; use crate::{info, prompt, warn};
pub fn detect() { pub fn detect() {
info!("Scanning for pacnew files");
let mut pacnew = vec![]; let mut pacnew = vec![];
for entry in std::fs::read_dir("/etc").unwrap() { // Run `find` to find pacnew files and split by lines into a vec
let entry = entry.unwrap(); let find = std::process::Command::new("sudo")
let path = entry.path(); .arg("pacdiff")
if path.to_str().unwrap().contains(".pacnew") || path.to_str().unwrap().contains(".pacsave") .arg("-f")
{ .output()
pacnew.push(path); .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() { 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`"); 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 { if choice {

@ -1,9 +1,9 @@
use crate::internal::exit_code::AppExitCode;
use std::error::Error; use std::error::Error;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
use std::io; use std::io;
use crate::crash; use crate::crash;
use crate::internal::exit_code::AppExitCode;
pub type AppResult<T> = Result<T, AppError>; pub type AppResult<T> = Result<T, AppError>;

@ -1,12 +1,10 @@
pub enum AppExitCode { pub enum AppExitCode {
RunAsRoot = 1, RunAsRoot = 1,
FailedAddingPkg = 2, FailedCreatingPaths = 2,
FailedInitDb = 3, MissingDeps = 3,
FailedCreatingPaths = 4, UserCancellation = 4,
MissingDeps = 5, PacmanError = 5,
UserCancellation = 6, GitError = 6,
PacmanError = 7, MakePkgError = 7,
GitError = 8, Other = 63,
MakePkgError = 9,
Other = 102,
} }

@ -1,129 +1,62 @@
use std::env; use std::env;
use std::path::Path; use std::path::Path;
use std::process::Command;
use crate::{crash, internal::exit_code::AppExitCode, log, Options}; use crate::{crash, internal::exit_code::AppExitCode, log, Options};
pub fn init(options: Options) { pub fn init(options: Options) {
// Initialise variables
let verbosity = options.verbosity; let verbosity = options.verbosity;
let homedir = env::var("HOME").unwrap(); let homedir = env::var("HOME").unwrap();
// Initialise stateful directory
if !Path::new(&format!("{}/.local/share/ame", homedir)).exists() { if !Path::new(&format!("{}/.local/share/ame", homedir)).exists() {
let r = std::fs::create_dir_all(format!("{}/.local/share/ame", homedir)); if verbosity >= 1 {
match r { log!("Initialising stateful directory");
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,
);
}
} }
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() { // If cache path doesn't exist, create it, if it does, delete it and recreate it
crate::database::init(options);
}
if !Path::new(&format!("{}/.cache/ame/", homedir)).exists() { if !Path::new(&format!("{}/.cache/ame/", homedir)).exists() {
let r = std::fs::create_dir_all(format!("{}/.cache/ame", homedir)); if verbosity >= 1 {
match r { log!("Initialising cache directory");
Ok(_) => {
if verbosity >= 1 {
log!("Created path: {}/.cache/ame", homedir);
}
}
Err(e) => {
crash!(
AppExitCode::FailedCreatingPaths,
"Couldn't create path: {}/.cache/ame: {}",
homedir,
e,
);
}
} }
} else { std::fs::create_dir_all(format!("{}/.cache/ame", homedir)).unwrap_or_else(|e| {
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) => {
crash!( crash!(
AppExitCode::FailedCreatingPaths, AppExitCode::FailedCreatingPaths,
"Couldn't set permissions for path: {}/.cache/ame: {}", "Couldn't create path: {}/.cache/ame: {}",
homedir, homedir,
e, e,
); );
});
} else {
if verbosity >= 1 {
log!("Deleting cache directory");
} }
}; rm_rf::remove(format!("{}/.cache/ame", homedir)).unwrap_or_else(|e| {
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) => {
crash!( crash!(
AppExitCode::FailedCreatingPaths, AppExitCode::FailedCreatingPaths,
"Couldn't set permissions for path: {}/.local/share/ame: {}", "Couldn't remove path: {}/.cache/ame: {}",
homedir, 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
)
});
}
} }

@ -1,8 +1,12 @@
pub use clean::*; pub use clean::*;
pub use clean::*;
pub use detect::*; pub use detect::*;
pub use initialise::*; pub use initialise::*;
pub use initialise::*;
pub use sort::*;
pub use sort::*; pub use sort::*;
use std::env; use std::env;
pub use sudoloop::*;
mod clean; mod clean;
pub mod commands; pub mod commands;
@ -17,11 +21,6 @@ pub mod structs;
pub(crate) mod utils; pub(crate) mod utils;
mod sudoloop; mod sudoloop;
pub use clean::*;
pub use initialise::*;
pub use sort::*;
pub use sudoloop::*;
#[macro_export] #[macro_export]
macro_rules! uwu { macro_rules! uwu {
($x:expr) => {{ ($x:expr) => {{

@ -31,10 +31,15 @@ pub struct InfoResults {
pub const URL: &str = "https://aur.archlinux.org/"; pub const URL: &str = "https://aur.archlinux.org/";
pub fn rpcinfo(pkg: String) -> InfoResults { pub fn rpcinfo(pkg: String) -> InfoResults {
// Initialise TLS connector
let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap()); let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap());
// Build request agent
let agent = ureq::AgentBuilder::new() let agent = ureq::AgentBuilder::new()
.tls_connector(tls_connector) .tls_connector(tls_connector)
.build(); .build();
// Send request and parse results into json
let res: SearchResults = agent let res: SearchResults = agent
.get(&format!( .get(&format!(
"https://aur.archlinux.org/rpc/?v=5&type=info&arg={}", "https://aur.archlinux.org/rpc/?v=5&type=info&arg={}",
@ -45,6 +50,7 @@ pub fn rpcinfo(pkg: String) -> InfoResults {
.into_json() .into_json()
.unwrap(); .unwrap();
// Check if package was found
if res.results.is_empty() { if res.results.is_empty() {
InfoResults { InfoResults {
found: false, found: false,
@ -59,10 +65,15 @@ pub fn rpcinfo(pkg: String) -> InfoResults {
} }
pub fn rpcsearch(pkg: String) -> SearchResults { pub fn rpcsearch(pkg: String) -> SearchResults {
// Initialise TLS connector
let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap()); let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap());
// Build request agent
let agent = ureq::AgentBuilder::new() let agent = ureq::AgentBuilder::new()
.tls_connector(tls_connector) .tls_connector(tls_connector)
.build(); .build();
// Send request and parse results into json
agent agent
.get(&format!( .get(&format!(
"https://aur.archlinux.org/rpc/?v=5&type=search&arg={}", "https://aur.archlinux.org/rpc/?v=5&type=search&arg={}",

@ -4,11 +4,13 @@ use crate::internal::{clean, rpc, structs};
use crate::{log, Options}; use crate::{log, Options};
pub fn sort(input: &[String], options: Options) -> structs::Sorted { pub fn sort(input: &[String], options: Options) -> structs::Sorted {
// Initialise variables
let mut repo: Vec<String> = vec![]; let mut repo: Vec<String> = vec![];
let mut aur: Vec<String> = vec![]; let mut aur: Vec<String> = vec![];
let mut nf: Vec<String> = vec![]; let mut nf: Vec<String> = vec![];
let verbosity = options.verbosity; let verbosity = options.verbosity;
// Sanitise all packages passed in
let a = clean(input, options); let a = clean(input, options);
if verbosity >= 1 { if verbosity >= 1 {
@ -16,6 +18,7 @@ pub fn sort(input: &[String], options: Options) -> structs::Sorted {
} }
for b in a { for b in a {
// Check if package is in the repos
let rs = Command::new("pacman") let rs = Command::new("pacman")
.arg("-Ss") .arg("-Ss")
.arg(format!("^{}$", &b)) .arg(format!("^{}$", &b))
@ -24,16 +27,19 @@ pub fn sort(input: &[String], options: Options) -> structs::Sorted {
.expect("Something has gone wrong"); .expect("Something has gone wrong");
if let Some(0) = rs.code() { if let Some(0) = rs.code() {
// If it is, add it to the repo vector
if verbosity >= 1 { if verbosity >= 1 {
log!("{} found in repos", b); log!("{} found in repos", b);
} }
repo.push(b.to_string()); repo.push(b.to_string());
} else if rpc::rpcinfo(b.to_string()).found { } 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 { if verbosity >= 1 {
log!("{} found in AUR", b); log!("{} found in AUR", b);
} }
aur.push(b.to_string()); aur.push(b.to_string());
} else { } else {
// Otherwise, add it to the not found vector
if verbosity >= 1 { if verbosity >= 1 {
log!("{} not found", b); log!("{} not found", b);
} }

@ -1,7 +1,8 @@
use crate::ShellCommand;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use crate::ShellCommand;
/// Loop sudo so it doesn't time out /// Loop sudo so it doesn't time out
pub fn start_sudoloop() { pub fn start_sudoloop() {
prompt_sudo(); prompt_sudo();

@ -1,3 +1,4 @@
use colored::*;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
use std::process::exit; use std::process::exit;
@ -5,7 +6,6 @@ use std::time::UNIX_EPOCH;
use crate::internal::exit_code::AppExitCode; use crate::internal::exit_code::AppExitCode;
use crate::{internal, uwu}; use crate::{internal, uwu};
use colored::*;
const OK_SYMBOL: &str = "❖"; const OK_SYMBOL: &str = "❖";
const ERR_SYMBOL: &str = "❌"; const ERR_SYMBOL: &str = "❌";

@ -1,10 +1,12 @@
// #![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
// #![allow(clippy::too_many_lines)]
use args::Args; use args::Args;
use clap::Parser; use clap::Parser;
use internal::commands::ShellCommand; use internal::commands::ShellCommand;
use internal::error::SilentUnwrap; use internal::error::SilentUnwrap;
use crate::args::{InstallArgs, Operation, QueryArgs, RemoveArgs, SearchArgs}; use crate::args::{InstallArgs, Operation, QueryArgs, RemoveArgs, SearchArgs};
use crate::internal::detect;
use crate::internal::exit_code::AppExitCode; use crate::internal::exit_code::AppExitCode;
use crate::internal::{init, sort, start_sudoloop, structs::Options}; 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; static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
mod args; mod args;
mod database;
mod internal; mod internal;
mod operations; mod operations;
fn main() { fn main() {
// Break if we are running as root
if unsafe { libc::geteuid() } == 0 { 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"); 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(); let args: Args = Args::parse();
// Initialize variables
let verbosity = args.verbose as i32; let verbosity = args.verbose as i32;
let noconfirm = args.no_confirm; let noconfirm = args.no_confirm;
// Get options struct
let options = Options { let options = Options {
verbosity, verbosity,
noconfirm, noconfirm,
asdeps: false, asdeps: false,
}; };
// Ensure amethyst is initialized
init(options); init(options);
// Start sudoloop if specified
if args.sudoloop { if args.sudoloop {
start_sudoloop(); start_sudoloop();
} }
// Match args
match args.subcommand.unwrap_or_default() { match args.subcommand.unwrap_or_default() {
Operation::Install(install_args) => cmd_install(install_args, options), Operation::Install(install_args) => cmd_install(install_args, options),
Operation::Remove(remove_args) => cmd_remove(remove_args, options), Operation::Remove(remove_args) => cmd_remove(remove_args, options),
@ -52,23 +60,17 @@ fn main() {
operations::clean(options); operations::clean(options);
} }
} }
detect();
} }
fn cmd_install(args: InstallArgs, options: Options) { fn cmd_install(args: InstallArgs, options: Options) {
// Initialise variables
let packages = args.packages; let packages = args.packages;
let sorted = sort(&packages, options); let sorted = sort(&packages, options);
info!("Attempting to install packages: {}", packages.join(", ")); 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 !sorted.nf.is_empty() {
// If some packages are not found, crash
crash!( crash!(
AppExitCode::PacmanError, AppExitCode::PacmanError,
"Couldn't find packages: {} in repos or the AUR", "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() if !sorted.repo.is_empty() {
.arg("-c") // If repo packages found, install them
.arg("sudo find /etc -name *.pacnew") operations::install(sorted.repo, options);
.wait_with_output() }
.silent_unwrap(AppExitCode::Other) if !sorted.aur.is_empty() {
.stdout; // If AUR packages found, install them
operations::aur_install(sorted.aur, options);
if !bash_output.is_empty() {
let pacnew_files = bash_output
.split_whitespace()
.collect::<Vec<&str>>()
.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" );
} }
} }
fn cmd_remove(args: RemoveArgs, options: Options) { fn cmd_remove(args: RemoveArgs, options: Options) {
// Initialise variables
let packages = args.packages; let packages = args.packages;
info!("Uninstalling packages: {}", &packages.join(", ")); info!("Uninstalling packages: {}", &packages.join(", "));
// Remove packages
operations::uninstall(packages, options); operations::uninstall(packages, options);
} }
fn cmd_search(args: SearchArgs, options: Options) { fn cmd_search(args: SearchArgs, options: Options) {
// Initialise variables
let query_string = args.search.join(" "); let query_string = args.search.join(" ");
if args.aur { if args.aur {
info!("Searching AUR for {}", &query_string); info!("Searching AUR for {}", &query_string);
// Search AUR
operations::aur_search(&query_string, options); operations::aur_search(&query_string, options);
} }
if args.repo { if args.repo {
info!("Searching repos for {}", &query_string); info!("Searching repos for {}", &query_string);
// Search repos
operations::search(&query_string, options); operations::search(&query_string, options);
} }
if !args.aur && !args.repo { if !args.aur && !args.repo {
info!("Searching AUR and repos for {}", &query_string); info!("Searching AUR and repos for {}", &query_string);
// If no search type specified, search both
operations::search(&query_string, options); operations::search(&query_string, options);
operations::aur_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) { fn cmd_query(args: QueryArgs) {
if args.aur { if args.aur {
// If AUR query, query AUR
ShellCommand::pacman() ShellCommand::pacman()
.arg("-Qm") .arg("-Qm")
.wait_success() .wait_success()
.silent_unwrap(AppExitCode::PacmanError); .silent_unwrap(AppExitCode::PacmanError);
} }
if args.repo { if args.repo {
// If repo query, query repos
ShellCommand::pacman() ShellCommand::pacman()
.arg("-Qn") .arg("-Qn")
.wait_success() .wait_success()
.silent_unwrap(AppExitCode::PacmanError); .silent_unwrap(AppExitCode::PacmanError);
} }
if !args.repo && !args.aur { if !args.repo && !args.aur {
// If no query type specified, query both
ShellCommand::pacman() ShellCommand::pacman()
.arg("-Qn") .arg("-Qn")
.wait_success() .wait_success()

@ -11,6 +11,7 @@ use crate::internal::rpc::rpcinfo;
use crate::{crash, info, log, prompt, Options}; use crate::{crash, info, log, prompt, Options};
pub fn aur_install(a: Vec<String>, options: Options) { pub fn aur_install(a: Vec<String>, options: Options) {
// Initialise variables
let url = crate::internal::rpc::URL; let url = crate::internal::rpc::URL;
let cachedir = format!("{}/.cache/ame/", env::var("HOME").unwrap()); let cachedir = format!("{}/.cache/ame/", env::var("HOME").unwrap());
let verbosity = options.verbosity; let verbosity = options.verbosity;
@ -23,12 +24,15 @@ pub fn aur_install(a: Vec<String>, options: Options) {
info!("Installing packages {} from the AUR", a.join(", ")); info!("Installing packages {} from the AUR", a.join(", "));
for package in a { for package in a {
// Query AUR for package info
let rpcres = rpcinfo(package); let rpcres = rpcinfo(package);
if !rpcres.found { if !rpcres.found {
// If package isn't found, break
break; break;
} }
// Get package name
let pkg = &rpcres.package.as_ref().unwrap().name; let pkg = &rpcres.package.as_ref().unwrap().name;
if verbosity >= 1 { if verbosity >= 1 {
@ -37,6 +41,7 @@ pub fn aur_install(a: Vec<String>, options: Options) {
info!("Cloning package source"); info!("Cloning package source");
// Clone package into cachedir
set_current_dir(Path::new(&cachedir)).unwrap(); set_current_dir(Path::new(&cachedir)).unwrap();
ShellCommand::git() ShellCommand::git()
.arg("clone") .arg("clone")
@ -61,7 +66,7 @@ pub fn aur_install(a: Vec<String>, options: Options) {
); );
} }
// dep sorting // Sort dependencies and makedepends
log!("Sorting dependencies"); log!("Sorting dependencies");
let sorted = crate::internal::sort(&rpcres.package.as_ref().unwrap().depends, options); let sorted = crate::internal::sort(&rpcres.package.as_ref().unwrap().depends, options);
log!("Sorting make dependencies"); log!("Sorting make dependencies");
@ -73,12 +78,14 @@ pub fn aur_install(a: Vec<String>, options: Options) {
log!("Sorted makedepends for {} are:\n{:?}", pkg, &md_sorted); log!("Sorted makedepends for {} are:\n{:?}", pkg, &md_sorted);
} }
// Create newopts struct for installing dependencies
let newopts = Options { let newopts = Options {
verbosity, verbosity,
noconfirm, noconfirm,
asdeps: true, asdeps: true,
}; };
// If dependencies are not found in AUR or repos, crash
if !sorted.nf.is_empty() || !md_sorted.nf.is_empty() { if !sorted.nf.is_empty() || !md_sorted.nf.is_empty() {
crash!( crash!(
AppExitCode::MissingDeps, AppExitCode::MissingDeps,
@ -89,6 +96,7 @@ pub fn aur_install(a: Vec<String>, options: Options) {
} }
if !noconfirm { if !noconfirm {
// Prompt user to view PKGBUILD
let p1 = prompt!(default false, let p1 = prompt!(default false,
"Would you like to review {}'s PKGBUILD (and any .install files if present)?", "Would you like to review {}'s PKGBUILD (and any .install files if present)?",
pkg pkg
@ -96,6 +104,7 @@ pub fn aur_install(a: Vec<String>, options: Options) {
let editor: &str = &env::var("PAGER").unwrap_or_else(|_| "less".parse().unwrap()); let editor: &str = &env::var("PAGER").unwrap_or_else(|_| "less".parse().unwrap());
if p1 { if p1 {
// Open PKGBUILD in pager
Command::new(editor) Command::new(editor)
.arg(format!("{}/PKGBUILD", pkg)) .arg(format!("{}/PKGBUILD", pkg))
.spawn() .spawn()
@ -103,6 +112,7 @@ pub fn aur_install(a: Vec<String>, options: Options) {
.wait() .wait()
.unwrap(); .unwrap();
// Check if any .install files are present
let status = ShellCommand::bash() let status = ShellCommand::bash()
.arg("-c") .arg("-c")
.arg(format!("ls {}/*.install &> /dev/null", pkg)) .arg(format!("ls {}/*.install &> /dev/null", pkg))
@ -110,6 +120,7 @@ pub fn aur_install(a: Vec<String>, options: Options) {
.silent_unwrap(AppExitCode::Other); .silent_unwrap(AppExitCode::Other);
if status.success() { if status.success() {
// If so, open them too
ShellCommand::bash() ShellCommand::bash()
.arg("-c") .arg("-c")
.arg(format!("{} {}/*.install", editor, pkg)) .arg(format!("{} {}/*.install", editor, pkg))
@ -117,17 +128,19 @@ pub fn aur_install(a: Vec<String>, options: Options) {
.silent_unwrap(AppExitCode::Other); .silent_unwrap(AppExitCode::Other);
} }
// Prompt user to continue
let p2 = prompt!(default true, "Would you still like to install {}?", pkg); let p2 = prompt!(default true, "Would you still like to install {}?", pkg);
if !p2 { if !p2 {
// If not, crash
fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap(); fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap();
crash!(AppExitCode::UserCancellation, "Not proceeding"); crash!(AppExitCode::UserCancellation, "Not proceeding");
} }
} }
} }
// dep installing
info!("Moving on to install dependencies"); info!("Moving on to install dependencies");
// Install dependencies and makedepends
if !sorted.repo.is_empty() { if !sorted.repo.is_empty() {
crate::operations::install(sorted.repo, newopts); crate::operations::install(sorted.repo, newopts);
crate::operations::install(md_sorted.repo, newopts); crate::operations::install(md_sorted.repo, newopts);
@ -137,6 +150,7 @@ pub fn aur_install(a: Vec<String>, options: Options) {
crate::operations::aur_install(md_sorted.aur, newopts); crate::operations::aur_install(md_sorted.aur, newopts);
} }
// Build makepkg args
let mut makepkg_args = vec!["-rsci", "--skippgp"]; let mut makepkg_args = vec!["-rsci", "--skippgp"];
if options.asdeps { if options.asdeps {
makepkg_args.push("--asdeps") makepkg_args.push("--asdeps")
@ -145,8 +159,9 @@ pub fn aur_install(a: Vec<String>, options: Options) {
makepkg_args.push("--noconfirm") makepkg_args.push("--noconfirm")
} }
// package building and installing
info!("Building time!"); info!("Building time!");
// Enter cachedir and build package
set_current_dir(format!("{}/{}", cachedir, pkg)).unwrap(); set_current_dir(format!("{}/{}", cachedir, pkg)).unwrap();
let status = ShellCommand::makepkg() let status = ShellCommand::makepkg()
.args(makepkg_args) .args(makepkg_args)
@ -154,6 +169,7 @@ pub fn aur_install(a: Vec<String>, options: Options) {
.silent_unwrap(AppExitCode::MakePkgError); .silent_unwrap(AppExitCode::MakePkgError);
if !status.success() { if !status.success() {
// If build failed, crash
fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap(); fs::remove_dir_all(format!("{}/{}", cachedir, pkg)).unwrap();
crash!( crash!(
AppExitCode::PacmanError, AppExitCode::PacmanError,
@ -162,10 +178,10 @@ pub fn aur_install(a: Vec<String>, options: Options) {
); );
} }
// Return to cachedir
set_current_dir(&cachedir).unwrap(); set_current_dir(&cachedir).unwrap();
remove_dir_all(format!("{}/{}", cachedir, &pkg)).unwrap();
// pushes package to database // Remove package from cache
crate::database::add(rpcres.package.unwrap(), options); remove_dir_all(format!("{}/{}", cachedir, &pkg)).unwrap();
} }
} }

@ -13,29 +13,35 @@ pub fn clean(options: Options) {
let verbosity = options.verbosity; let verbosity = options.verbosity;
let noconfirm = options.noconfirm; let noconfirm = options.noconfirm;
// Check for orphaned packages
let orphaned_packages = ShellCommand::pacman() let orphaned_packages = ShellCommand::pacman()
.arg("-Qdtq") .arg("-Qdtq")
.wait_with_output() .wait_with_output()
.silent_unwrap(AppExitCode::PacmanError); .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"); info!("No orphaned packages found");
} else { } else {
// Prompt users whether to remove orphaned packages
info!( info!(
"Removing orphans would uninstall the following packages: \n{}", "Removing orphans would uninstall the following packages: \n{}",
&orphaned_packages.stdout &orphaned_packages.stdout
); );
let cont = prompt!(default false, "Continue?"); let cont = prompt!(default false, "Continue?");
if !cont { if !cont {
// If user doesn't want to continue, break
info!("Exiting"); info!("Exiting");
std::process::exit(AppExitCode::PacmanError as i32); std::process::exit(AppExitCode::PacmanError as i32);
} }
// Build pacman args
let mut pacman_args = vec!["-Rns"]; let mut pacman_args = vec!["-Rns"];
if noconfirm { if noconfirm {
pacman_args.push("--noconfirm"); pacman_args.push("--noconfirm");
} }
// Collect orphaned packages into a vector
let orphaned_packages_vec = orphaned_packages.stdout.split('\n').collect::<Vec<&str>>(); let orphaned_packages_vec = orphaned_packages.stdout.split('\n').collect::<Vec<&str>>();
for package in &orphaned_packages_vec { for package in &orphaned_packages_vec {
if !package.is_empty() { if !package.is_empty() {
@ -47,6 +53,7 @@ pub fn clean(options: Options) {
log!("Removing orphans: {:?}", orphaned_packages_vec); log!("Removing orphans: {:?}", orphaned_packages_vec);
} }
// Remove orphaned packages
let pacman_result = ShellCommand::pacman() let pacman_result = ShellCommand::pacman()
.elevated() .elevated()
.args(pacman_args) .args(pacman_args)
@ -54,23 +61,29 @@ pub fn clean(options: Options) {
.silent_unwrap(AppExitCode::PacmanError); .silent_unwrap(AppExitCode::PacmanError);
if pacman_result.success() { if pacman_result.success() {
// If pacman succeeded, notify user
info!("Successfully removed orphans"); info!("Successfully removed orphans");
} else { } else {
// If pacman failed, crash
crash!(AppExitCode::PacmanError, "Failed to remove orphans",); crash!(AppExitCode::PacmanError, "Failed to remove orphans",);
} }
} }
// Prompt the user whether to clear cache or not
let clear_cache = if !noconfirm { let clear_cache = if !noconfirm {
prompt!(default false, "Also clear pacman's package cache?") prompt!(default false, "Also clear pacman's package cache?")
} else { } else {
true true
}; };
if clear_cache { if clear_cache {
// Build pacman args
let mut pacman_args = vec!["-Sc"]; let mut pacman_args = vec!["-Sc"];
if noconfirm { if noconfirm {
pacman_args.push("--noconfirm"); pacman_args.push("--noconfirm");
} }
// Build paccache args
let mut paccache_args = vec!["-r"]; let mut paccache_args = vec!["-r"];
if noconfirm { if noconfirm {
paccache_args.push("--noconfirm"); paccache_args.push("--noconfirm");
@ -80,6 +93,7 @@ pub fn clean(options: Options) {
log!("Clearing using `paccache -r`"); log!("Clearing using `paccache -r`");
} }
// Clear pacman's cache (keeping latest 3 versions of installed packages)
Command::new("sudo") Command::new("sudo")
.arg("paccache") .arg("paccache")
.args(paccache_args) .args(paccache_args)
@ -98,6 +112,7 @@ pub fn clean(options: Options) {
log!("Clearing using `pacman -Sc`"); log!("Clearing using `pacman -Sc`");
} }
// Clear pacman's cache (keeping only installed packages)
let pacman_result = ShellCommand::pacman() let pacman_result = ShellCommand::pacman()
.elevated() .elevated()
.args(pacman_args) .args(pacman_args)
@ -105,8 +120,10 @@ pub fn clean(options: Options) {
.silent_unwrap(AppExitCode::PacmanError); .silent_unwrap(AppExitCode::PacmanError);
if pacman_result.success() { if pacman_result.success() {
// If pacman succeeded, notify user
info!("Successfully cleared package cache"); info!("Successfully cleared package cache");
} else { } else {
// If pacman failed, crash
crash!(AppExitCode::PacmanError, "Failed to clear package cache",); crash!(AppExitCode::PacmanError, "Failed to clear package cache",);
} }
} }

@ -5,6 +5,8 @@ use crate::{crash, info, log, Options};
pub fn install(packages: Vec<String>, options: Options) { pub fn install(packages: Vec<String>, options: Options) {
info!("Installing packages {} from repos", &packages.join(", ")); info!("Installing packages {} from repos", &packages.join(", "));
// Build pacman args
let mut opers = vec!["-S", "--needed"]; let mut opers = vec!["-S", "--needed"];
if options.noconfirm { if options.noconfirm {
opers.push("--noconfirm"); opers.push("--noconfirm");
@ -19,6 +21,7 @@ pub fn install(packages: Vec<String>, options: Options) {
log!("Installing from repos: {:?}", &packages); log!("Installing from repos: {:?}", &packages);
} }
// Install packages
let status = ShellCommand::pacman() let status = ShellCommand::pacman()
.elevated() .elevated()
.args(opers) .args(opers)
@ -26,6 +29,7 @@ pub fn install(packages: Vec<String>, options: Options) {
.wait() .wait()
.silent_unwrap(AppExitCode::PacmanError); .silent_unwrap(AppExitCode::PacmanError);
if !status.success() { if !status.success() {
// If pacman failed, crash
crash!( crash!(
AppExitCode::PacmanError, AppExitCode::PacmanError,
"An error occured while installing packages: {}, aborting", "An error occured while installing packages: {}, aborting",

@ -5,9 +5,13 @@ use crate::internal::rpc::rpcsearch;
use crate::{log, Options}; use crate::{log, Options};
pub fn aur_search(query: &str, options: Options) { pub fn aur_search(query: &str, options: Options) {
// Initialise variables
let verbosity = options.verbosity; let verbosity = options.verbosity;
// Query AUR for package info
let res = rpcsearch(query.to_string()); let res = rpcsearch(query.to_string());
// Format output
for package in &res.results { for package in &res.results {
println!( println!(
"aur/{} {}\n {}", "aur/{} {}\n {}",
@ -26,7 +30,10 @@ pub fn aur_search(query: &str, options: Options) {
} }
pub fn repo_search(query: &str, options: Options) { pub fn repo_search(query: &str, options: Options) {
// Initialise variables
let verbosity = options.verbosity; let verbosity = options.verbosity;
// Query pacman for package info
let output = ShellCommand::pacman() let output = ShellCommand::pacman()
.arg("-Ss") .arg("-Ss")
.arg(query) .arg(query)

@ -7,9 +7,9 @@ use crate::internal::exit_code::AppExitCode;
use crate::{log, Options}; use crate::{log, Options};
pub fn uninstall(packages: Vec<String>, options: Options) { pub fn uninstall(packages: Vec<String>, options: Options) {
// Build pacman args
let mut pacman_args = vec!["-Rs"]; let mut pacman_args = vec!["-Rs"];
pacman_args.append(&mut packages.iter().map(|s| s.as_str()).collect()); pacman_args.append(&mut packages.iter().map(|s| s.as_str()).collect());
if options.noconfirm { if options.noconfirm {
pacman_args.push("--noconfirm"); pacman_args.push("--noconfirm");
} }
@ -18,6 +18,7 @@ pub fn uninstall(packages: Vec<String>, options: Options) {
log!("Uninstalling: {:?}", &packages); log!("Uninstalling: {:?}", &packages);
} }
// Uninstall packages
ShellCommand::pacman() ShellCommand::pacman()
.elevated() .elevated()
.args(pacman_args) .args(pacman_args)
@ -29,7 +30,7 @@ pub fn uninstall(packages: Vec<String>, options: Options) {
} }
for package in packages { for package in packages {
crate::database::remove(&package, options); // Remove old cache directory
if Path::new(&format!( if Path::new(&format!(
"{}/.cache/ame/{}", "{}/.cache/ame/{}",
env::var("HOME").unwrap(), env::var("HOME").unwrap(),

@ -1,14 +1,23 @@
use crate::internal::commands::ShellCommand; use crate::internal::commands::ShellCommand;
use crate::internal::detect;
use crate::internal::error::SilentUnwrap; use crate::internal::error::SilentUnwrap;
use crate::internal::exit_code::AppExitCode; use crate::internal::exit_code::AppExitCode;
use crate::internal::rpc::rpcinfo; use crate::internal::rpc::rpcinfo;
use crate::operations::aur_install::aur_install; use crate::operations::aur_install::aur_install;
use crate::{info, log, prompt, Options}; use crate::{info, log, prompt, Options};
#[derive(Debug)]
struct QueriedPackage {
pub name: String,
pub version: String,
}
pub fn upgrade(options: Options) { pub fn upgrade(options: Options) {
// Initialise variables
let verbosity = options.verbosity; let verbosity = options.verbosity;
let noconfirm = options.noconfirm; let noconfirm = options.noconfirm;
// Build pacman args
let mut pacman_args = vec!["-Syu"]; let mut pacman_args = vec!["-Syu"];
if noconfirm { if noconfirm {
pacman_args.push("--noconfirm"); pacman_args.push("--noconfirm");
@ -18,6 +27,7 @@ pub fn upgrade(options: Options) {
log!("Upgrading repo packages"); log!("Upgrading repo packages");
} }
// Upgrade repo packages
let pacman_result = ShellCommand::pacman() let pacman_result = ShellCommand::pacman()
.elevated() .elevated()
.args(pacman_args) .args(pacman_args)
@ -25,39 +35,85 @@ pub fn upgrade(options: Options) {
.silent_unwrap(AppExitCode::PacmanError); .silent_unwrap(AppExitCode::PacmanError);
if pacman_result.success() { if pacman_result.success() {
// If pacman was successful, notify user
info!("Successfully upgraded repo packages"); info!("Successfully upgraded repo packages");
} else { } else {
// Otherwise, prompt user whether to continue
let cont = prompt!(default false, let cont = prompt!(default false,
"Failed to upgrade repo packages, continue to upgrading AUR packages?", "Failed to upgrade repo packages, continue to upgrading AUR packages?",
); );
if !cont { if !cont {
// If user doesn't want to continue, break
info!("Exiting"); info!("Exiting");
std::process::exit(AppExitCode::PacmanError as i32); std::process::exit(AppExitCode::PacmanError as i32);
} }
} }
if verbosity >= 1 { 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<String>
let non_native = ShellCommand::pacman()
.arg("-Qm")
.args(&["--color", "never"])
.wait_with_output()
.silent_unwrap(AppExitCode::PacmanError);
// Collect by lines to a Vec<String>
let mut non_native = non_native.stdout.split('\n').collect::<Vec<&str>>();
// Remove last element, which is an empty line
non_native.pop();
// Parse non-native packages into a Vec<QueriedPackage>
let mut parsed_non_native: Vec<QueriedPackage> = vec![];
for pkg in non_native {
// Split by space
let split = pkg.split(' ').collect::<Vec<&str>>();
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 { if verbosity >= 1 {
log!("{:?}", &res); log!("{:?}", &parsed_non_native);
} }
// Check if AUR package versions are the same as installed
let mut aur_upgrades = vec![]; let mut aur_upgrades = vec![];
for r in res { for pkg in parsed_non_native {
let re = r.clone(); // Query AUR
let ver = rpcinfo(r.name); let rpc_result = rpcinfo((&*pkg.name).to_string());
if ver.package.unwrap().version != r.version {
aur_upgrades.push(re.name); 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() { 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 { } else {
info!("No upgrades available for installed AUR packages"); info!("No upgrades available for installed AUR packages");
} }
// Check for .pacnew files
detect();
} }

Loading…
Cancel
Save