Merge pull request 'rewrite' (#16) from rewrite into main

Reviewed-on: https://git.getcryst.al/crystal/ame/pulls/16
i18n
michal 2 years ago
commit 96cc77950e

4
.gitignore vendored

@ -1,6 +1,6 @@
target/
Cargo.lock
ame
ame.exe
.vscode
aur_pkgs.db
test.sql
.idea

@ -0,0 +1,28 @@
# 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

@ -1,19 +1,60 @@
[package]
name = "Amethyst"
version = "3.0.0"
authors = ["michal <michal@tar.black>", "axtlos <axtlos@tar.black>"]
edition = "2021"
description = "A fast and efficient AUR helper."
license-file = "LICENSE.md"
[features]
pkg-warner = []
[[bin]]
name = "apt"
path = "src/bin/apt.rs"
required-features = ["pkg-warner"]
[[bin]]
name = "apt-get"
path = "src/bin/apt-get.rs"
required-features = ["pkg-warner"]
[[bin]]
name = "dnf"
path = "src/bin/dnf.rs"
required-features = ["pkg-warner"]
[[bin]]
name = "eopkg"
path = "src/bin/eopkg.rs"
required-features = ["pkg-warner"]
[[bin]]
name = "yum"
path = "src/bin/yum.rs"
required-features = ["pkg-warner"]
[[bin]]
name = "zypper"
path = "src/bin/zypper.rs"
required-features = ["pkg-warner"]
[[bin]]
name = "ame"
version = "0.0.0"
authors = [ "jnats <michal@tar.black>", "axtlos <axtlos@tar.black>" ]
edition = "2018"
description = "a fast and efficient aur helper."
path = "src/main.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
incremental = true
debug = false
lto = "fat"
codegen-units = 1
[dependencies]
mimalloc = { version = "0.1.27", default-features = false }
clap = { version = "2.34.0", default-features = false, features = ["suggestions"] }
regex = { version = "1.5.4", default-features = false, features = ["std", "unicode-perl"] }
runas = "0.2.1"
ansi_term = "0.12.1"
uwuizer = "0.2.1"
moins = "0.5.0"
regex = { version = "1.5.4", default-features = false }
toml = "0.5.8"
sqlite = "0.26.0"
reqwest = { version = "0.11.7", default-features = false, features = [ "blocking", "json", "default-tls" ] }
serde = { version = "1.0.90", default-features = false, features = [ "derive" ] }
rusqlite = { version = "0.26.3", default-features = false }
ureq = { version = "2.4.0", default-features = false, features = ["native-tls", "json"] }
serde = { version = "1.0.90", default-features = false, features = ["derive", "serde_derive"] }
native-tls = "0.2.8"

@ -0,0 +1,23 @@
The Nolicense Revision 2.1 - Monday 15th November 2021
Copyright (c) 2022 Crystal Linux Team
Everyone is permitted to freely copy and distribute this license document. Modified redistributions are subject to the
following license agreement.
The Nolicense terms and conditions for copying, distribution and modification of software and any created assets are as
follows:
- Any unmodified redistributions in either source code or binary form must retain this copyright notice in its entirety.
- Any and all redistributions or derivative works, whether modified or unmodified, must credit the original author(s) of
the source code, and provide an easily accessible way to find the original author's source code, wherever it may be
published.
- Derivative works and modified redistributions cannot be published under the same name as the original software, nor
can it be presented as an extension of or newer revision of said software, and unless explicitly permitted, the
original authors name(s) shall not be used to endorse said derivative works.
This software is provided as-is; Neither the copyright holders nor any contributors to the software are to be held
liable for any damages caused by any files attached. By modifying or redistributing the software in any way, you
automatically agree to the terms of the license agreement.

@ -1,11 +0,0 @@
debug:
cargo build
ln -sf target/debug/ame .
release:
cargo build --release
ln -sf target/release/ame .
clean:
rm -rf target/ Cargo.lock ame
install:
cargo build --release
sudo cp target/release/ame /usr/bin/ame

@ -3,49 +3,66 @@
<img src="https://git.getcryst.al/crystal/branding/raw/branch/main/logos/crystal-logo-minimal.png" alt="Logo" width="150" height="150">
</a>
</p>
<p align="center">
<h2 align="center"> Amethyst</h2>
</p>
<p align="center">
<a href="https://discord.gg/yp4xpZeAgW"><img alt="Discord" src="https://img.shields.io/discord/825473796227858482?color=blue&label=Discord&logo=Discord&logoColor=white"?link=https://discord.gg/yp4xpZeAgW&link=https://discord.gg/yp4xpZeAgW> </p></a>
<a href="https://discord.gg/yp4xpZeAgW"><img alt="Discord" src="https://img.shields.io/discord/825473796227858482?color=blue&label=Discord&logo=Discord&logoColor=white"?link=https://discord.gg/yp4xpZeAgW&link=https://discord.gg/yp4xpZeAgW> </a>
</p>
<p align="center">
Amethyst is a fast, efficient and lightweight AUR helper and Pacman wrapper.<br>
Made for Crystal, compatible with any Arch-based Linux distribution.
</p>
### Basic usage
<p align="center"> Amethyst is a fast, efficient and lightweight AUR helper and Pacman wrapper.
Made for Crystal, compatible with any Arch-based Linux distribution.</p>
| Action | FreeBSD pkg-style alias | Pacman-style flags |
|----------------------|-------------------------|--------------------|
| Install a package | ame ins/install | ame -S |
| Remove a package | ame rm/remove | ame -R/-Rs |
| Upgrade a package | ame upg/upgrade | ame -Syu |
| Search for a package | ame sea | ame -Ss |
![](screenshot.png)
### Exit codes overview
## Basic usage
| Action | FreeBSD pkg-style alias | Pacman-style flag(s) |
| ------ | ------ | ------ |
| Install a package | ame ins | ame -S |
| Remove a package | ame rm | ame -R |
| Remove a package with its dependencies | ame purge | ame -Rs |
| Update repository | ame upd | ame -Sy |
| Upgrade a package | ame upg | ame -Syu |
| Search for a package in general | ame sea | ame -Ss |
| Search for a package in the official arch repos | ame repsea | ame -Sr |
| Search for a package in aur | ame aursea | ame -Sa |
| Exit Code (i32) | Reason |
|-----------------|----------------------------------------------------------|
| 1 | Running ame as UID 0 / root |
| 2 | Failed adding package to database |
| 3 | Failed initialising database |
| 4 | Error creating cache and/or database paths |
| 5 | Could not find one or more required package dependencies |
| 6 | User cancelled package installation |
| 7 | Pacman error when installing package |
You can also use any pacman flag!
### How to build:
## How to build:
(Install cargo)
Tested on latest Cargo (1.60.0-nightly)
For release:
- `make clean release`
For general debug/test:
- `make debug`
<br>
#### Debug/development builds
- `cargo build`
#### Optimised/release builds
- `cargo build --release`
Clean all build directories:
- `make clean`
#### Pkg-warner included
- `cargo build (--release) --all --features=pkg-warner`
<br>
<br>
```sh
echo "AME_UWU=YES" >> ~/.zshrc # for zsh
echo "AME_UWU=YES" >> ~/.bashrc # for bash
set -Ux AME_UWU YES # for fish
```
self explanatory
<!--
echo "AME_UWU=true" >> ~/.zshrc
echo "AME_UWU=true" >> ~/.bashrc
set -Ux AME_UWU true
:)
-->

@ -0,0 +1,14 @@
use std::env;
fn main() {
let arg = &env::args().collect::<Vec<String>>()[0];
println!(
"Sorry for the bother, we don't use \x1b[2;22;35m{}\x1b[0m on Crystal, we use \x1b[2;22;35mame\x1b[0m! Please use that instead!",
arg.split('/')
.collect::<Vec<&str>>()
.last()
.unwrap()
);
std::process::exit(0);
}

@ -0,0 +1,14 @@
use std::env;
fn main() {
let arg = &env::args().collect::<Vec<String>>()[0];
println!(
"Sorry for the bother, we don't use \x1b[2;22;35m{}\x1b[0m on Crystal, we use \x1b[2;22;35mame\x1b[0m! Please use that instead!",
arg.split('/')
.collect::<Vec<&str>>()
.last()
.unwrap()
);
std::process::exit(0);
}

@ -0,0 +1,14 @@
use std::env;
fn main() {
let arg = &env::args().collect::<Vec<String>>()[0];
println!(
"Sorry for the bother, we don't use \x1b[2;22;35m{}\x1b[0m on Crystal, we use \x1b[2;22;35mame\x1b[0m! Please use that instead!",
arg.split('/')
.collect::<Vec<&str>>()
.last()
.unwrap()
);
std::process::exit(0);
}

@ -0,0 +1,14 @@
use std::env;
fn main() {
let arg = &env::args().collect::<Vec<String>>()[0];
println!(
"Sorry for the bother, we don't use \x1b[2;22;35m{}\x1b[0m on Crystal, we use \x1b[2;22;35mame\x1b[0m! Please use that instead!",
arg.split('/')
.collect::<Vec<&str>>()
.last()
.unwrap()
);
std::process::exit(0);
}

@ -0,0 +1,14 @@
use std::env;
fn main() {
let arg = &env::args().collect::<Vec<String>>()[0];
println!(
"Sorry for the bother, we don't use \x1b[2;22;35m{}\x1b[0m on Crystal, we use \x1b[2;22;35mame\x1b[0m! Please use that instead!",
arg.split('/')
.collect::<Vec<&str>>()
.last()
.unwrap()
);
std::process::exit(0);
}

@ -0,0 +1,14 @@
use std::env;
fn main() {
let arg = &env::args().collect::<Vec<String>>()[0];
println!(
"Sorry for the bother, we don't use \x1b[2;22;35m{}\x1b[0m on Crystal, we use \x1b[2;22;35mame\x1b[0m! Please use that instead!",
arg.split('/')
.collect::<Vec<&str>>()
.last()
.unwrap()
);
std::process::exit(0);
}

