Case-insensitive environment keys.
This commit is contained in:
parent
88cb454b91
commit
ffa8b2917c
1 changed files with 75 additions and 7 deletions
|
@ -24,6 +24,9 @@ use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo};
|
||||||
use rt::rtio;
|
use rt::rtio;
|
||||||
use c_str::CString;
|
use c_str::CString;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::hash::sip::SipState;
|
||||||
|
use clone::Clone;
|
||||||
|
|
||||||
/// Signal a process to exit, without forcibly killing it. Corresponds to
|
/// Signal a process to exit, without forcibly killing it. Corresponds to
|
||||||
/// SIGTERM on unix platforms.
|
/// SIGTERM on unix platforms.
|
||||||
|
@ -78,8 +81,56 @@ pub struct Process {
|
||||||
pub extra_io: Vec<Option<io::PipeStream>>,
|
pub extra_io: Vec<Option<io::PipeStream>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A representation of environment variable name
|
||||||
|
/// It compares case-insensitive on Windows and case-sensitive everywhere else.
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
#[deriving(PartialEq, Eq, Hash, Clone, Show)]
|
||||||
|
struct EnvKey(CString);
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[deriving(Eq, Clone, Show)]
|
||||||
|
struct EnvKey(CString);
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
impl Hash for EnvKey {
|
||||||
|
fn hash(&self, state: &mut SipState) {
|
||||||
|
let &EnvKey(ref x) = self;
|
||||||
|
match x.as_str() {
|
||||||
|
Some(s) => for ch in s.chars() {
|
||||||
|
(ch as u8 as char).to_lowercase().hash(state);
|
||||||
|
},
|
||||||
|
None => x.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
impl PartialEq for EnvKey {
|
||||||
|
fn eq(&self, other: &EnvKey) -> bool {
|
||||||
|
let &EnvKey(ref x) = self;
|
||||||
|
let &EnvKey(ref y) = other;
|
||||||
|
match (x.as_str(), y.as_str()) {
|
||||||
|
(Some(xs), Some(ys)) => {
|
||||||
|
if xs.len() != ys.len() {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
for (xch, ych) in xs.chars().zip(ys.chars()) {
|
||||||
|
if xch.to_lowercase() != ych.to_lowercase() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// If either is not a valid utf8 string, just compare them byte-wise
|
||||||
|
_ => return x.eq(y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A HashMap representation of environment variables.
|
/// A HashMap representation of environment variables.
|
||||||
pub type EnvMap = HashMap<CString, CString>;
|
pub type EnvMap = HashMap<EnvKey, CString>;
|
||||||
|
|
||||||
/// The `Command` type acts as a process builder, providing fine-grained control
|
/// The `Command` type acts as a process builder, providing fine-grained control
|
||||||
/// over how a new process should be spawned. A default configuration can be
|
/// over how a new process should be spawned. A default configuration can be
|
||||||
|
@ -161,14 +212,14 @@ impl Command {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
// Get a mutable borrow of the environment variable map for this `Command`.
|
// Get a mutable borrow of the environment variable map for this `Command`.
|
||||||
fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap {
|
fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap {
|
||||||
match self.env {
|
match self.env {
|
||||||
Some(ref mut map) => map,
|
Some(ref mut map) => map,
|
||||||
None => {
|
None => {
|
||||||
// if the env is currently just inheriting from the parent's,
|
// if the env is currently just inheriting from the parent's,
|
||||||
// materialize the parent's env into a hashtable.
|
// materialize the parent's env into a hashtable.
|
||||||
self.env = Some(os::env_as_bytes().into_iter()
|
self.env = Some(os::env_as_bytes().into_iter()
|
||||||
.map(|(k, v)| (k.as_slice().to_c_str(),
|
.map(|(k, v)| (EnvKey(k.as_slice().to_c_str()),
|
||||||
v.as_slice().to_c_str()))
|
v.as_slice().to_c_str()))
|
||||||
.collect());
|
.collect());
|
||||||
self.env.as_mut().unwrap()
|
self.env.as_mut().unwrap()
|
||||||
|
@ -177,15 +228,18 @@ impl Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts or updates an environment variable mapping.
|
/// Inserts or updates an environment variable mapping.
|
||||||
|
///
|
||||||
|
/// Note that environment variable names are case-insensitive (but case-preserving) on Windows,
|
||||||
|
/// and case-sensitive on all other platforms.
|
||||||
pub fn env<'a, T: ToCStr, U: ToCStr>(&'a mut self, key: T, val: U)
|
pub fn env<'a, T: ToCStr, U: ToCStr>(&'a mut self, key: T, val: U)
|
||||||
-> &'a mut Command {
|
-> &'a mut Command {
|
||||||
self.get_env_map().insert(key.to_c_str(), val.to_c_str());
|
self.get_env_map().insert(EnvKey(key.to_c_str()), val.to_c_str());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes an environment variable mapping.
|
/// Removes an environment variable mapping.
|
||||||
pub fn env_remove<'a, T: ToCStr>(&'a mut self, key: T) -> &'a mut Command {
|
pub fn env_remove<'a, T: ToCStr>(&'a mut self, key: T) -> &'a mut Command {
|
||||||
self.get_env_map().remove(&key.to_c_str());
|
self.get_env_map().remove(&EnvKey(key.to_c_str()));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +249,7 @@ impl Command {
|
||||||
/// variable, the *rightmost* instance will determine the value.
|
/// variable, the *rightmost* instance will determine the value.
|
||||||
pub fn env_set_all<'a, T: ToCStr, U: ToCStr>(&'a mut self, env: &[(T,U)])
|
pub fn env_set_all<'a, T: ToCStr, U: ToCStr>(&'a mut self, env: &[(T,U)])
|
||||||
-> &'a mut Command {
|
-> &'a mut Command {
|
||||||
self.env = Some(env.iter().map(|&(ref k, ref v)| (k.to_c_str(), v.to_c_str()))
|
self.env = Some(env.iter().map(|&(ref k, ref v)| (EnvKey(k.to_c_str()), v.to_c_str()))
|
||||||
.collect());
|
.collect());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -273,7 +327,9 @@ impl Command {
|
||||||
let env = match self.env {
|
let env = match self.env {
|
||||||
None => None,
|
None => None,
|
||||||
Some(ref env_map) =>
|
Some(ref env_map) =>
|
||||||
Some(env_map.iter().collect::<Vec<_>>())
|
Some(env_map.iter()
|
||||||
|
.map(|(&EnvKey(ref key), val)| (key, val))
|
||||||
|
.collect::<Vec<_>>())
|
||||||
};
|
};
|
||||||
let cfg = ProcessConfig {
|
let cfg = ProcessConfig {
|
||||||
program: &self.program,
|
program: &self.program,
|
||||||
|
@ -1039,4 +1095,16 @@ mod tests {
|
||||||
assert!(cmd.status().unwrap().success());
|
assert!(cmd.status().unwrap().success());
|
||||||
assert!(fdes.inner_write("extra write\n".as_bytes()).is_ok());
|
assert!(fdes.inner_write("extra write\n".as_bytes()).is_ok());
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn env_map_keys_ci() {
|
||||||
|
use super::EnvKey;
|
||||||
|
let mut cmd = Command::new("");
|
||||||
|
cmd.env("path", "foo");
|
||||||
|
cmd.env("Path", "bar");
|
||||||
|
let env = &cmd.env.unwrap();
|
||||||
|
let val = env.find(&EnvKey("PATH".to_c_str()));
|
||||||
|
assert!(val.unwrap() == &"bar".to_c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue