2025-03-10 19:31:06 -05:00
|
|
|
//! An alternate client for the arch linux package repositories. Stores files in a
|
|
|
|
//! separate directory from everything else and keeps everything hashed.
|
2025-03-09 20:26:05 -05:00
|
|
|
#![warn(
|
|
|
|
missing_docs,
|
|
|
|
clippy::missing_docs_in_private_items,
|
|
|
|
clippy::empty_docs
|
|
|
|
)]
|
|
|
|
|
2025-03-10 19:31:06 -05:00
|
|
|
use clap::Parser;
|
|
|
|
|
|
|
|
/// Parser for command line arguments
|
|
|
|
#[derive(Parser)]
|
|
|
|
#[command(version, about, long_about = None, name = "pacwoman")]
|
|
|
|
struct Cli {
|
|
|
|
/// Whether to operate as a user or not. In most cases, running as system will
|
|
|
|
/// require running as root.
|
|
|
|
#[arg(short, long, default_value_t = false)]
|
|
|
|
user: bool,
|
|
|
|
/// Initalize the GPG keyring and create skeleton directories. This will also add
|
|
|
|
/// a default configuration for the mirrors.
|
|
|
|
#[arg(long, default_value_t = false)]
|
|
|
|
initalize: bool,
|
|
|
|
/// Download one or more packages. This WILL NOT install them to your bin directory!
|
|
|
|
#[arg(id = "packages to download", short = 'D')]
|
|
|
|
download: Vec<String>,
|
|
|
|
/// Download and install one or more packages. This WILL NOT update the repositories if needed!
|
|
|
|
#[arg(id = "packages to sync", short = 'S')]
|
|
|
|
sync: Vec<String>,
|
|
|
|
/// Remove a package. Because I'm lazy, this will only remove the store entry, leaving a bunch
|
|
|
|
/// of dangling symlinks.
|
|
|
|
#[arg(id = "packages to remove", short = 'R')]
|
|
|
|
remove: Vec<String>,
|
|
|
|
/// Sync repository indexes from mirrors.
|
|
|
|
#[arg(id = "repositories to sync", short = 'Y')]
|
|
|
|
sync_repos: Vec<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Default mirrorlist configuration.
|
|
|
|
const DEFAULT_MIRRORLIST: &[u8] = include_bytes!("default_mirrorlist");
|
|
|
|
|
2025-03-10 12:30:21 -05:00
|
|
|
fn main() -> std::io::Result<()> {
|
2025-03-10 19:31:06 -05:00
|
|
|
let args = Cli::parse();
|
|
|
|
|
|
|
|
if args.initalize {
|
|
|
|
pacwoman::create_directories(!args.user)?;
|
|
|
|
pacwoman::init_gpg()?;
|
|
|
|
|
|
|
|
std::fs::write(
|
|
|
|
pacwoman::config_directory(!args.user)?.join("mirrors"),
|
|
|
|
DEFAULT_MIRRORLIST,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let regex = regex::Regex::new(r"(?m)#.*$").unwrap();
|
|
|
|
|
|
|
|
let mirrors: Vec<pacwoman::Mirror> = regex
|
|
|
|
.replace_all(
|
|
|
|
&(std::fs::read_to_string(pacwoman::config_directory(!args.user)?.join("mirrors"))?),
|
|
|
|
"",
|
|
|
|
)
|
|
|
|
.split("\n")
|
|
|
|
.filter(|item| item.trim() != "")
|
|
|
|
.map(|item| pacwoman::Mirror::new(url::Url::parse(item).unwrap()))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
if !args.sync_repos.is_empty() {
|
|
|
|
for repo in &args.sync_repos {
|
|
|
|
let mut ok = false;
|
|
|
|
for mirror in &mirrors {
|
|
|
|
if pacwoman::populate_index(
|
|
|
|
mirror.clone(),
|
|
|
|
pacwoman::RepoDescriptor::new()
|
|
|
|
.set_repo(repo.clone())
|
|
|
|
.set_arch(std::env::consts::ARCH.to_string())
|
|
|
|
.clone(),
|
|
|
|
).is_ok() {
|
|
|
|
ok = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
println!(" [ERR] Cannot download repo {repo}!");
|
|
|
|
println!(" [TIP] Check the spelling and your mirrors.");
|
|
|
|
|
|
|
|
drop(mirrors);
|
|
|
|
drop(args);
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !args.download.is_empty() {
|
|
|
|
for package in &args.download {
|
|
|
|
let results = pacwoman::locate_package(package.clone())?;
|
|
|
|
if results.is_empty() {
|
|
|
|
println!(" [ERR] Cannot locate packages for {package}!");
|
|
|
|
println!(" [TIP] Try adding a repository to the index.");
|
|
|
|
|
|
|
|
drop(mirrors);
|
|
|
|
drop(args);
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
for result in results {
|
|
|
|
let mut ok = false;
|
|
|
|
for mirror in &mirrors {
|
|
|
|
if pacwoman::recieve_package_and_dependencies(
|
|
|
|
mirror.clone(),
|
|
|
|
result.1.clone(),
|
|
|
|
result.0.clone(),
|
|
|
|
!args.user,
|
|
|
|
)
|
|
|
|
.is_ok()
|
|
|
|
{
|
|
|
|
ok = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
println!(" [ERR] Cannot download package {}!", result.0);
|
|
|
|
println!(" [TIP] Check the spelling and try syncing repos.");
|
|
|
|
|
|
|
|
drop(mirrors);
|
|
|
|
drop(args);
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !args.sync.is_empty() {
|
|
|
|
for package in &args.sync {
|
|
|
|
let results = pacwoman::locate_package(package.clone())?;
|
|
|
|
if results.is_empty() {
|
|
|
|
println!(" [ERR] Cannot locate packages for {package}!");
|
|
|
|
println!(" [TIP] Try adding a repository to the index.");
|
|
|
|
|
|
|
|
drop(mirrors);
|
|
|
|
drop(args);
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
for result in results {
|
|
|
|
let mut ok = false;
|
|
|
|
for mirror in &mirrors {
|
|
|
|
if pacwoman::install_package(
|
|
|
|
mirror.clone(),
|
|
|
|
result.1.clone(),
|
|
|
|
result.0.clone(),
|
|
|
|
!args.user,
|
|
|
|
)
|
|
|
|
.is_ok()
|
|
|
|
{
|
|
|
|
ok = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
println!(" [ERR] Cannot sync package {}!", result.0);
|
|
|
|
println!(" [TIP] Check the spelling and try syncing repos.");
|
|
|
|
|
|
|
|
drop(mirrors);
|
|
|
|
drop(args);
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !args.remove.is_empty() {
|
|
|
|
for package in &args.remove {
|
|
|
|
let results = pacwoman::locate_package(package.clone())?;
|
|
|
|
if results.is_empty() {
|
|
|
|
println!(" [ERR] Cannot locate packages for {package}!");
|
|
|
|
println!(" [TIP] Try adding a repository to the index.");
|
|
|
|
|
|
|
|
drop(mirrors);
|
|
|
|
drop(args);
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
for result in results {
|
|
|
|
pacwoman::remove_package(result.0, !args.user, result.1)?;
|
|
|
|
}
|
|
|
|
}
|
2025-03-10 12:30:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2025-03-09 20:26:05 -05:00
|
|
|
}
|