@ -0,0 +1,26 @@
use std::env;
use std::path::Path;
use rusqlite::Connection;
use crate::{crash, log, Options};
use crate::internal::rpc::Package;
pub fn add(pkg: Package, options: Options) {
let conn = Connection::open(Path::new(&format!(
"{}/.local/share/ame/db.sqlite",
env::var("HOME").unwrap()
)))
.expect("Couldn't connect to database");
if options.verbosity >= 1 {
log(format!("Adding package {} to database", pkg.name));
}
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.unwrap_or_else(|| "No description found.".parse().unwrap()), &pkg.depends.join(" "), &pkg.make_depends.join(" ")],
).unwrap_or_else(|e| {
crash(format!("Failed adding package {} to the database: {}", pkg.name, e), 2);
1
});
}

@ -0,0 +1,38 @@
use std::env;
use std::path::Path;
use rusqlite::Connection;
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(format!("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".to_string());
}
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(format!("Couldn't initialise database: {}", e), 3);
1
});
}

@ -0,0 +1,23 @@
use crate::internal::rpc::Package;
use crate::Options;
mod add;
mod initialise;
mod query;
mod remove;
pub fn add(a: Package, options: Options) {
add::add(a, options);
}
pub fn remove(a: &str, options: Options) {
remove::remove(a, options);
}
pub fn init(options: Options) {
initialise::init(options);
}
pub fn query(options: Options) -> Vec<Package> {
query::query(options)
}

@ -0,0 +1,64 @@
use std::env;
use std::path::Path;
use rusqlite::Connection;
use crate::{log, Options};
use crate::internal::rpc::Package;
pub fn query(options: Options) -> Vec<Package> {
let verbosity = options.verbosity;
if verbosity >= 1 {
log("Connecting to database".to_string());
}
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".to_string());
}
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".to_string());
}
let mut results: Vec<Package> = vec![];
for package in packages_iter {
results.push(package.unwrap());
}
if verbosity >= 1 {
log("Collected results".to_string());
}
results
}

@ -0,0 +1,30 @@
use std::env;
use std::path::Path;
use rusqlite::Connection;
use crate::{log, Options};
pub fn remove(pkg: &str, options: Options) {
let conn = Connection::open(Path::new(&format!(
"{}/.local/share/ame/db.sqlite",
env::var("HOME").unwrap()
)))
.expect("Couldn't connect to database");
let verbosity = options.verbosity;
if verbosity >= 1 {
log(format!("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");
}

@ -0,0 +1,25 @@
use regex::Regex;
use crate::internal::strings::log;
use crate::Options;
pub fn clean(a: &[String], options: Options) -> Vec<String> {
let r = Regex::new(r"(\S+)((?:>=|<=|>|<)\S+$)").unwrap();
let mut cleaned: Vec<String> = vec![];
let verbosity = options.verbosity;
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());
cleaned.push(c.to_string());
} else {
cleaned.push(b.to_string());
}
}
if verbosity >= 1 {
log(format!("Cleaned: {:?}\nInto: {:?}", a, cleaned));
}
cleaned
}

@ -0,0 +1,48 @@
use std::env;
use std::path::Path;
use crate::internal::strings::{crash, log};
use crate::Options;
pub fn init(options: Options) {
let verbosity = options.verbosity;
let homedir = env::var("HOME").unwrap();
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(format!("Created path: {}/.local/share/ame", homedir));
}
}
Err(e) => {
crash(
format!("Couldn't create path: {}/.local/share/ame: {}", homedir, e),
4,
);
}
}
}
if !Path::new(&format!("{}/.local/share/ame/db.sqlite", homedir)).exists() {
crate::database::init(options);
}
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(format!("Created path: {}/.cache/ame", homedir));
}
}
Err(e) => {
crash(
format!("Couldn't create path: {}/.cache/ame: {}", homedir, e),
4,
);
}
}
}
}

@ -0,0 +1,53 @@
use crate::Options;
mod clean;
mod initialise;
pub mod rpc;
mod sort;
mod strings;
pub mod structs;
pub fn sort(a: &[String], options: Options) -> structs::Sorted {
sort::sort(a, options)
}
pub fn clean(a: &[String], options: Options) -> Vec<String> {
clean::clean(a, options)
}
pub fn init(options: Options) {
initialise::init(options);
}
pub fn info(a: String) {
strings::info(a);
}
pub fn crash(a: String, b: i32) {
strings::crash(a, b);
}
pub fn log(a: String) {
strings::log(a);
}
pub fn prompt(a: String, b: bool) -> bool {
strings::prompt(a, b)
}
#[macro_export]
macro_rules! uwu {
($x:expr) => {{
let uwu: String = String::from_str($x).unwrap();
let uwu = uwu.replace("l", "w");
let uwu = uwu.replace("L", "W");
let uwu = uwu.replace("r", "w");
let uwu = uwu.replace("R", "W");
let uwu = uwu.replace("na", "nya");
let uwu = uwu.replace("Na", "Nya");
let uwu = uwu.replace("NA", "NYA");
uwu
}};
}

@ -0,0 +1,75 @@
use std::sync::Arc;
#[derive(serde::Deserialize, Debug, Clone)]
pub struct Package {
#[serde(rename = "Name")]
pub name: String,
#[serde(rename = "Version")]
pub version: String,
#[serde(rename = "Description")]
pub description: Option<String>,
#[serde(rename = "Depends")]
#[serde(default)]
pub depends: Vec<String>,
#[serde(rename = "MakeDepends")]
#[serde(default)]
pub make_depends: Vec<String>,
}
#[derive(serde::Deserialize)]
pub struct SearchResults {
pub resultcount: u32,
pub results: Vec<Package>,
}
#[derive(Clone)]
pub struct InfoResults {
pub found: bool,
pub package: Option<Package>,
}
pub const URL: &str = "https://aur.archlinux.org/";
pub fn rpcinfo(pkg: String) -> InfoResults {
let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap());
let agent = ureq::AgentBuilder::new()
.tls_connector(tls_connector)
.build();
let res: SearchResults = agent
.get(&format!(
"https://aur.archlinux.org/rpc/?v=5&type=info&arg={}",
pkg
))
.call()
.unwrap()
.into_json()
.unwrap();
if res.results.is_empty() {
InfoResults {
found: false,
package: None,
}
} else {
InfoResults {
found: true,
package: Some(res.results[0].clone()),
}
}
}
pub fn rpcsearch(pkg: String) -> SearchResults {
let tls_connector = Arc::new(native_tls::TlsConnector::new().unwrap());
let agent = ureq::AgentBuilder::new()
.tls_connector(tls_connector)
.build();
agent
.get(&format!(
"https://aur.archlinux.org/rpc/?v=5&type=search&arg={}",
pkg
))
.call()
.unwrap()
.into_json::<SearchResults>()
.unwrap()
}

@ -0,0 +1,46 @@
use std::process::{Command, Stdio};
use crate::internal::{clean, rpc, structs};
use crate::internal::strings::log;
use crate::Options;
pub fn sort(input: &[String], options: Options) -> structs::Sorted {
let mut repo: Vec<String> = vec![];
let mut aur: Vec<String> = vec![];
let mut nf: Vec<String> = vec![];
let verbosity = options.verbosity;
let a = clean(input, options);
if verbosity >= 1 {
log(format!("Sorting: {:?}", a.join(" ")));
}
for b in a {
let rs = Command::new("pacman")
.arg("-Ss")
.arg(format!("^{}$", &b))
.stdout(Stdio::null())
.status()
.expect("Something has gone wrong");
if rpc::rpcinfo(b.to_string()).found {
if verbosity >= 1 {
log(format!("{} found in AUR", b));
}
aur.push(b.to_string());
} else if let Some(0) = rs.code() {
if verbosity >= 1 {
log(format!("{} found in repos", b));
}
repo.push(b.to_string());
} else {
if verbosity >= 1 {
log(format!("{} not found", b));
}
nf.push(b.to_string());
}
}
structs::Sorted::new(repo, aur, nf)
}

@ -0,0 +1,76 @@
use std::{env, io};
use std::io::Write;
use std::process::exit;
use std::str::FromStr;
use std::time::UNIX_EPOCH;
use crate::uwu;
pub fn info(a: String) {
let a = if env::var("AME_UWU").unwrap_or_else(|_| "".to_string()) == "true" {
uwu!(&a)
} else {
a
};
println!("\x1b[2;22;35m❖\x1b[0m \x1b[1;37m{}\x1b[0m", a)
}
pub fn crash(a: String, b: i32) {
let a = if env::var("AME_UWU").unwrap_or_else(|_| "".to_string()) == "true" {
uwu!(&a)
} else {
a
};
println!("\x1b[2;22;31m❌:\x1b[0m \x1b[1;91m{}\x1b[0m", a);
exit(b);
}
pub fn log(a: String) {
let a = if env::var("AME_UWU").unwrap_or_else(|_| "".to_string()) == "true"
&& env::var("AME_UWU_DEBUG").unwrap_or_else(|_| "".to_string()) == "true"
{
uwu!(&a)
} else {
a
};
eprintln!(
"{} {}",
std::time::SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
a
);
}
pub fn prompt(a: String, b: bool) -> bool {
let default = ["[Y/n]", "[y/N]"];
let i = if b { 0 } else { 1 };
let a = if env::var("AME_UWU").unwrap_or_else(|_| "".to_string()) == "true" {
uwu!(&a)
} else {
a
};
print!(
"\x1b[2;22;35m?\x1b[0m \x1b[1;37m{}\x1b[0m \x1b[2;22;37m{}\x1b[0m: ",
a, default[i]
);
let mut yn: String = String::new();
io::stdout().flush().ok();
let _ = std::io::stdin().read_line(&mut yn);
if yn.trim().to_lowercase() == "n" || yn.trim().to_lowercase() == "no" {
false
} else if yn.trim().to_lowercase() == "y" || yn.trim().to_lowercase() == "yes" {
true
} else {
b
}
}

