use std::ffi::OsString;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Environment {
vars: Vec<(OsString, OsString)>,
inherit: bool,
}
impl Default for Environment {
fn default() -> Self {
Self {
vars: vec![],
inherit: false,
}
}
}
impl Environment {
pub fn inherit() -> Self {
Self {
vars: vec![],
inherit: true,
}
}
pub fn empty() -> Self {
Self::default()
}
pub fn insert<S1: Into<OsString>, S2: Into<OsString>>(mut self, key: S1, val: S2) -> Self {
self.vars.push((key.into(), val.into()));
self
}
pub fn compile(self) -> Vec<(OsString, OsString)> {
if self.inherit {
::std::env::vars_os().chain(self.vars).collect()
} else {
self.vars
}
}
}
impl<'a> From<&'a Environment> for Environment {
fn from(v: &'a Environment) -> Self {
v.clone()
}
}
pub trait EnvironmentItem {
fn to_environment_tuple(&self) -> (OsString, OsString);
}
impl<T: ToString, Z: ToString> EnvironmentItem for (T, Z) {
fn to_environment_tuple(&self) -> (OsString, OsString) {
(
OsString::from(self.0.to_string()),
OsString::from(self.1.to_string()),
)
}
}
impl<'s, T: ToString, Z: ToString> EnvironmentItem for &'s (T, Z) {
fn to_environment_tuple(&self) -> (OsString, OsString) {
(
OsString::from(self.0.to_string()),
OsString::from(self.1.to_string()),
)
}
}
impl<'s, T> From<T> for Environment
where
T: IntoIterator,
T::Item: EnvironmentItem,
{
fn from(v: T) -> Self {
Self {
vars: v.into_iter().map(|k| k.to_environment_tuple()).collect(),
inherit: false,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::process::Command;
#[test]
fn take_ownership() {
let x = Environment::inherit();
let mut c = Command::new("printenv");
c.env_clear().envs(x.clone().compile()).envs(x.compile());
}
#[test]
fn in_place_mod() {
let y = Environment::empty();
let y = y.insert("key", "value");
assert_eq!(
y.compile(),
vec![(OsString::from("key"), OsString::from("value"))]
);
}
#[test]
fn in_place_mod2() {
let x = Environment::inherit();
let mut c = Command::new("printenv");
let output = c.env_clear()
.envs(x.insert("key", "value").insert("key", "vv").compile())
.output()
.expect("failed to execute command");
let output = String::from_utf8_lossy(&output.stdout);
assert!(output.contains("key=vv"));
}
#[test]
fn in_place_mod3() {
let y = Environment::empty();
assert_eq!(
y.clone().insert("key", "value").compile(),
vec![(OsString::from("key"), OsString::from("value"))]
);
let mut c = Command::new("printenv");
let output = c.env_clear()
.envs(y.compile())
.output()
.expect("failed to execute command");
let output = String::from_utf8_lossy(&output.stdout);
assert_eq!(output, "");
}
#[test]
fn empty_env() {
let y = Environment::empty();
let mut c = Command::new("printenv");
let output = c.env_clear()
.envs(y.compile())
.output()
.expect("failed to execute command");
let output = String::from_utf8_lossy(&output.stdout);
assert!(output.is_empty());
}
#[test]
fn take_vec() {
let v = vec![("bar", "baz")];
let e: Environment = v.into();
let mut c = Command::new("printenv");
let output = c.env_clear()
.envs(e.clone().compile())
.output()
.expect("failed to execute command");
let output = String::from_utf8_lossy(&output.stdout);
assert!(output.contains("bar=baz"));
let mut c = Command::new("printenv");
let output = c.env_clear()
.envs(e.clone().insert("bar", "vv").compile())
.output()
.expect("failed to execute command");
let output = String::from_utf8_lossy(&output.stdout);
assert!(output.contains("bar=vv"));
assert!(!output.contains("bar=baz"));
}
}