use std::process::Command;
use crate::spawn::{
spawn as free_spawn, spawn_daemon as free_spawn_daemon, DaemonChild, SpawnStdio, SpawnedChild,
};
pub const ORIGINATOR_ENV_VAR: &str = "RUNNING_PROCESS_ORIGINATOR";
pub struct ContainedProcessGroup {
originator: Option<String>,
}
fn format_originator_value(tool: &str) -> String {
format!("{}:{}", tool, std::process::id())
}
impl ContainedProcessGroup {
pub fn new() -> Result<Self, std::io::Error> {
Ok(Self { originator: None })
}
pub fn with_originator(originator: &str) -> Result<Self, std::io::Error> {
Ok(Self {
originator: Some(originator.to_string()),
})
}
pub fn originator(&self) -> Option<&str> {
self.originator.as_deref()
}
pub fn originator_value(&self) -> Option<String> {
self.originator.as_ref().map(|o| format_originator_value(o))
}
fn inject_originator_env(&self, command: &mut Command) {
if let Some(ref originator) = self.originator {
command.env(ORIGINATOR_ENV_VAR, format_originator_value(originator));
} else {
command.env_remove(ORIGINATOR_ENV_VAR);
}
}
pub fn spawn(
&self,
command: &mut Command,
stdio: SpawnStdio<'_>,
) -> Result<SpawnedChild, std::io::Error> {
self.inject_originator_env(command);
free_spawn(command, stdio)
}
pub fn spawn_daemon(&self, command: &mut Command) -> Result<DaemonChild, std::io::Error> {
self.inject_originator_env(command);
free_spawn_daemon(command)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contained_process_group_creates_successfully() {
let group = ContainedProcessGroup::new();
assert!(group.is_ok());
}
#[test]
fn with_originator_creates_successfully() {
let group = ContainedProcessGroup::with_originator("CLUD");
assert!(group.is_ok());
let group = group.unwrap();
assert_eq!(group.originator(), Some("CLUD"));
}
#[test]
fn originator_value_format() {
let group = ContainedProcessGroup::with_originator("CLUD").unwrap();
let value = group.originator_value().unwrap();
let expected = format!("CLUD:{}", std::process::id());
assert_eq!(value, expected);
}
#[test]
fn no_originator_returns_none() {
let group = ContainedProcessGroup::new().unwrap();
assert!(group.originator().is_none());
assert!(group.originator_value().is_none());
}
#[test]
fn format_originator_value_correct() {
let value = format_originator_value("JUPYTER");
let parts: Vec<&str> = value.splitn(2, ':').collect();
assert_eq!(parts.len(), 2);
assert_eq!(parts[0], "JUPYTER");
assert_eq!(parts[1], std::process::id().to_string());
}
}