@ -0,0 +1,23 @@
#[derive(Debug, serde::Serialize)]
pub struct Sorted {
#[allow(dead_code)]
pub repo: Vec<String>,
#[allow(dead_code)]
pub aur: Vec<String>,
#[allow(dead_code)]
pub nf: Vec<String>,
}
impl Sorted {
pub fn new(repo: Vec<String>, aur: Vec<String>, nf: Vec<String>) -> Self {
let a: Sorted = Sorted { repo, aur, nf };
a
}
}
#[derive(Clone, Copy)]
pub struct Options {
pub verbosity: i32,
pub noconfirm: bool,
pub asdeps: bool,
}

@ -1,25 +1,16 @@
mod mods;
use mods::{
clearcache::clearcache,
clone::clone,
database::create_database,
help::help,
inssort::{inssort, inssort_from_file},
install::install,
purge::{purge, purge_from_file},
search::{a_search, r_search},
stat_database::*,
statpkgs::*,
strs::err_rec,
strs::err_unrec,
strs::inf,
uninstall::{uninstall, uninstall_from_file},
update::update,
upgrade::upgrade,
ver::ver,
xargs::*,
};
use std::{env, process::exit};
use std::io;
use std::process::{exit, Command};
use clap::{App, AppSettings, Arg, ArgMatches, ArgSettings, Shell, SubCommand};
use crate::internal::{crash, info, init, log, sort, structs::Options};
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
mod database;
mod internal;
mod operations;
fn main() {
extern "C" {
@ -27,92 +18,287 @@ fn main() {
}
if unsafe { geteuid() } == 0 {
// check if user runs ame as root
err_unrec(
"Do not run ame as root! this can cause serious damage to your system!".to_string(),
);
crash("Running amethyst as root is disallowed as it can lead to system breakage. Instead, amethyst will prompt you when it needs superuser permissions".to_string(), 1);
}
let args: Vec<String> = env::args().skip(1).collect();
let mut pkgs: Vec<String> = env::args().skip(2).collect();
if args.is_empty() {
help();
exit(1);
fn build_app() -> App<'static, 'static> {
let app = App::new("Amethyst")
.version(env!("CARGO_PKG_VERSION"))
.about(env!("CARGO_PKG_DESCRIPTION"))
.arg(
Arg::with_name("verbose")
.short("v")
.long("verbose")
.multiple(true)
.set(ArgSettings::Global)
.help("Sets the level of verbosity"),
)
.arg(
Arg::with_name("noconfirm")
.long("noconfirm")
.set(ArgSettings::Global)
.help("Complete operation without prompting user"),
)
.subcommand(
SubCommand::with_name("install")
.about(
"Installs a package from either the AUR or the PacMan-defined repositories",
)
.aliases(&["-S", "ins"])
.arg(
Arg::with_name("package(s)")
.help("The name of the package(s) to install")
.required(true)
.multiple(true)
.index(1),
),
)
.subcommand(
SubCommand::with_name("remove")
.about("Removes a previously installed package")
.aliases(&["-R", "-Rs", "rm"])
.arg(
Arg::with_name("package(s)")
.help("The name of the package(s) to remove")
.required(true)
.multiple(true)
.index(1),
),
)
.subcommand(
SubCommand::with_name("search")
.about("Searches for the relevant packages in both the AUR and repos")
.aliases(&["-Ss", "sea"])
.arg(
Arg::with_name("aur")
.short("a")
.long("aur")
.help("Search only the AUR for the package"),
)
.arg(
Arg::with_name("repo")
.short("r")
.long("repo")
.help("Searches only local repos for the package"),
)
.arg(
Arg::with_name("package(s)")
.help("The name of the package to search for")
.required(true)
.multiple(false)
.index(1),
),
)
.subcommand(
SubCommand::with_name("query")
.about("Queries installed packages")
.aliases(&["-Q", "ls"])
.arg(
Arg::with_name("aur")
.short("a")
.help("Lists AUR/foreign packages"),
)
.arg(
Arg::with_name("repo")
.short("r")
.help("Lists repo/native packages"),
),
)
.subcommand(
SubCommand::with_name("upgrade")
.about("Upgrades locally installed packages to their latest versions")
.aliases(&["-Syu", "upg"]),
)
.subcommand(
SubCommand::with_name("compgen")
.about("Generates shell completions for given shell (bash by default)")
.aliases(&["-G", "cg"])
.arg(
Arg::with_name("shell")
.help("The name of the shell you want to generate completions for")
.possible_values(&["bash", "fish", "zsh", "pwsh", "elvish"])
.required(true),
),
)
.settings(&[
AppSettings::GlobalVersion,
AppSettings::VersionlessSubcommands,
AppSettings::ArgRequiredElseHelp,
AppSettings::InferSubcommands,
]);
app
}
let file = format!("{}/.local/share/ame/aur_pkgs.db", env::var("HOME").unwrap());
if !std::path::Path::new(&file).exists() {
create_database();
let matches = build_app().get_matches();
let verbosity: i32 = matches.occurrences_of("verbose") as i32;
let noconfirm: bool = matches.is_present("noconfirm");
let options = Options {
verbosity,
noconfirm,
asdeps: false,
};
init(options);
fn collect_matches(a: &ArgMatches) -> Vec<String> {
a.subcommand()
.1
.unwrap()
.values_of("package(s)")
.unwrap()
.into_iter()
.map(|s| s.to_string())
.collect()
}
let oper = &args[0];
let noconfirm: bool = noconf(&args);
if let true = matches.is_present("install") {
let packages = collect_matches(&matches);
let sorted = sort(&packages, options);
argssort(&mut pkgs);
match oper.as_str() {
// match oper
"-S" | "-Sn" | "ins" => {
inssort(noconfirm, false, pkgs); // install
}
"-Sl" | "-Sln" | "insl" => {
inssort_from_file(noconfirm, false, &pkgs[0]); // install from file
}
"-B" | "-Bn" | "build" => {
rebuild(noconfirm); // install as a dependency
}
"-R" | "-Rn" | "rm" => {
uninstall(noconfirm, pkgs); // uninstall
}
"-Rs" | "-Rsn" | "purge" => {
purge(noconfirm, pkgs); // purge
}
"-Rl" | "-Rln" | "rml" => {
uninstall_from_file(noconfirm, &pkgs[0]); // uninstall from file
}
"-Rsl" | "-Rsln" | "purgel" => {
purge_from_file(noconfirm, &pkgs[0]); // purge from file
info(format!(
"Attempting to install packages: {}",
packages.join(", ")
));
if !sorted.repo.is_empty() {
operations::install(sorted.repo, options);
}
"-Syu" | "-Syun" | "upg" => {
upgrade(noconfirm); // upgrade
if !sorted.aur.is_empty() {
operations::aur_install(sorted.aur, options);
}
"-Sy" | "upd" => {
update(); // update
if !sorted.nf.is_empty() {
log(format!(
"Couldn't find packages: {} in repos or the AUR",
sorted.nf.join(", ")
));
}
"-Ss" | "sea" => {
r_search(&args[1]); // search for packages in the repository
a_search(&args[1]); // search for packages in the aur
exit(0);
}
if let true = matches.is_present("remove") {
let packages = collect_matches(&matches);
info(format!("Uninstalling packages: {}", &packages.join(", ")));
operations::uninstall(packages, options);
exit(0);
}
if let true = matches.is_present("upgrade") {
info("Performing system upgrade".to_string());
operations::upgrade(options);
exit(0);
}
if let true = matches.is_present("search") {
let packages = collect_matches(&matches);
if matches
.subcommand_matches("search")
.unwrap()
.is_present("aur")
{
info(format!("Searching AUR for {}", &packages[0]));
operations::aur_search(&packages[0], options);
}
"-Sa" | "aursea" => {
a_search(&args[1]); // search for packages in the aur
if matches
.subcommand_matches("search")
.unwrap()
.is_present("repo")
{
info(format!("Searching repos for {}", &packages[0]));
operations::search(&packages[0], options);
}
"-Sr" | "repsea" => {
r_search(&args[1]); // search for packages in the repository
if !matches
.subcommand_matches("search")
.unwrap()
.is_present("repo")
&& !matches
.subcommand_matches("search")
.unwrap()
.is_present("aur")
{
info(format!("Searching AUR and repos for {}", &packages[0]));
operations::search(&packages[0], options);
operations::aur_search(&packages[0], options);
}
"-Cc" | "clr" => {
clearcache(); // clear cache
exit(0);
}
if let true = matches.is_present("query") {
if matches
.subcommand_matches("query")
.unwrap()
.is_present("aur")
{
Command::new("pacman")
.arg("-Qm")
.spawn()
.expect("Something has gone wrong")
.wait()
.unwrap();
}
"-v" | "-V" | "ver" => {
ver(); // version
if matches
.subcommand_matches("query")
.unwrap()
.is_present("repo")
{
Command::new("pacman")
.arg("-Qn")
.spawn()
.expect("Something has gone wrong")
.wait()
.unwrap();
}
"-h" | "help" => {
help(); // help
if !matches
.subcommand_matches("query")
.unwrap()
.is_present("aur")
&& !matches
.subcommand_matches("query")
.unwrap()
.is_present("repo")
{
Command::new("pacman")
.arg("-Qn")
.spawn()
.expect("Something has gone wrong")
.wait()
.unwrap();
Command::new("pacman")
.arg("-Qm")
.spawn()
.expect("Something has gone wrong")
.wait()
.unwrap();
}
_ => {
// if oper is not valid it either passes the args to pacman or prints an error
let pass = runas::Command::new("pacman")
.args(&args)
.status()
.expect("Something has gone wrong.");
match pass.code() {
Some(1) => {
err_rec(format!("No such operation \"{}\"", args.join(" ")));
inf("Try running \"ame help\" for an overview of how to use ame".to_string())
}
Some(_) => {}
None => err_unrec("Something has gone terribly wrong.".to_string()),
exit(0);
}
if let true = &matches.is_present("compgen") {
let mut app = build_app();
match matches
.subcommand_matches("compgen")
.unwrap()
.value_of("shell")
.unwrap()
{
"bash" => {
app.gen_completions_to("ame", Shell::Bash, &mut io::stdout());
}
"fish" => {
app.gen_completions_to("ame", Shell::Fish, &mut io::stdout());
}
"zsh" => {
app.gen_completions_to("ame", Shell::Zsh, &mut io::stdout());
}
"pwsh" => {
app.gen_completions_to("ame", Shell::PowerShell, &mut io::stdout());
}
"elvish" => {
app.gen_completions_to("ame", Shell::Elvish, &mut io::stdout());
}
_ => {}
}
}
}
}

@ -1,17 +0,0 @@
pub mod clearcache;
pub mod clone;
pub mod database;
pub mod help;
pub mod inssort;
pub mod install;
pub mod purge;
pub mod rpc;
pub mod search;
pub mod stat_database;
pub mod statpkgs;
pub mod strs;
pub mod uninstall;
pub mod update;
pub mod upgrade;
pub mod ver;
pub mod xargs;

@ -1,12 +0,0 @@
use crate::mods::strs::err_rec;
use std::fs;
pub fn clearcache() {
// delete all files in cache
let path = format!("{}/.cache/ame/", std::env::var("HOME").unwrap());
err_rec("Clearing cache".to_string());
fs::remove_dir_all(&path).unwrap();
fs::create_dir(&path).unwrap();
}

@ -1,180 +0,0 @@
use crate::{
err_unrec, inf, mods::database::add_pkg, mods::purge::purge, mods::rpc::*, mods::strs::prompt,
mods::strs::sec, mods::strs::succ, inssort
};
use moins::Moins;
use std::{env, fs, path::Path, process::Command};
fn uninstall_make_depend(pkg: &str) {
// uninstall make depends of a package
let make_depends = rpcinfo(pkg).make_depends;
let explicit_packages = Command::new("pacman")
.arg("-Qetq")
.stdout(std::process::Stdio::piped())
.output()
.expect("Something has gone terribly wrong");
let expl_pkgs_parse = String::from_utf8(explicit_packages.stdout).unwrap();
let expl_pkgs_parse = expl_pkgs_parse.split('\n').collect::<Vec<&str>>();
let mut rem_pkgs: Vec<String> = Vec::new();
for pkg in expl_pkgs_parse {
for md in &make_depends {
if let false = md.contains(pkg) {
if let false = rem_pkgs.contains(md) {
rem_pkgs.push(md.as_str().to_string());
}
};
}
}
if !rem_pkgs.is_empty() {
inf(format!(
"{} installed following make dependencies: {}",
pkg,
rem_pkgs.join(", ")
));
let remove = prompt("Would you like to remove them?".to_string());
if remove {
purge(true, rem_pkgs);
}
}
succ(format!("Succesfully installed {}", pkg));
}
pub fn clone(noconfirm: bool, as_dep: bool, pkg: &str) {
// clone a package from aur
let cachedir = format!("{}/.cache/ame", env::var("HOME").unwrap());
let path = Path::new(&cachedir);
let pkgdir = format!("{}/{}", &cachedir, &pkg);
let search = rpcsearch(pkg).results;
let package = search.first().unwrap();
if search.is_empty() {
err_unrec("No matching AUR packages found".to_string());
}
let url = format!("https://aur.archlinux.org/{}.git", pkg);
if !Path::new(&format!("{}/.cache", env::var("HOME").unwrap())).exists() {
fs::create_dir_all(format!("{}/.cache", env::var("HOME").unwrap()))
.expect("Failed to create ~/.cache directory");
}
if !path.is_dir() {
let cache_result = fs::create_dir(&path);
match cache_result {
Ok(_) => inf("Created cache path (first run)".to_string()),
Err(_) => err_unrec("Could not create cache path".to_string()),
}
}
inf(format!("Cloning {} ...", pkg));
if Path::new(&pkgdir).is_dir() {
let rm_result = fs::remove_dir_all(&pkgdir);
match rm_result {
Ok(_) => inf(format!(
"Package path for {} already found. Removing to reinstall",
pkg
)),
Err(_) => err_unrec(format!(
"Package path for {} already found, but could not remove to reinstall",
pkg
)),
}
}
let dir_result = fs::create_dir(&pkgdir);
match dir_result {
Ok(_) => inf(format!("Created package directory for {}", pkg)),
Err(_) => err_unrec(format!("Couldn't create package directory for {}", pkg)),
}
let cd_result = env::set_current_dir(&pkgdir);
match cd_result {
Ok(_) => inf("Entered package directory".to_string()),
Err(_) => err_unrec("Could not enter package directory".to_string()),
}
sec("Installing AUR package depends".to_string());
let clone = std::process::Command::new("git")
.arg("clone")
.arg(&url)
.arg(&pkgdir)
.status()
.expect("Couldn't clone repository");
match clone.code() {
Some(0) => {
inf(format!("Cloning {} into package directory", pkg));
}
Some(_) => err_unrec(format!("Failed cloning {} into package directory", pkg)),
_ => err_unrec(format!("Failed cloning {} into package directory", pkg)),
}
if !as_dep {
if !noconfirm {
let pkgbuild = prompt("View PKGBUILD?".to_string());
if pkgbuild {
let mut pkgbld = fs::read_to_string(format!("{}/PKGBUILD", &pkgdir)).unwrap();
Moins::run(&mut pkgbld, None);
}
}
sec(format!("Installing {} ...", pkg));
if noconfirm {
let install_result = Command::new("makepkg")
.arg("-si")
.arg("--noconfirm")
.arg("--needed")
.status();
match install_result {
Ok(_) => {
uninstall_make_depend(pkg);
let vec = vec![pkg];
add_pkg(false, &vec);
}
Err(_) => {
err_unrec(format!("Couldn't install {}", pkg));
}
};
} else {
let install_result = Command::new("makepkg")
.arg("-si")
.arg("--needed")
.status()
.expect("Couldn't call makepkg");
match install_result.code() {
Some(0) => {
uninstall_make_depend(pkg);
let vec = vec![pkg];
add_pkg(false, &vec);
}
Some(_) => {
err_unrec(format!("Couldn't install {}", pkg));
}
None => {
err_unrec(format!("Couldn't install {}", pkg));
}
};
}
} else {
sec(format!("Installing {} ...", pkg));
let install_result = Command::new("makepkg")
.arg("-si")
.arg("--noconfirm")
.arg("--needed")
.arg("--asdeps")
.status();
match install_result {
Ok(_) => {
uninstall_make_depend(pkg);
let vec = vec![pkg];
add_pkg(false, &vec);
}
Err(_) => {
err_unrec(format!("Couldn't install {}", pkg));
}
};
}
}

@ -1,24 +0,0 @@
use crate::mods::strs::{err_rec, inf};
pub fn help() {
// print help message
println!();
inf("Usage:".to_string());
println!(
"
ame -S(n) / ins <pkg> - install a package
ame -R(n) / rm <pkg> - remove a package
ame -Rs(n) / purge <pkg> - remove a package with it dependencies
ame -Syu(n) / upg - upgrade all packages to latest version
ame -Ss / sea <pkg> - search for a package
ame -Sa / aursea <pkg> - search for a package in the aur
ame -Sr / repsea <pkg> - search for a package in the repos
ame -v / ver - contributors and version info
ame -h / help - display this help message
ame <any valid pacman flags> - passes said flags to be processed by pacman"
);
println!();
err_rec("Appending 'n' where (n) is present passes '--noconfirm' to pacman. Use at your own risk. (alternatively, using '--noconfirm' as a flag works too.)".to_string());
println!();
}

@ -1,208 +0,0 @@
use crate::{clone, err_unrec, install, mods::strs::sec, mods::rpc::*};
use regex::Regex;
use std::process::{Command, Stdio};
pub fn inssort(noconfirm: bool, as_dep: bool, pkgs: Vec<String>) {
// TODO: understand what the fuck is actually going on here
let mut repo = vec![];
let mut aur = vec![];
let re = Regex::new(r"(\S+)((?:>=|<=|>|<)\S+$)").unwrap();
let reg = Regex::new(r"((?:>=|<=|>|<)\S+$)").unwrap();
for pkg in pkgs {
match pkg.contains('/') {
true => match pkg.split('/').collect::<Vec<&str>>()[0] == "aur" {
true => {
aur.push(pkg.split('/').collect::<Vec<&str>>()[1].to_string());
}
false => {
let out = Command::new("bash")
.arg("-c")
.arg(format!(
"pacman -Sl {} | grep {}",
pkg.split('/').collect::<Vec<&str>>()[0],
pkg.split('/').collect::<Vec<&str>>()[1]
))
.stdout(Stdio::null())
.status()
.expect("Something has gone wrong.");
match out.code() {
Some(0) => repo.push(reg.replace_all(&pkg, "").to_string()),
Some(1) => err_unrec(format!(
"Package {} not found in repository {}",
pkg.split('/').collect::<Vec<&str>>()[1],
pkg.split('/').collect::<Vec<&str>>()[0]
)),
Some(_) => err_unrec("Something has gone terribly wrong".to_string()),
None => err_unrec("Process terminated".to_string()),
}
}
},
false => {
let caps = re.captures(&pkg);
match caps {
Some(_) => {
let out = Command::new("pacman")
.arg("-Ss")
.arg(format!(
"^{}$",
caps.unwrap().get(1).map_or("", |m| m.as_str())
))
.stdout(Stdio::null())
.status()
.expect("Something has gone wrong.");
match out.code() {
Some(0) => repo.push(reg.replace_all(&pkg, "").to_string()),
Some(1) => aur.push(pkg),
Some(_) => err_unrec("Something has gone terribly wrong".to_string()),
None => err_unrec("Process terminated".to_string()),
}
}
None => {
let out = Command::new("pacman")
.arg("-Ss")
.arg(format!("^{}$", &pkg))
.stdout(Stdio::null())
.status()
.expect("Something has gone wrong.");
match out.code() {
Some(0) => repo.push(pkg),
Some(1) => aur.push(pkg),
Some(_) => err_unrec("Something has gone terribly wrong".to_string()),
None => err_unrec("Process terminated".to_string()),
}
}
}
}
}
}
if !as_dep {
if !repo.is_empty() {
sec(format!("Installing repo packages: {}", &repo.join(", ")));
install(noconfirm, false, &repo.join(" "));
}
for a in aur {
sec(format!("Couldn't find {} in repos. Searching AUR", a));
let md = &rpcinfo(&a).make_depends;
inssort(noconfirm, true, md.to_vec());
clone(noconfirm, false, &a);
}
} else {
if !repo.is_empty() {
sec(format!("Installing repo packages: {}", &repo.join(", ")));
install(noconfirm, true, &repo.join(" "));
}
for a in aur {
sec(format!("Couldn't find {} in repos. Searching AUR", a));
let md = &rpcinfo(&a).make_depends;
inssort(noconfirm, true, md.to_vec());
clone(noconfirm, true, &a);
}
}
}
pub fn inssort_from_file(noconfirm: bool, as_dep: bool, file: &str) {
// same thing as above but with a list of packages from a file
let mut pkgs: Vec<String> = Vec::new();
let contents = std::fs::read_to_string(&file).expect("Couldn't read file");
for line in contents.lines() {
pkgs.push(line.to_string());
}
let mut repo = vec![];
let mut aur = vec![];
let re = Regex::new(r"(\S+)((?:>=|<=)\S+$)").unwrap();
let reg = Regex::new(r"((?:>=|<=)\S+$)").unwrap();
for pkg in pkgs {
match pkg.contains('/') {
true => match pkg.split('/').collect::<Vec<&str>>()[0] == "aur" {
true => {
aur.push(pkg.split('/').collect::<Vec<&str>>()[1].to_string());
}
false => {
let out = Command::new("bash")
.arg("-c")
.arg(format!(
"pacman -Sl {} | grep {}",
pkg.split('/').collect::<Vec<&str>>()[0],
pkg.split('/').collect::<Vec<&str>>()[1]
))
.stdout(Stdio::null())
.status()
.expect("Something has gone wrong.");
match out.code() {
Some(0) => repo.push(reg.replace_all(&pkg, "").to_string()),
Some(1) => err_unrec(format!(
"Package {} not found in repository {}",
pkg.split('/').collect::<Vec<&str>>()[1],
pkg.split('/').collect::<Vec<&str>>()[0]
)),
Some(_) => err_unrec("Something has gone terribly wrong".to_string()),
None => err_unrec("Process terminated".to_string()),
}
}
},
false => {
let caps = re.captures(&pkg);
match caps {
Some(_) => {
let out = Command::new("pacman")
.arg("-Ss")
.arg(format!(
"^{}$",
caps.unwrap().get(1).map_or("", |m| m.as_str())
))
.stdout(Stdio::null())
.status()
.expect("Something has gone wrong.");
match out.code() {
Some(0) => repo.push(reg.replace_all(&pkg, "").to_string()),
Some(1) => aur.push(pkg),
Some(_) => err_unrec("Something has gone terribly wrong".to_string()),
None => err_unrec("Process terminated".to_string()),
}
}
None => {
let out = Command::new("pacman")
.arg("-Ss")
.arg(format!("^{}$", &pkg))
.stdout(Stdio::null())
.status()
.expect("Something has gone wrong.");
match out.code() {
Some(0) => repo.push(pkg),
Some(1) => aur.push(pkg),
Some(_) => err_unrec("Something has gone terribly wrong".to_string()),
None => err_unrec("Process terminated".to_string()),
}
}
}
}
}
}
if !as_dep {
if !repo.is_empty() {
sec(format!("Installing repo packages: {}", &repo.join(", ")));
install(noconfirm, false, &repo.join(" "));
}
for a in aur {
sec(format!("Couldn't find {} in repos. Searching AUR", a));
let md = &rpcinfo(&a).make_depends;
inssort(noconfirm, true, md.to_vec());
clone(noconfirm, false, &a);
}
} else {
if !repo.is_empty() {
sec(format!("Installing repo packages: {}", &repo.join(", ")));
install(noconfirm, true, &repo.join(" "));
}
for a in aur {
sec(format!("Couldn't find {} in repos. Searching AUR", a));
let md = &rpcinfo(&a).make_depends;
inssort(noconfirm, true, md.to_vec());
clone(noconfirm, true, &a);
}
}
}

@ -1,59 +0,0 @@
use crate::mods::database::add_pkg;
use crate::mods::strs::{err_unrec, succ};
use runas::Command;
pub fn install(noconfirm: bool, as_dep: bool, pkg: &str) {
// install a package
let pkgs: Vec<&str> = pkg.split(' ').collect();
if !as_dep {
if noconfirm {
let result = Command::new("pacman")
.arg("-S")
.arg("--noconfirm")
.arg("--needed")
.args(&pkgs)
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!("Succesfully installed packages: {}", pkg));
add_pkg(true, &pkgs);
}
Some(_) => err_unrec(format!("Couldn't install packages: {}", pkg)),
None => err_unrec(format!("Couldn't install packages: {}", pkg)),
};
} else {
let result = Command::new("pacman")
.arg("-S")
.arg("--needed")
.args(&pkgs)
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!("Succesfully installed packages: {}", pkg));
add_pkg(true, &pkgs);
}
Some(_) => err_unrec(format!("Couldn't install packages: {}", pkg)),
None => err_unrec(format!("Couldn't install packages: {}", pkg)),
};
}
} else {
let result = Command::new("pacman")
.arg("-S")
.arg("--noconfirm")
.arg("--needed")
.arg("--asdeps")
.args(&pkgs)
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!("Succesfully installed packages: {}", pkg));
add_pkg(true, &pkgs);
}
Some(_) => err_unrec(format!("Couldn't install packages: {}", pkg)),
None => err_unrec(format!("Couldn't install packages: {}", pkg)),
};
}
}

@ -1,121 +0,0 @@
use crate::mods::{
database::rem_pkg,
strs::{err_rec, err_unrec, sec, succ},
};
use runas::Command;
use std::{fs, path::Path};
pub fn purge(noconfirm: bool, pkgs: Vec<String>) {
// purge packages
sec(format!(
"Attempting to uninstall packages: {}",
&pkgs.join(" ")
));
if noconfirm {
let result = Command::new("pacman")
.arg("-Rsu")
.args(&pkgs)
.arg("--noconfirm")
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!(
"Succesfully uninstalled packages: {}",
&pkgs.join(" ")
));
rem_pkg(&pkgs);
}
Some(_) => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
None => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
};
} else {
let result = Command::new("pacman")
.arg("-Rsu")
.args(&pkgs)
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!(
"Succesfully uninstalled packages: {}",
&pkgs.join(" ")
));
rem_pkg(&pkgs);
}
Some(_) => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
None => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
};
}
for pkg in &pkgs {
let pkgdir = format!("{}/.cache/ame/{}", std::env::var("HOME").unwrap(), pkg);
let path = Path::new(&pkgdir);
if path.is_dir() {
let rm_result = fs::remove_dir_all(&path);
match rm_result {
Ok(_) => succ(format!("Removed AUR cache directory for {}", pkg)),
Err(_) => err_unrec(format!("Failed to remove AUR cache directory for {}", pkg)),
};
}
}
}
pub fn purge_from_file(noconfirm: bool, file: &str) {
// purge packages from list of packages
let mut pkgs: Vec<String> = Vec::new();
let contents = std::fs::read_to_string(&file).expect("Couldn't read file");
for line in contents.lines() {
pkgs.push(line.to_string());
}
sec(format!(
"Attempting to uninstall packages: {}",
&pkgs.join(" ")
));
if noconfirm {
let result = Command::new("pacman")
.arg("-Rsu")
.args(&pkgs)
.arg("--noconfirm")
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!(
"Succesfully uninstalled packages: {}",
&pkgs.join(" ")
));
rem_pkg(&pkgs);
}
Some(_) => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
None => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
};
} else {
let result = Command::new("pacman")
.arg("-Rsu")
.args(&pkgs)
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!(
"Succesfully uninstalled packages: {}",
&pkgs.join(" ")
));
rem_pkg(&pkgs);
}
Some(_) => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
None => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
};
}
for pkg in &pkgs {
let pkgdir = format!("{}/.cache/ame/{}", std::env::var("HOME").unwrap(), pkg);
let path = Path::new(&pkgdir);
if path.is_dir() {
let rm_result = fs::remove_dir_all(&path);
match rm_result {
Ok(_) => succ(format!("Removed AUR cache directory for {}", pkg)),
Err(_) => err_unrec(format!("Failed to remove AUR cache directory for {}", pkg)),
};
}
}
}

