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
//! Configuration for command execution.
//!
//! This module provides configuration types for command execution.

use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;

fn from_env_vars() -> HashMap<OsString, OsString> {
    std::env::vars()
        .map(|(k, v)| (k.into(), v.into()))
        .collect()
}

/// Configuration for command execution.
#[derive(Debug, Clone, Default)]
pub struct Config {
    /// Default environment variables.
    pub env: HashMap<OsString, OsString>,
    /// Default working directory.
    pub cwd: Option<PathBuf>,
    /// Default stdin configuration.
    pub stdin: Option<StdioConfig>,
    /// Default stdout configuration.
    pub stdout: Option<StdioConfig>,
    /// Default stderr configuration.
    pub stderr: Option<StdioConfig>,
}

/// Standard I/O configuration.
#[derive(Debug, Clone, Copy, Default)]
pub enum StdioConfig {
    /// Inherit from parent.
    #[default]
    Inherit,
    /// Pipe (capture).
    Piped,
    /// Null/Discard.
    Null,
    /// Specific file descriptor.
    Fd(u32),
}

impl Config {
    /// Creates a new empty configuration.
    pub fn new() -> Self {
        Self::default()
    }

    /// Creates a configuration from environment variables.
    ///
    /// # Errors
    ///
    /// Returns an error if the working directory is invalid.
    pub fn from_env() -> std::io::Result<Self> {
        let cwd = std::env::current_dir()?;
        let env = from_env_vars();

        Ok(Self {
            env,
            cwd: Some(cwd),
            stdin: None,
            stdout: None,
            stderr: None,
        })
    }

    /// Sets an environment variable.
    #[must_use]
    pub fn env<K, V>(mut self, key: K, value: V) -> Self
    where
        K: AsRef<OsStr>,
        V: AsRef<OsStr>,
    {
        drop(
            self.env
                .insert(key.as_ref().to_os_string(), value.as_ref().to_os_string()),
        );
        self
    }

    /// Sets the working directory.
    #[must_use]
    pub fn cwd<D: Into<PathBuf>>(mut self, dir: D) -> Self {
        self.cwd = Some(dir.into());
        self
    }

    /// Sets stdin configuration.
    #[must_use]
    pub fn stdin(mut self, config: StdioConfig) -> Self {
        self.stdin = Some(config);
        self
    }

    /// Sets stdout configuration.
    #[must_use]
    pub fn stdout(mut self, config: StdioConfig) -> Self {
        self.stdout = Some(config);
        self
    }

    /// Sets stderr configuration.
    #[must_use]
    pub fn stderr(mut self, config: StdioConfig) -> Self {
        self.stderr = Some(config);
        self
    }
}

/// Extension trait for applying configuration to CommandBuilder.
pub trait ConfigExt {
    /// Applies this configuration to a command builder.
    fn with_config(self, config: &Config) -> Self;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_config_new() {
        let config = Config::new();
        assert!(config.env.is_empty());
        assert!(config.cwd.is_none());
    }

    #[test]
    fn test_config_env() {
        let config = Config::new().env("KEY", "value").env("OTHER", "123");

        let key = OsString::from("KEY");
        assert_eq!(
            config.env.get(&key).map(|s| s.to_str()),
            Some(Some("value"))
        );
    }

    #[test]
    fn test_config_cwd() {
        let config = Config::new().cwd("/tmp");
        assert!(config.cwd.is_some());
    }
}