#![doc = doc_self!()]
use std::{io::Write, sync::LazyLock};
use async_trait::async_trait;
use futures::prelude::*;
use indoc::indoc;
use tap::Pipe;
use super::{Pm, PmHelper, PmMode, PromptStrategy, Strategy};
use crate::{
config::Config,
error::{Error, Result},
exec::{Cmd, StatusCode},
print::println_err,
};
macro_rules! doc_self {
() => {
indoc! {"
The [X Binary Package System](https://github.com/void-linux/xbps).
"}
};
}
use doc_self;
#[doc = doc_self!()]
#[derive(Debug)]
pub struct Xbps {
cfg: Config,
}
const PKG_NOT_FOUND_CODE: StatusCode = 2;
static STRAT_PROMPT: LazyLock<Strategy> = LazyLock::new(|| Strategy {
prompt: PromptStrategy::native_no_confirm(["--yes"]),
..Strategy::default()
});
impl Xbps {
#[must_use]
#[allow(missing_docs)]
pub const fn new(cfg: Config) -> Self {
Self { cfg }
}
}
#[async_trait]
impl Pm for Xbps {
fn name(&self) -> &'static str {
"xbps"
}
fn cfg(&self) -> &crate::config::Config {
&self.cfg
}
async fn q(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
if kws.is_empty() {
return self
.run(Cmd::new(["xbps-query", "-l"]).kws(kws).flags(flags))
.await;
}
let lines: Vec<_> = stream::iter(kws)
.map(Ok)
.and_then(|&pkg| async {
let cmd = Cmd::new(["xbps-query", "--property", "pkgver", pkg]).flags(flags);
match self
.check_output(cmd, PmMode::Mute, &Strategy::default())
.await
{
Ok(line) => Ok(Ok(line)),
Err(Error::CmdStatusCodeError {
code: PKG_NOT_FOUND_CODE,
output,
}) => Ok(Err((pkg.to_owned(), output))),
Err(e) => Err(e),
}
})
.try_collect()
.await?;
let mut stdout = std::io::stdout();
lines.into_iter().try_for_each(|line| match line {
Ok(line) => stdout.write_all(&line).map_err(Into::into),
Err((missing, output)) => {
println_err(format_args!("package `{missing}` was not found"));
Err(Error::CmdStatusCodeError {
code: PKG_NOT_FOUND_CODE,
output,
})
}
})
}
async fn qe(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
if kws.is_empty() {
return self
.run(Cmd::new(["xbps-query", "-m"]).kws(kws).flags(flags))
.await;
}
let lines: Vec<Result<Vec<u8>, String>> = stream::iter(kws)
.filter(|pkg| async {
let check_cmd =
Cmd::new(["xbps-query", "--property", "automatic-install", pkg]).flags(flags);
self.check_output(check_cmd, PmMode::Mute, &Strategy::default())
.await
.map_or(true, |auto_stat| auto_stat.is_empty())
})
.map(Ok)
.and_then(|&pkg| async {
let cmd = Cmd::new(["xbps-query", "--property", "pkgver", pkg]).flags(flags);
match self
.check_output(cmd, PmMode::Mute, &Strategy::default())
.await
{
Ok(line) => Ok(Ok(line)),
Err(Error::CmdStatusCodeError {
code: PKG_NOT_FOUND_CODE,
..
}) => Ok(Err(pkg.to_owned())),
Err(e) => Err(e),
}
})
.try_collect()
.await?;
let mut stdout = std::io::stdout();
lines.into_iter().try_fold(Ok(()), |acc, line| {
std::io::Result::Ok(match line {
Ok(line) => {
stdout.write_all(&line)?;
acc
}
Err(missing) => {
println_err(format_args!("package `{missing}` was not found"));
Err(Error::CmdStatusCodeError {
code: PKG_NOT_FOUND_CODE,
output: vec![],
})
}
})
})?
}
async fn qi(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
self.run(Cmd::new(["xbps-query", "-S"]).kws(kws).flags(flags))
.await
}
async fn qii(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
self.run(Cmd::new(["xbps-query", "-X"]).kws(kws).flags(flags))
.await
}
async fn ql(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
self.run(Cmd::new(["xbps-query", "-f"]).kws(kws).flags(flags))
.await
}
async fn qs(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
self.run(Cmd::new(["xbps-query", "-s"]).kws(kws).flags(flags))
.await
}
async fn r(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
Cmd::with_sudo(["xbps-remove"])
.kws(kws)
.flags(flags)
.pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
.await
}
async fn rs(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
Cmd::with_sudo(["xbps-remove", "-R"])
.kws(kws)
.flags(flags)
.pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
.await
}
async fn s(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
Cmd::with_sudo(["xbps-install"])
.kws(kws)
.flags(flags)
.pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
.await
}
async fn sc(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
Cmd::with_sudo(["xbps-remove", "-O"])
.kws(kws)
.flags(flags)
.pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
.await
}
async fn si(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
self.run(Cmd::new(["xbps-query", "-RS"]).kws(kws).flags(flags))
.await
}
async fn sii(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
self.run(Cmd::new(["xbps-query", "-RX"]).kws(kws).flags(flags))
.await
}
async fn ss(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
self.run(Cmd::new(["xbps-query", "-Rs"]).kws(kws).flags(flags))
.await
}
async fn suy(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
Cmd::with_sudo(["xbps-install", "-Su"])
.kws(kws)
.flags(flags)
.pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
.await
}
async fn sy(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
self.run(Cmd::new(["xbps-install", "-S"]).kws(kws).flags(flags))
.await
}
async fn sw(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
Cmd::with_sudo(["xbps-install", "-D"])
.kws(kws)
.flags(flags)
.pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
.await
}
async fn su(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
Cmd::with_sudo(["xbps-install", "-u"])
.kws(kws)
.flags(flags)
.pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
.await
}
}