@ -1,39 +0,0 @@
#[derive(serde::Deserialize, Debug, Clone)]
pub struct Package {
#[serde(rename = "Name")]
pub name: String,
#[serde(rename = "Version")]
pub version: String,
#[serde(rename = "Description")]
pub description: Option<String>,
#[serde(default)]
#[serde(rename = "Depends")]
pub depends: Vec<String>,
#[serde(default)]
#[serde(rename = "MakeDepends")]
pub make_depends: Vec<String>,
}
#[derive(serde::Deserialize)]
pub struct SearchResults {
pub resultcount: u32,
pub results: Vec<Package>,
}
pub fn rpcinfo(pkg: &str) -> Package {
let res = reqwest::blocking::get(&format!(
"https://aur.archlinux.org/rpc/?v=5&type=info&arg={}",
pkg
)).unwrap();
res.json::<SearchResults>().unwrap().results[0].clone()
}
pub fn rpcsearch(pkg: &str) -> SearchResults {
let res = reqwest::blocking::get(&format!(
"https://aur.archlinux.org/rpc/?v=5&type=search&arg={}",
pkg
)).unwrap();
res.json().unwrap()
}

@ -1,44 +0,0 @@
use crate::mods::rpc::*;
use crate::mods::strs::{err_rec, err_unrec, succ};
use ansi_term::Colour;
use std::process::Command;
pub fn a_search(pkg: &str) {
// search for a package in the AUR
let results = rpcsearch(pkg).results;
for r in &results {
if results.is_empty() {
err_rec("No matching AUR packages found".to_string());
}
println!(
"{}{} {}\n {}",
Colour::Cyan.bold().paint("aur/"),
Colour::White.bold().paint(&r.name),
Colour::Green.bold().paint(&r.version),
Colour::White.paint(
r.description
.as_ref()
.unwrap_or(&"No description available".to_string())
)
);
}
if !results.is_empty() {
succ("AUR search successful".to_string());
}
}
pub fn r_search(pkg: &str) {
// search for a package in the repositories
let result = Command::new("pacman")
.arg("-Ss")
.arg(&pkg)
.status()
.unwrap();
match result.code() {
Some(0) => succ("Repo search successful".to_string()),
Some(1) => err_rec("No matching repo packages found".to_string()),
Some(_) => err_unrec("Someting went terribly wrong".to_string()),
None => err_unrec("Couldn't search pacman repos".to_string()),
};
}

