bare-script 0.1.1

The type-safe scripting authority for Rust. A framework for building robust shell commands and automation with 'Parse, don't validate' philosophy.
Documentation
//! Asynchronous command execution module.
//!
//! This module provides a type-safe interface for executing shell commands
//! using Tokio's `tokio::process::Command`.

use std::ffi::OsStr;
use std::process::Stdio;

use tokio::process::Command;

use crate::error::{ScriptError, ScriptResult};
use crate::output::Output;

/// Command builder for asynchronous execution.
#[derive(Debug)]
pub struct CommandBuilder {
    cmd: Command,
}

impl CommandBuilder {
    /// Creates a new command builder for the specified program.
    pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
        Self {
            cmd: Command::new(program),
        }
    }

    /// Adds a single argument to the command.
    pub fn arg<S: AsRef<OsStr>>(mut self, arg: S) -> Self {
        let _ = self.cmd.arg(arg);
        self
    }

    /// Adds multiple arguments to the command.
    pub fn args<I, S>(mut self, args: I) -> Self
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        let _ = self.cmd.args(args);
        self
    }

    /// Sets an environment variable for the command.
    pub fn env<K, V>(mut self, key: K, value: V) -> Self
    where
        K: AsRef<OsStr>,
        V: AsRef<OsStr>,
    {
        let _ = self.cmd.env(key, value);
        self
    }

    /// Sets multiple environment variables for the command.
    pub fn envs<I, K, V>(mut self, vars: I) -> Self
    where
        I: IntoIterator<Item = (K, V)>,
        K: AsRef<OsStr>,
        V: AsRef<OsStr>,
    {
        let _ = self.cmd.envs(vars);
        self
    }

    /// Clears all environment variables inherited from the parent process.
    pub fn env_clear(mut self) -> Self {
        let _ = self.cmd.env_clear();
        self
    }

    /// Removes an environment variable from the command's environment.
    pub fn env_remove<K>(mut self, key: K) -> Self
    where
        K: AsRef<OsStr>,
    {
        let _ = self.cmd.env_remove(key);
        self
    }

    /// Sets the working directory for the command.
    pub fn current_dir<D>(mut self, dir: D) -> Self
    where
        D: AsRef<std::path::Path>,
    {
        let _ = self.cmd.current_dir(dir);
        self
    }

    /// Configures the stdin handle for the command.
    pub fn stdin<T: Into<Stdio>>(mut self, stdin: T) -> Self {
        let _ = self.cmd.stdin(stdin);
        self
    }

    /// Configures the stdout handle for the command.
    pub fn stdout<T: Into<Stdio>>(mut self, stdout: T) -> Self {
        let _ = self.cmd.stdout(stdout);
        self
    }

    /// Configures the stderr handle for the command.
    pub fn stderr<T: Into<Stdio>>(mut self, stderr: T) -> Self {
        let _ = self.cmd.stderr(stderr);
        self
    }

    /// Configures the command to capture both stdout and stderr.
    pub fn capture_output(mut self) -> Self {
        let _ = self.cmd.stdout(Stdio::piped());
        let _ = self.cmd.stderr(Stdio::piped());
        self
    }

    /// Configures the command to inherit stdin from the parent process.
    pub fn inherit_stdin(mut self) -> Self {
        let _ = self.cmd.stdin(Stdio::inherit());
        self
    }

    /// Configures the command to inherit stdout from the parent process.
    pub fn inherit_stdout(mut self) -> Self {
        let _ = self.cmd.stdout(Stdio::inherit());
        self
    }

    /// Configures the command to inherit stderr from the parent process.
    pub fn inherit_stderr(mut self) -> Self {
        let _ = self.cmd.stderr(Stdio::inherit());
        self
    }

    /// Configures the command to read from null stdin.
    pub fn null_stdin(mut self) -> Self {
        let _ = self.cmd.stdin(Stdio::null());
        self
    }

    /// Configures the command to write to null stdout.
    pub fn null_stdout(mut self) -> Self {
        let _ = self.cmd.stdout(Stdio::null());
        self
    }

    /// Configures the command to write to null stderr.
    pub fn null_stderr(mut self) -> Self {
        let _ = self.cmd.stderr(Stdio::null());
        self
    }

    /// Sets whether to kill the child process when the handle is dropped.
    pub fn kill_on_drop(mut self, kill: bool) -> Self {
        let _ = self.cmd.kill_on_drop(kill);
        self
    }

    /// Spawns the command as a child process.
    pub async fn spawn(&mut self) -> std::io::Result<tokio::process::Child> {
        self.cmd.spawn()
    }

    /// Executes the command and captures its output.
    pub async fn output(&mut self) -> std::io::Result<Output> {
        let output = self.cmd.output().await?;
        Ok(Output::new(output.stdout, output.stderr, output.status))
    }

    /// Executes the command and captures its output, returning a ScriptResult.
    pub async fn execute(&mut self) -> ScriptResult<Output> {
        self.output().await.map_err(ScriptError::IoError)
    }

    /// Executes the command with a timeout.
    pub async fn execute_with_timeout(
        mut self,
        timeout: std::time::Duration,
    ) -> ScriptResult<Output> {
        use tokio::time;

        match time::timeout(timeout, self.cmd.output()).await {
            Ok(Ok(output)) => Ok(Output::new(output.stdout, output.stderr, output.status)),
            Ok(Err(e)) => Err(ScriptError::IoError(e)),
            Err(_) => Err(ScriptError::Timeout(timeout)),
        }
    }

    /// Waits for the command to complete and returns its exit status.
    pub async fn status(&mut self) -> std::io::Result<std::process::ExitStatus> {
        self.cmd.status().await
    }
}