doot/crates/doot-core/src/package/pacman.rs
2026-02-05 22:35:09 -06:00

154 lines
3.7 KiB
Rust

use super::{PackageError, PackageManager};
use std::io::Write;
use std::process::{Command, Stdio};
pub struct Pacman {
dry_run: bool,
use_sudo: bool,
}
impl Pacman {
pub fn new() -> Self {
Self {
dry_run: false,
use_sudo: true,
}
}
pub fn dry_run(mut self, dry_run: bool) -> Self {
self.dry_run = dry_run;
self
}
pub fn use_sudo(mut self, use_sudo: bool) -> Self {
self.use_sudo = use_sudo;
self
}
fn run_pacman(&self, args: &[&str]) -> Result<(), PackageError> {
if self.dry_run {
let prefix = if self.use_sudo { "sudo " } else { "" };
println!("[dry-run] {}pacman {}", prefix, args.join(" "));
return Ok(());
}
let output = if self.use_sudo {
Command::new("sudo").arg("pacman").args(args).output()?
} else {
Command::new("pacman").args(args).output()?
};
if !output.status.success() {
return Err(PackageError::InstallFailed {
package: args.join(" "),
message: String::from_utf8_lossy(&output.stderr).to_string(),
});
}
Ok(())
}
fn run_pacman_with_password(&self, args: &[&str], password: &str) -> Result<(), PackageError> {
if self.dry_run {
println!("[dry-run] sudo pacman {}", args.join(" "));
return Ok(());
}
let mut child = Command::new("sudo")
.arg("-S")
.arg("pacman")
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
if let Some(mut stdin) = child.stdin.take() {
writeln!(stdin, "{}", password).ok();
}
let output = child.wait_with_output()?;
if !output.status.success() {
return Err(PackageError::InstallFailed {
package: args.join(" "),
message: String::from_utf8_lossy(&output.stderr).to_string(),
});
}
Ok(())
}
}
impl PackageManager for Pacman {
fn name(&self) -> &'static str {
"pacman"
}
fn is_available(&self) -> bool {
std::path::Path::new("/usr/bin/pacman").exists()
}
fn needs_sudo(&self) -> bool {
self.use_sudo
}
fn install(&self, packages: &[String]) -> Result<(), PackageError> {
if packages.is_empty() {
return Ok(());
}
let mut args = vec!["-S", "--noconfirm"];
for pkg in packages {
args.push(pkg);
}
self.run_pacman(&args)
}
fn install_with_sudo(&self, packages: &[String], password: &str) -> Result<(), PackageError> {
if packages.is_empty() {
return Ok(());
}
let mut args = vec!["-S", "--noconfirm"];
for pkg in packages {
args.push(pkg);
}
self.run_pacman_with_password(&args, password)
}
fn uninstall(&self, packages: &[String]) -> Result<(), PackageError> {
if packages.is_empty() {
return Ok(());
}
let mut args = vec!["-R", "--noconfirm"];
for pkg in packages {
args.push(pkg);
}
self.run_pacman(&args)
}
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
let output = Command::new("pacman").args(["-Q", package]).output()?;
Ok(output.status.success())
}
fn update(&self) -> Result<(), PackageError> {
self.run_pacman(&["-Sy"])
}
fn upgrade(&self) -> Result<(), PackageError> {
self.run_pacman(&["-Syu", "--noconfirm"])
}
}
impl Default for Pacman {
fn default() -> Self {
Self::new()
}
}