@ -1,100 +0,0 @@
use crate::{err_unrec, inf};
use std::env;
pub fn stat_dump_dat() -> Vec<String> {
let file = format!("{}/.local/share/ame/aur_pkgs.db", env::var("HOME").unwrap());
let connection = sqlite::open(file).unwrap();
let mut dat_pkgs = Vec::new();
let result = connection.iterate("SELECT name FROM static_pkgs", |pairs| {
for &(_column, value) in pairs.iter() {
dat_pkgs.push(value.unwrap().to_string());
}
true
});
match result {
Ok(_) => {
//inf("Dumped static packages".to_string());
}
Err(_) => err_unrec("Couldn't dump packages from database".to_string()),
}
dat_pkgs
}
pub fn stat_get_value(pkg: &str, sear_value: &str) -> bool {
let file = format!("{}/.local/share/ame/aur_pkgs.db", env::var("HOME").unwrap());
let connection = sqlite::open(file).unwrap();
let mut return_val = false;
match sear_value {
"name" => {
let result = connection.iterate(
format!("SELECT name FROM static_pkgs WHERE name = \"{}\";", &pkg),
|pairs| {
for &(_column, _value) in pairs.iter() {
return_val = true;
}
return_val
},
);
match result {
Ok(_) => {}
Err(_) => err_unrec("Couldn't get value from database".to_string()),
}
return return_val;
}
"update" => {
let result = connection.iterate(
format!("SELECT pin FROM static_pkgs WHERE name = \"{}\";", &pkg),
|pairs| {
for &(_column, _value) in pairs.iter() {
return_val = true;
}
return_val
},
);
match result {
Ok(_) => {}
Err(_) => err_unrec("Couldn't get value from database".to_string()),
}
return return_val;
}
_ => return_val = false,
}
return_val
}
pub fn stat_rem_pkg(static_pkgs: &[String]) {
let file = format!("{}/.local/share/ame/aur_pkgs.db", env::var("HOME").unwrap());
let connection = sqlite::open(file).unwrap();
print!("{:?}", static_pkgs);
for i in static_pkgs {
let result = connection.execute(format!(
"
DELETE FROM static_pkgs WHERE name = \"{}\";
",
i
));
match result {
Ok(_) => inf(format!("Removed {} from database", i)),
Err(_) => err_unrec(format!(
"Couldn't remove {} from database (static packages table)",
i
)),
}
}
}
pub fn stat_add_pkg(update: &str, pkg: &str) {
let file = format!("{}/.local/share/ame/aur_pkgs.db", env::var("HOME").unwrap());
let connection = sqlite::open(file).unwrap();
let pin = if update == "true" { 1 } else { 0 };
let result = connection.execute(format!(
"
INSERT INTO static_pkgs (name, pin) VALUES (\"{}\", {});
",
pkg, pin
));
match result {
Ok(_) => inf(format!("Added {} to database", pkg)),
Err(_) => err_unrec(format!("Couldn't add {} to database", pkg)),
}
}

@ -1,79 +0,0 @@
use crate::inf;
use crate::{
err_rec, inssort, stat_add_pkg, stat_dump_dat, stat_get_value, stat_rem_pkg, uninstall,
};
use std::{env, fs};
pub fn rebuild(noconfirm: bool) {
let file = format!("{}/.config/ame/pkgs.toml", env::var("HOME").unwrap());
let database = fs::read_to_string(&file).expect("Can't Open Database");
inf("installing crystal config".to_string());
let file = format!("{}/.local/share/ame/aur_pkgs.db", env::var("HOME").unwrap());
let connection = sqlite::open(file).unwrap();
connection
.execute(
"
CREATE TABLE IF NOT EXISTS static_pkgs (name TEXT, pin INTEGER);
",
)
.unwrap();
let db_parsed = database.parse::<toml::Value>().expect("Invalid Database");
let mut pkgs = Vec::new();
if let Some(entry) = db_parsed.as_table() {
for (key, value) in &*entry {
let mut tempvec = Vec::new();
// println!("{}", key);
// println!("{}", format!("{}",value).replace("update = ", ""));
tempvec.push(key.to_string());
tempvec.push(format!("{}", value).replace("update = ", ""));
pkgs.push(tempvec);
}
}
let mut pkgs_to_add: Vec<Vec<String>> = Vec::new();
let mut pkgs_to_install: Vec<String> = Vec::new();
for i in pkgs {
if !stat_get_value(&i[0], "name") {
let tempvec = vec![i[0].to_string(), i[1].to_string()];
pkgs_to_add.push(tempvec);
pkgs_to_install.push(i[0].to_string());
}
}
let mut config_no_change = 0;
if !pkgs_to_install.is_empty() {
inf(format!("Installing {}", pkgs_to_install.join(", ")));
inssort(noconfirm, false, pkgs_to_install);
for i in pkgs_to_add {
stat_add_pkg(&i[1], &i[0]);
}
config_no_change += 1;
}
let dat_pkgs = stat_dump_dat();
let mut pkgs = Vec::new();
if let Some(entry) = db_parsed.as_table() {
for (key, _value) in &*entry {
pkgs.push(key);
}
}
let mut pkgs_to_remove: Vec<String> = Vec::new();
for i in dat_pkgs {
if !pkgs.contains(&&i) {
pkgs_to_remove.push(i.to_string());
}
config_no_change += 1;
}
if !pkgs_to_remove.is_empty() {
inf(format!("Removing {}", pkgs_to_remove.join(", ")));
stat_rem_pkg(&pkgs_to_remove);
uninstall(noconfirm, pkgs_to_remove);
}
if config_no_change != 0 {
inf("Rebuild Complete".to_string());
} else {
err_rec("Configuration not changed!".to_string());
}
}

@ -1,109 +0,0 @@
use ansi_term::Colour;
use std::{env, io, io::Write, process, string};
use uwuizer::*;
pub fn inf(a: string::String) {
// info
if env::var("AME_UWU").unwrap_or_else(|_| "n/a".to_string()) == "YES" {
println!(
"{} {}",
Colour::Purple.paint("❖"),
Colour::White.paint(uwuize!(&a))
);
} else {
println!("{} {}", Colour::Purple.paint("❖"), Colour::White.paint(a));
}
}
pub fn sec(a: string::String) {
if env::var("AME_UWU").unwrap_or_else(|_| "n/a".to_string()) == "YES" {
println!(
"{} {}",
Colour::Purple.bold().paint("❖"),
Colour::White.bold().paint(uwuize!(&a))
);
} else {
println!(
"{} {}",
Colour::Purple.bold().paint("❖"),
Colour::White.bold().paint(a)
);
}
}
pub fn succ(a: string::String) {
// success
if env::var("AME_UWU").unwrap_or_else(|_| "n/a".to_string()) == "YES" {
println!(
"{} {}",
Colour::Green.bold().paint("✓"),
Colour::Green.paint(uwuize!(&a))
);
} else {
println!(
"{} {}",
Colour::Green.bold().paint("✓"),
Colour::Green.paint(&a)
);
}
}
pub fn prompt(a: string::String) -> bool {
// prompt
if env::var("AME_UWU").unwrap_or_else(|_| "n/a".to_string()) == "YES" {
print!(
"{} {} {}",
Colour::Purple.bold().paint("❖"),
Colour::White.bold().paint(uwuize!(&a)),
Colour::White.bold().paint("(Y/n): ")
);
} else {
print!(
"{} {} {}",
Colour::Purple.bold().paint("❖"),
Colour::White.bold().paint(&a),
Colour::White.bold().paint("(Y/n): ")
);
}
io::stdout().flush().ok();
let mut yn: String = String::new();
let _ = std::io::stdin().read_line(&mut yn);
!(yn.trim() == "n" || yn.trim() == "N" || yn.trim() == "no" || yn.trim() == "No")
}
pub fn err_unrec(a: string::String) {
// unrecoverable error
if env::var("AME_UWU").unwrap_or_else(|_| "n/a".to_string()) == "YES" {
println!(
"{} {} {}",
Colour::Red.bold().paint(uwuize!("✖ Unrecoverable error:")),
Colour::Red.paint(uwuize!(&a)),
Colour::Red.bold().paint(uwuize!("Terminating."))
);
} else {
println!(
"{} {} {}",
Colour::Red.bold().paint("✖ Unrecoverable error:"),
Colour::Red.paint(a),
Colour::Red.bold().paint("Terminating.")
);
}
process::exit(1);
}
pub fn err_rec(a: string::String) {
// recoverable error
if env::var("AME_UWU").unwrap_or_else(|_| "n/a".to_string()) == "YES" {
println!(
"{} {}",
Colour::Yellow.bold().paint(uwuize!("⚠ WARNING:")),
Colour::Yellow.paint(uwuize!(&a))
);
} else {
println!(
"{} {}",
Colour::Yellow.bold().paint("⚠ WARNING:"),
Colour::Yellow.paint(a)
);
}
}

@ -1,160 +0,0 @@
use crate::mods::{
database::rem_pkg,
strs::{err_rec, err_unrec, sec, succ},
};
use runas::Command;
use std::{fs, path::Path};
pub fn uninstall(noconfirm: bool, pkgs: Vec<String>) {
// uninstall a package
sec(format!(
"Attempting to uninstall packages: {}",
&pkgs.join(" ")
));
let important = [
"base",
"linux",
"linux-firmware",
"systemd-sysvcompat",
"networkmanager",
"man-db",
"man-pages",
"texinfo",
"sudo",
"curl",
"archlinux-keyring",
"btrfs-progs",
"timeshift",
"timeshift-autosnap",
];
let mut overrides: Vec<String> = Vec::new();
if Path::new("/etc/ame/overrides.conf").exists() {
overrides = fs::read_to_string("/etc/ame/overrides.conf")
.expect("Failed to read overrides.conf")
.lines()
.map(|s| s.to_string())
.collect();
}
let mut matches: Vec<String> = Vec::new();
for pkg in pkgs.iter() {
for imp in important.iter() {
if pkg == imp && !overrides.contains(pkg) {
matches.push(pkg.to_string());
}
}
}
if !matches.is_empty() {
err_unrec(format!("The action you called for tries to uninstall packages: {} . This is disallowed by default as these are important system packages. If you fully know what you are doing and would like to uninstall these, please create an override in /etc/ame/overrides.conf.", matches.join(" ")));
}
if noconfirm {
let result = Command::new("pacman")
.arg("-Ru")
.args(&pkgs)
.arg("--noconfirm")
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!(
"Succesfully uninstalled packages: {}",
&pkgs.join(" ")
));
rem_pkg(&pkgs);
}
Some(_) => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
None => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
};
} else {
let result = Command::new("pacman")
.arg("-Ru")
.args(&pkgs)
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!(
"Succesfully uninstalled packages: {}",
&pkgs.join(" ")
));
rem_pkg(&pkgs);
}
Some(_) => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
None => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
};
}
for pkg in &pkgs {
let pkgdir = format!("{}/.cache/ame/{}", std::env::var("HOME").unwrap(), pkg);
let path = Path::new(&pkgdir);
if path.is_dir() {
let rm_result = fs::remove_dir_all(&path);
match rm_result {
Ok(_) => succ(format!("Removed AUR cache directory for {}", pkg)),
Err(_) => err_unrec(format!("Failed to remove AUR cache directory for {}", pkg)),
};
}
}
}
pub fn uninstall_from_file(noconfirm: bool, file: &str) {
// uninstall a package from a list of packages
let mut pkgs: Vec<String> = Vec::new();
let contents = std::fs::read_to_string(&file).expect("Couldn't read file");
for line in contents.lines() {
pkgs.push(line.to_string());
}
sec(format!(
"Attempting to uninstall packages: {}",
&pkgs.join(" ")
));
if noconfirm {
let result = Command::new("pacman")
.arg("-Ru")
.args(&pkgs)
.arg("--noconfirm")
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!(
"Succesfully uninstalled packages: {}",
&pkgs.join(" ")
));
rem_pkg(&pkgs);
}
Some(_) => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
None => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
};
} else {
let result = Command::new("pacman")
.arg("-Ru")
.args(&pkgs)
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => {
succ(format!(
"Succesfully uninstalled packages: {}",
&pkgs.join(" ")
));
rem_pkg(&pkgs);
}
Some(_) => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
None => err_rec(format!("Couldn't uninstall packages: {}", &pkgs.join(" "))),
};
}
for pkg in &pkgs {
let pkgdir = format!("{}/.cache/ame/{}", std::env::var("HOME").unwrap(), pkg);
let path = Path::new(&pkgdir);
if path.is_dir() {
let rm_result = fs::remove_dir_all(&path);
match rm_result {
Ok(_) => succ(format!("Removed AUR cache directory for {}", pkg)),
Err(_) => err_unrec(format!("Failed to remove AUR cache directory for {}", pkg)),
};
}
}
}

