arcature-cli 2026.2.0

Developer lifecycle CLI for Arcature applications.
Documentation
use std::ffi::OsString;
use std::path::{Path, PathBuf};

#[derive(Debug, Clone)]
pub(crate) struct ProcessSpec {
    program: OsString,
    arguments: Vec<OsString>,
    directory: PathBuf,
    environment: Vec<(OsString, OsString)>,
    /// When true the child is spawned in its own Windows process group
    /// (`CREATE_NEW_PROCESS_GROUP`). This isolates it from console control
    /// events (Ctrl+C / Ctrl+Break) targeted at the supervisor's process
    /// group, so an intentional `arc dev` shutdown does not propagate the
    /// signal to supervised children. On non-Windows the flag is ignored.
    new_process_group: bool,
}

impl ProcessSpec {
    pub(crate) fn new(program: impl Into<OsString>, directory: impl Into<PathBuf>) -> Self {
        Self {
            program: program.into(),
            arguments: Vec::new(),
            directory: directory.into(),
            environment: Vec::new(),
            new_process_group: false,
        }
    }
    pub(crate) fn arg(mut self, value: impl Into<OsString>) -> Self {
        self.arguments.push(value.into());
        self
    }
    pub(crate) fn args<I, T>(mut self, values: I) -> Self
    where
        I: IntoIterator<Item = T>,
        T: Into<OsString>,
    {
        self.arguments.extend(values.into_iter().map(Into::into));
        self
    }
    pub(crate) fn env(mut self, key: impl Into<OsString>, value: impl Into<OsString>) -> Self {
        self.environment.push((key.into(), value.into()));
        self
    }
    /// Place the child in its own process group on Windows so console
    /// control events sent to the supervisor's group do not reach it.
    pub(crate) fn new_process_group(mut self, enabled: bool) -> Self {
        self.new_process_group = enabled;
        self
    }
    pub(crate) fn program(&self) -> &std::ffi::OsStr {
        &self.program
    }
    pub(crate) fn arguments(&self) -> &[OsString] {
        &self.arguments
    }
    pub(crate) fn directory(&self) -> &Path {
        &self.directory
    }
    pub(crate) fn environment(&self) -> &[(OsString, OsString)] {
        &self.environment
    }
    /// Only read on Windows by the spawn path; dead code elsewhere.
    #[cfg_attr(not(windows), allow(dead_code))]
    pub(crate) fn new_process_group_enabled(&self) -> bool {
        self.new_process_group
    }
}