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/
ame
ame.exe
.vscode
.idea
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",
"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"

@ -1,6 +1,6 @@
[package]
name = "Amethyst"
version = "3.3.0"
version = "3.4.0"
authors = ["michal <michal@tar.black>", "axtlos <axtlos@tar.black>"]
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 }

@ -3,8 +3,8 @@
# Developer: Michal S <michal[at]tar[dot]black>
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"

@ -1,6 +1,6 @@
<p align="center">
<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>
</p>
@ -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

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

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

@ -57,6 +57,12 @@ pub struct InstallArgs {
/// The name of the package(s) to install
#[clap(required = true)]
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)]

@ -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};
pub fn clean(a: &[String], options: Options) -> Vec<String> {
// Strip versioning from package names
let r = Regex::new(r"(\S+)((?:>=|<=|>|<|=\W)\S+$)").unwrap();
let mut cleaned: Vec<String> = 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());

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

@ -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<T> = Result<T, AppError>;

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

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

@ -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) => {{

@ -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={}",

@ -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<String> = vec![];
let mut aur: Vec<String> = vec![];
let mut nf: Vec<String> = 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);
}

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

@ -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 = "❌";

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

@ -11,6 +11,7 @@ use crate::internal::rpc::rpcinfo;
use crate::{crash, info, log, prompt, Options};
pub fn aur_install(a: Vec<String>, 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<String>, 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<String>, 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<String>, 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<String>, 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<String>, 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<String>, 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<String>, 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<String>, 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<String>, 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<String>, 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<String>, 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<String>, 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<String>, 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();
}
}

@ -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::<Vec<&str>>();
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",);
}
}

@ -5,6 +5,8 @@ use crate::{crash, info, log, Options};
pub fn install(packages: Vec<String>, 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<String>, 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<String>, options: Options) {
.wait()
.silent_unwrap(AppExitCode::PacmanError);
if !status.success() {
// If pacman failed, crash
crash!(
AppExitCode::PacmanError,
"An error occured while installing packages: {}, aborting",

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

@ -7,9 +7,9 @@ use crate::internal::exit_code::AppExitCode;
use crate::{log, Options};
pub fn uninstall(packages: Vec<String>, 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<String>, options: Options) {
log!("Uninstalling: {:?}", &packages);
}
// Uninstall packages
ShellCommand::pacman()
.elevated()
.args(pacman_args)
@ -29,7 +30,7 @@ pub fn uninstall(packages: Vec<String>, 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(),

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

Loading…
Cancel
Save