@ -1,17 +0,0 @@
use crate::mods::strs::{err_unrec, sec, succ};
use runas::Command;
pub fn update() {
// update the repositories
sec("Syncing package repos".to_string());
let result = Command::new("pacman")
.arg("-Sy")
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => succ("Repos succesfully synced".to_string()),
Some(_) => err_unrec("Couldn't sync package repos".to_string()),
None => err_unrec("Couldn't sync package repos".to_string()),
}
}

@ -1,213 +0,0 @@
use crate::{
err_rec, err_unrec, inf, inssort, mods::database::get_value, mods::rpc::*, mods::strs::prompt,
mods::strs::sec, mods::strs::succ, uninstall,
};
use runas::Command;
use std::{env, fs, path::Path};
use toml;
fn uninstall_make_depend(pkg: &str) {
// uninstall make depends installed by ame itself
let make_depends = rpcinfo(pkg).make_depends;
if !make_depends.is_empty() {
inf(format!(
"{} installed following make dependencies: {}",
pkg,
make_depends.join(", ")
));
let remove = prompt("Would you like to remove them?".to_string());
if remove {
uninstall(true, make_depends);
}
}
succ(format!("Succesfully upgraded {}", pkg));
}
pub fn upgrade(noconfirm: bool) {
// upgrade all packages
let homepath = env::var("HOME").unwrap();
let cachedir = format!("/{}/.cache/ame/", homepath);
let cache_exists = Path::new(&format!("/{}/.cache/ame/", homepath)).is_dir();
let file = format!("{}/.local/ame/aurPkgs.db", env::var("HOME").unwrap());
let database = String::new();
if Path::new(&file).exists() {
let _db = fs::read_to_string(&file).expect("Can't Open Database");
} else {
let _cdar = fs::create_dir_all(format!("/{}/.local/ame/", homepath));
match _cdar {
Ok(_) => inf("Created cache directory (previously missing)".to_string()),
Err(_) => err_unrec("Couldn't create cache directory".to_string()),
}
err_rec(String::from("Database wasn't found, creating new one"));
let _dbfile = fs::File::create(&file);
let _db = String::new();
}
let db_parsed = database.parse::<toml::Value>().expect("Invalid Database");
if !cache_exists {
let cachecreate = fs::create_dir_all(&cachedir);
match cachecreate {
Ok(_) => inf("Creating cachedir. (didn't exist previously)".to_string()),
Err(_) => err_unrec("Couldn't create cachedir".to_string()),
}
}
sec("Performing system upgrade".to_string());
if noconfirm {
let result = Command::new("pacman")
.arg("-Syu")
.arg("--noconfirm")
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => succ("All repo packages upgraded".to_string()),
Some(_) => err_unrec("Couldn't upgrade packages".to_string()),
None => err_unrec("Couldn't upgrade packages".to_string()),
};
} else {
let result = Command::new("pacman")
.arg("-Syu")
.status()
.expect("Couldn't call pacman");
match result.code() {
Some(0) => succ("All repo packages upgraded".to_string()),
Some(_) => err_unrec("Couldn't upgrade packages".to_string()),
None => err_unrec("Couldn't upgrade packages".to_string()),
};
}
if let Some(entry) = db_parsed.as_table() {
for (key, _value) in &*entry {
let results = rpcsearch(&key.to_string()).results;
let url = format!("https://aur.archlinux.org/{}.git", key);
let package = rpcinfo(&key.to_string());
let version = get_value(key, "version");
if results[0].version.contains(&version) {
let keydir = format!("{}{}", &cachedir, &key);
if Path::new(&keydir).is_dir() {
let cd_result = env::set_current_dir(&keydir);
match cd_result {
Ok(_) => inf("Entered package directory".to_string()),
Err(_) => err_unrec("Could not enter package directory".to_string()),
}
inssort(true, true, package.depends.clone());
sec(format!("Installing {} ...", &key));
let install_result = std::process::Command::new("makepkg")
.arg("-si")
.arg("--noconfirm")
.arg("--needed")
.status();
match install_result {
Ok(_) => {
uninstall_make_depend(key);
}
Err(_) => {
err_unrec(format!("Couldn't install {}", &key));
}
};
sec(format!("Installing {} ...", &key));
let install_result = std::process::Command::new("makepkg")
.arg("-si")
.arg("--needed")
.status()
.expect("Couldn't call makepkg");
match install_result.code() {
Some(0) => {
uninstall_make_depend(key);
}
Some(_) => {
err_unrec(format!("Couldn't install {}", &key));
}
None => {
err_unrec(format!("Couldn't install {}", &key));
}
};
} else {
inf(format!("Cloning {} ...", &key));
if Path::new(&keydir).is_dir() {
let rm_result = fs::remove_dir_all(&keydir);
match rm_result {
Ok(_) => inf(format!(
"Package path for {} already found. Removing to reinstall",
&key
)),
Err(_) => err_unrec(format!(
"Package path for {} already found, but could not remove to reinstall",
&key
)),
}
}
let dir_result = fs::create_dir(&keydir);
match dir_result {
Ok(_) => inf(format!("Created package directory for {}", &key)),
Err(_) => {
err_unrec(format!("Couldn't create package directory for {}", &key))
}
}
let cd_result = env::set_current_dir(&keydir);
match cd_result {
Ok(_) => inf("Entered package directory".to_string()),
Err(_) => err_unrec("Could not enter package directory".to_string()),
}
inssort(true, true, package.depends.clone());
let clone = std::process::Command::new("git")
.arg("clone")
.arg(&url)
.arg(&keydir)
.status()
.expect("Couldn't clone repo");
match clone.code() {
Some(0) => {
inf(format!("Cloning {} into package directory", &key));
}
Some(_) => {
err_unrec(format!("Failed cloning {} into package directory", &key))
}
_ => err_unrec(format!("Failed cloning {} into package directory", &key)),
}
}
sec(format!("Installing {} ...", &key));
let install_result = std::process::Command::new("makepkg")
.arg("-si")
.arg("--noconfirm")
.arg("--needed")
.status();
match install_result {
Ok(_) => {
uninstall_make_depend(key);
}
Err(_) => {
err_unrec(format!("Couldn't install {}", &key));
}
};
sec(format!("Installing {} ...", &key));
let install_result = std::process::Command::new("makepkg")
.arg("-si")
.arg("--needed")
.status()
.expect("Couldn't call makepkg");
match install_result.code() {
Some(0) => {
uninstall_make_depend(key);
}
Some(_) => {
err_unrec(format!("Couldn't install {}", &key));
}
None => {
err_unrec(format!("Couldn't install {}", &key));
}
};
} else {
inf(format!("Package {} already up to date", &key));
}
}
}
}

