ahab/src/command_builder.rs

102 lines
2.3 KiB
Rust

use anyhow::{anyhow, Context, Result};
use std::{
collections::VecDeque,
env,
ffi::OsStr,
fmt::Display,
fs::{File, OpenOptions},
os::unix::process::CommandExt,
path::PathBuf,
process::{Child, Command, Stdio},
};
use crate::debug_eprintln;
pub struct Args(Vec<String>);
impl From<&str> for Args {
fn from(value: &str) -> Self {
Self(Vec::from_iter(value.split_whitespace().map(String::from)))
}
}
impl From<&String> for Args {
fn from(value: &String) -> Self {
Self(Vec::from_iter(value.split_whitespace().map(String::from)))
}
}
impl From<&[String]> for Args {
fn from(value: &[String]) -> Self {
Self(value.to_vec())
}
}
fn get_compose_file() -> Result<String> {
let cf = env::var("COMPOSE_FILE")?;
Ok(cf)
}
#[derive(Default)]
pub struct CommandBuilder {
args: Vec<String>,
}
impl Display for CommandBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.args.join(" "))
}
}
impl CommandBuilder {
pub fn new(args: &str) -> Self {
Self::default().args("docker")
}
pub fn docker() -> Self {
Self::default().args("docker")
}
pub fn docker_compose() -> Self {
let cf =
get_compose_file().unwrap_or_else(|_| "docker/local/docker-compose.yaml".to_string());
Self::default().args("docker compose -f").args(&cf)
}
pub fn args<T>(mut self, args: T) -> Self
where
Args: From<T>,
{
self.args.extend(Args::from(args).0);
self
}
pub fn build(self) -> Result<Command> {
debug_eprintln!("running `{self}`");
let (first, rest) = self.args.split_first().context("empty args")?;
let mut command = Command::new(first);
command.args(rest);
Ok(command)
}
pub fn exec_get_stdout(mut self) -> Result<String> {
Ok(String::from_utf8(self.build()?.output()?.stdout)?)
}
pub fn exec(mut self) -> Result<()> {
self.build()?.spawn()?.wait()?;
Ok(())
}
pub fn spawn(mut self) -> Result<(Child)> {
Ok(self.build()?.spawn()?)
}
pub fn exec_redirect_stdout(mut self, stdio: Stdio) -> Result<()> {
self.build()?.stdout(stdio).spawn()?.wait()?;
Ok(())
}
}