use std::{
io,
path::{Path, PathBuf},
process::Output,
process::Stdio,
sync::atomic::{AtomicBool, Ordering},
};
use color_eyre::eyre;
use smol::{process::Command, unblock};
pub(crate) async fn which(name: &'static str) -> Result<PathBuf, which::Error> {
unblock(move || which::which(name)).await
}
static STD_OUTPUT: AtomicBool = AtomicBool::new(false);
pub fn set_std_output(enabled: bool) {
STD_OUTPUT.store(enabled, std::sync::atomic::Ordering::SeqCst);
}
pub(crate) fn command(command: &mut Command) -> &mut Command {
command
.kill_on_drop(true)
.stdout(if STD_OUTPUT.load(Ordering::SeqCst) {
Stdio::inherit()
} else {
Stdio::piped()
})
.stderr(if STD_OUTPUT.load(Ordering::SeqCst) {
Stdio::inherit()
} else {
Stdio::piped()
})
}
pub(crate) async fn run_command_output(
name: &str,
args: impl IntoIterator<Item = &str>,
) -> eyre::Result<Output> {
let result = Command::new(name)
.args(args)
.kill_on_drop(true)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.await?;
if STD_OUTPUT.load(Ordering::SeqCst) {
use std::io::Write;
let _ = std::io::stdout().write_all(&result.stdout);
let _ = std::io::stderr().write_all(&result.stderr);
}
Ok(result)
}
pub(crate) async fn run_command(
name: &str,
args: impl IntoIterator<Item = &str>,
) -> eyre::Result<String> {
let result = run_command_output(name, args).await?;
if result.status.success() {
Ok(String::from_utf8_lossy(&result.stdout).to_string())
} else {
Err(eyre::eyre!(
"Command {} failed with status {}",
name,
result.status
))
}
}
pub(crate) fn parse_whitespace_separated_u32s(input: &str) -> Vec<u32> {
input
.split_whitespace()
.filter_map(|part| part.parse::<u32>().ok())
.collect()
}
pub async fn copy_file(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
let from = from.as_ref().to_path_buf();
let to = to.as_ref().to_path_buf();
unblock(move || reflink::reflink_or_copy(from, to).map(|_| ())).await
}
#[cfg(test)]
mod tests {
use super::parse_whitespace_separated_u32s;
#[test]
fn parses_pidof_output_with_multiple_pids() {
let parsed = parse_whitespace_separated_u32s("123 456\n");
assert_eq!(parsed, vec![123, 456]);
}
#[test]
fn ignores_non_numeric_tokens() {
let parsed = parse_whitespace_separated_u32s("foo 42 bar\n");
assert_eq!(parsed, vec![42]);
}
}