@ -1,27 +0,0 @@
use crate::inf;
use ansi_term::Colour;
pub fn ver() {
const VERSION: &str = env!("CARGO_PKG_VERSION");
// print version and contributors
println!();
inf(format!("ame - {}", VERSION));
println!();
inf("Contributors:".to_string());
println!("- axtlos <axtlos@tar.black>");
println!("- jnats <michal@tar.black>");
println!("- jasio <jasiobene@icloud.com>");
println!("- generic <mdc028@bucknell.edu>");
println!();
inf("This software is licensed under the BSD 3-Clause license.".to_string());
inf("All source code is available at:".to_string());
println!();
println!(
"{}",
Colour::Purple
.bold()
.paint("https://git.getcryst.al/crystal/ame")
);
println!();
}

@ -1,13 +0,0 @@
pub fn noconf(args: &[String]) -> bool {
// noconfirm if user passed --noconfirm or added n to the end of the arg
args.contains(&"--noconfirm".to_string()) || args[0].ends_with(&"n".to_string())
}
pub fn argssort(args: &mut Vec<String>) -> &Vec<String> {
// sort the args
if args.contains(&"--noconfirm".to_string()) {
args.retain(|x| x != &"--noconfirm".to_string());
return args;
}
args
}

@ -0,0 +1,145 @@
use std::env;
use std::env::set_current_dir;
use std::fs::remove_dir_all;
use std::path::Path;
use std::process::{Command, Stdio};
use crate::{info, log, Options};
use crate::internal::{crash, prompt};
use crate::internal::rpc::rpcinfo;
pub fn aur_install(a: Vec<String>, options: Options) {
let url = crate::internal::rpc::URL;
let cachedir = format!("{}/.cache/ame/", env::var("HOME").unwrap());
let verbosity = options.verbosity;
let noconfirm = options.noconfirm;
if verbosity >= 1 {
log(format!("Installing from AUR: {:?}", &a));
}
info(format!("Installing packages {} from the AUR", a.join(", ")));
for package in a {
let rpcres = rpcinfo(package);
if !rpcres.found {
break;
}
let pkg = &rpcres.package.as_ref().unwrap().name;
if verbosity >= 1 {
log(format!("Cloning {} into cachedir", pkg));
}
info("Cloning package source".to_string());
set_current_dir(Path::new(&cachedir)).unwrap();
Command::new("git")
.arg("clone")
.arg(format!("{}/{}", url, pkg))
.stdout(Stdio::null())
.status()
.expect("Something has gone wrong");
if verbosity >= 1 {
log(format!(
"Cloned {} into cachedir, moving on to resolving dependencies",
pkg
));
log(format!(
"Raw dependencies for package {} are:\n{:?}",
pkg,
rpcres.package.as_ref().unwrap().depends.join(", ")
));
}
// dep sorting
info("Sorting dependencies".to_string());
let sorted = crate::internal::sort(&rpcres.package.as_ref().unwrap().depends, options);
if verbosity >= 1 {
log(format!(
"Sorted dependencies for {} are:\n{:?}",
pkg, &sorted
));
}
let newopts = Options {
verbosity,
noconfirm,
asdeps: true,
};
if !sorted.nf.is_empty() {
crash(
format!(
"Could not find dependencies {} for package {}, aborting",
sorted.nf.join(", "),
pkg
),
5,
);
}
if !noconfirm {
let p1 = prompt(
format!("Would you like to review {}'s PKGBUILD?", pkg),
false,
);
let editor = env::var("PAGER").unwrap_or_else(|_| "less".parse().unwrap());
if p1 {
Command::new(editor)
.arg(format!("{}/PKGBUILD", pkg))
.spawn()
.unwrap()
.wait()
.unwrap();
let p2 = prompt(format!("Would you still like to install {}?", pkg), true);
if !p2 {
crash("Not proceeding".to_string(), 6);
}
}
}
// dep installing
info("Moving on to install dependencies".to_string());
if !sorted.repo.is_empty() {
crate::operations::install(sorted.repo, newopts);
}
if !sorted.aur.is_empty() {
crate::operations::aur_install(sorted.aur, newopts);
}
let mut makepkg_args = vec!["-rsic", "--needed"];
if options.asdeps {
makepkg_args.push("--asdeps")
}
if options.noconfirm {
makepkg_args.push("--noconfirm")
}
// package building and installing
info("Building time!".to_string());
set_current_dir(format!("{}/{}", cachedir, pkg)).unwrap();
let out = Command::new("makepkg")
.args(&makepkg_args)
.status()
.expect("Something has gone wrong");
if out.code() != Some(0) {
crash(
format!("Error encountered while installing {}, aborting", pkg),
7,
);
}
set_current_dir(&cachedir).unwrap();
remove_dir_all(format!("{}/{}", cachedir, &pkg)).unwrap();
// pushes package to database
crate::database::add(rpcres.package.unwrap(), options);
}
}

@ -0,0 +1,38 @@
use crate::{crash, info, log, Options};
pub fn install(a: Vec<String>, options: Options) {
info(format!("Installing packages {} from repos", &a.join(", ")));
let mut opers = vec![];
if options.noconfirm {
opers.push("--noconfirm".to_string());
}
if options.asdeps {
opers.push("--asdeps".to_string());
}
let verbosity = options.verbosity;
if verbosity >= 1 {
log(format!("Installing from repos: {:?}", &a));
}
let r = runas::Command::new("pacman")
.arg("-S")
.arg("--needed")
.args(&a)
.args(&opers)
.status()
.expect("Something has gone wrong");
if r.code() != Some(0) {
crash(
format!(
"An error occured while installing packages: {}, aborting",
a.join(", ")
),
7,
);
}
if verbosity >= 1 {
log(format!("Installing packages: {:?} was successful", &a));
}
}

@ -0,0 +1,31 @@
use crate::Options;
mod aur_install;
mod install;
mod search;
mod uninstall;
mod upgrade;
pub fn install(a: Vec<String>, options: Options) {
install::install(a, options);
}
pub fn uninstall(a: Vec<String>, options: Options) {
uninstall::uninstall(a, options);
}
pub fn search(a: &str, options: Options) {
search::repo_search(a, options);
}
pub fn aur_install(a: Vec<String>, options: Options) {
aur_install::aur_install(a, options);
}
pub fn aur_search(a: &str, options: Options) {
search::aur_search(a, options);
}
pub fn upgrade(options: Options) {
upgrade::upgrade(options);
}

@ -0,0 +1,48 @@
use std::process::Command;
use crate::{log, Options};
use crate::internal::rpc::rpcsearch;
pub fn aur_search(a: &str, options: Options) {
let verbosity = options.verbosity;
let res = rpcsearch(a.to_string());
if verbosity >= 1 {
log(format!(
"Found {} resuls for \"{}\" in AUR",
res.resultcount, a
));
}
for r in &res.results {
println!(
"aur/{} {}\n {}",
r.name,
r.version,
r.description
.as_ref()
.unwrap_or(&"No description".to_string())
)
}
}
pub fn repo_search(a: &str, options: Options) {
let verbosity = options.verbosity;
let rs = Command::new("pacman")
.arg("-Ss")
.arg(format!("^{}$", &a))
.output()
.expect("Something has gone wrong");
let str = String::from_utf8(rs.stdout).unwrap();
if verbosity >= 1 {
log(format!(
"Found {} results for \"{}\" in repos",
&str.split('\n').count() / 2,
&a
));
}
print!("{}", str);
}

@ -0,0 +1,45 @@
use std::{env, fs};
use std::path::Path;
use crate::{log, Options};
pub fn uninstall(mut a: Vec<String>, options: Options) {
let b = a.clone();
if options.noconfirm {
a.push("--noconfirm".to_string());
}
let verbosity = options.verbosity;
if verbosity >= 1 {
log(format!("Uninstalling: {:?}", &b));
}
let r = runas::Command::new("pacman")
.arg("-Rs")
.args(&a)
.status()
.expect("Something has gone wrong");
if let Some(x) = r.code() {
if verbosity >= 1 {
log(format!(
"Uninstalling packages: {:?} exited with code {}",
&b, x
));
}
}
for b in a {
crate::database::remove(&b, options);
if Path::new(&format!("{}/.cache/ame/{}", env::var("HOME").unwrap(), b)).exists() {
if verbosity >= 1 {
log("Old cache directory found, deleting".to_string());
}
fs::remove_dir_all(Path::new(&format!(
"{}/.cache/ame/{}",
env::var("HOME").unwrap(),
b
)))
.unwrap();
}
}
}

@ -0,0 +1,49 @@
use runas::Command;
use crate::{info, log, Options};
use crate::internal::rpc::rpcinfo;
use crate::operations::aur_install::aur_install;
pub fn upgrade(options: Options) {
let verbosity = options.verbosity;
let noconfirm = options.noconfirm;
let mut pacman_args = vec!["-Syu"];
if noconfirm {
pacman_args.push("--noconfirm");
}
if verbosity >= 1 {
log("Upgrading repo packages".to_string());
}
Command::new("pacman")
.args(&pacman_args)
.status()
.expect("Something has gone wrong");
if verbosity >= 1 {
log("Upgrading AUR packages".to_string());
}
let res = crate::database::query(options);
if verbosity >= 1 {
log(format!("{:?}", &res));
}
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);
}
}
if !aur_upgrades.is_empty() {
aur_install(aur_upgrades, options);
} else {
info("No upgrades available for installed AUR packages".to_string());
}
}
Loading…
Cancel
Save