Skip to content

Give CommandEnv OS specific implementations #88064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions library/std/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,6 @@ use crate::path::Path;
use crate::str;
use crate::sys::pipe::{read2, AnonPipe};
use crate::sys::process as imp;
#[unstable(feature = "command_access", issue = "44434")]
pub use crate::sys_common::process::CommandEnvs;
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};

/// Representation of a running or exited child process.
Expand Down Expand Up @@ -1006,7 +1004,7 @@ impl Command {
/// ```
#[unstable(feature = "command_access", issue = "44434")]
pub fn get_envs(&self) -> CommandEnvs<'_> {
self.inner.get_envs()
CommandEnvs { iter: self.inner.get_envs() }
}

/// Returns the working directory for the child process.
Expand Down Expand Up @@ -2058,3 +2056,35 @@ impl Termination for ExitCode {
self.0.as_i32()
}
}

/// An iterator over the command environment variables.
///
/// This struct is created by
/// [`Command::get_envs`][crate::process::Command::get_envs]. See its
/// documentation for more.
#[unstable(feature = "command_access", issue = "44434")]
#[derive(Debug)]
pub struct CommandEnvs<'a> {
iter: imp::CommandEnvs<'a>,
}

#[unstable(feature = "command_access", issue = "44434")]
impl<'a> Iterator for CommandEnvs<'a> {
type Item = (&'a OsStr, Option<&'a OsStr>);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(key, value)| (key.as_ref(), value.as_deref()))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}

#[unstable(feature = "command_access", issue = "44434")]
impl<'a> ExactSizeIterator for CommandEnvs<'a> {
fn len(&self) -> usize {
self.iter.len()
}
fn is_empty(&self) -> bool {
self.iter.is_empty()
}
}
3 changes: 1 addition & 2 deletions library/std/src/sys/unix/process/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
pub use self::process_common::{Command, CommandArgs, CommandEnvs, ExitCode, Stdio, StdioPipes};
pub use self::process_inner::{ExitStatus, ExitStatusError, Process};
pub use crate::ffi::OsString as EnvKey;
pub use crate::sys_common::process::CommandEnvs;

mod process_common;

Expand Down
84 changes: 82 additions & 2 deletions library/std/src/sys/unix/process/process_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ mod tests;

use crate::os::unix::prelude::*;

use crate::collections::BTreeMap;
use crate::collections::{btree_map, BTreeMap};
use crate::env;
use crate::ffi::{CStr, CString, OsStr, OsString};
use crate::fmt;
use crate::io;
Expand All @@ -12,7 +13,7 @@ use crate::ptr;
use crate::sys::fd::FileDesc;
use crate::sys::fs::File;
use crate::sys::pipe::{self, AnonPipe};
use crate::sys_common::process::{CommandEnv, CommandEnvs};
use crate::sys::process::EnvKey;
use crate::sys_common::IntoInner;

#[cfg(not(target_os = "fuchsia"))]
Expand Down Expand Up @@ -324,6 +325,85 @@ impl Command {
}
}

// An iterator over environment key/values.
pub type CommandEnvs<'a> = btree_map::Iter<'a, EnvKey, Option<OsString>>;

// Stores a set of changes to an environment
#[derive(Clone, Debug)]
pub struct CommandEnv {
clear: bool,
saw_path: bool,
vars: BTreeMap<EnvKey, Option<OsString>>,
}

impl Default for CommandEnv {
fn default() -> Self {
CommandEnv { clear: false, saw_path: false, vars: Default::default() }
}
}

impl CommandEnv {
// Capture the current environment with these changes applied
pub fn capture(&self) -> BTreeMap<EnvKey, OsString> {
let mut result = BTreeMap::<EnvKey, OsString>::new();
if !self.clear {
for (k, v) in env::vars_os() {
result.insert(k.into(), v);
}
}
for (k, maybe_v) in &self.vars {
if let &Some(ref v) = maybe_v {
result.insert(k.clone(), v.clone());
} else {
result.remove(k);
}
}
result
}

pub fn is_unchanged(&self) -> bool {
!self.clear && self.vars.is_empty()
}

pub fn capture_if_changed(&self) -> Option<BTreeMap<EnvKey, OsString>> {
if self.is_unchanged() { None } else { Some(self.capture()) }
}

// The following functions build up changes
pub fn set(&mut self, key: &OsStr, value: &OsStr) {
self.maybe_saw_path(&key);
self.vars.insert(key.to_owned().into(), Some(value.to_owned()));
}

pub fn remove(&mut self, key: &OsStr) {
self.maybe_saw_path(&key);
if self.clear {
self.vars.remove(key);
} else {
self.vars.insert(key.to_owned().into(), None);
}
}

pub fn clear(&mut self) {
self.clear = true;
self.vars.clear();
}

pub fn have_changed_path(&self) -> bool {
self.saw_path || self.clear
}

fn maybe_saw_path(&mut self, key: &OsStr) {
if !self.saw_path && key == "PATH" {
self.saw_path = true;
}
}

pub fn iter(&self) -> CommandEnvs<'_> {
self.vars.iter()
}
}

fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
CString::new(s.as_bytes()).unwrap_or_else(|_e| {
*saw_nul = true;
Expand Down
31 changes: 29 additions & 2 deletions library/std/src/sys/unsupported/process.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::ffi::OsStr;
use crate::collections::{btree_map, BTreeMap};
use crate::ffi::{OsStr, OsString};
use crate::fmt;
use crate::io;
use crate::marker::PhantomData;
Expand All @@ -7,7 +8,6 @@ use crate::path::Path;
use crate::sys::fs::File;
use crate::sys::pipe::AnonPipe;
use crate::sys::unsupported;
use crate::sys_common::process::{CommandEnv, CommandEnvs};

pub use crate::ffi::OsString as EnvKey;

Expand Down Expand Up @@ -95,6 +95,33 @@ impl fmt::Debug for Command {
}
}

// An iterator over environment key/values.
pub type CommandEnvs<'a> = btree_map::Iter<'a, EnvKey, Option<OsString>>;

// Stores a set of changes to an environment
#[derive(Clone, Debug, Default)]
pub struct CommandEnv {
vars: BTreeMap<EnvKey, Option<OsString>>,
}

impl CommandEnv {
pub fn set(&mut self, key: &OsStr, value: &OsStr) {
self.vars.insert(key.to_owned().into(), Some(value.to_owned()));
}

pub fn remove(&mut self, key: &OsStr) {
self.vars.remove(key);
}

pub fn clear(&mut self) {
self.vars.clear();
}

pub fn iter(&self) -> CommandEnvs<'_> {
self.vars.iter()
}
}

pub struct ExitStatus(!);

impl ExitStatus {
Expand Down
67 changes: 65 additions & 2 deletions library/std/src/sys/windows/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
mod tests;

use crate::cmp;
use crate::collections::BTreeMap;
use crate::collections::{btree_map, BTreeMap};
use crate::convert::{TryFrom, TryInto};
use crate::env;
use crate::env::split_paths;
Expand All @@ -26,7 +26,6 @@ use crate::sys::handle::Handle;
use crate::sys::pipe::{self, AnonPipe};
use crate::sys::stdio;
use crate::sys_common::mutex::StaticMutex;
use crate::sys_common::process::{CommandEnv, CommandEnvs};
use crate::sys_common::{AsInner, IntoInner};

use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
Expand Down Expand Up @@ -147,6 +146,70 @@ impl AsRef<OsStr> for EnvKey {
}
}

// An iterator over environment key/values.
pub type CommandEnvs<'a> = btree_map::Iter<'a, EnvKey, Option<OsString>>;

// Stores a set of changes to an environment
#[derive(Clone, Debug)]
pub struct CommandEnv {
clear: bool,
vars: BTreeMap<EnvKey, Option<OsString>>,
}

impl Default for CommandEnv {
fn default() -> Self {
CommandEnv { clear: false, vars: Default::default() }
}
}

impl CommandEnv {
// The following functions build up changes
pub fn set(&mut self, key: &OsStr, value: &OsStr) {
let key = EnvKey::from(key);
self.vars.insert(key, Some(value.to_owned()));
}

pub fn remove(&mut self, key: &OsStr) {
let key = EnvKey::from(key);
if self.clear {
self.vars.remove(&key);
} else {
self.vars.insert(key, None);
}
}

pub fn clear(&mut self) {
self.clear = true;
self.vars.clear();
}

pub fn iter(&self) -> CommandEnvs<'_> {
self.vars.iter()
}

// Capture the current environment with these changes applied if the have been changed.
fn capture_if_changed(&self) -> Option<BTreeMap<EnvKey, OsString>> {
if self.clear || !self.vars.is_empty() {
let mut result = BTreeMap::<EnvKey, OsString>::new();
if !self.clear {
for (k, v) in env::vars_os() {
result.insert(k.into(), v);
}
}
for (k, maybe_v) in &self.vars {
if let &Some(ref v) = maybe_v {
result.insert(k.clone(), v.clone());
} else {
result.remove(k);
}
}
Some(result)
} else {
None
}
}
}

fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
if str.as_ref().encode_wide().any(|b| b == 0) {
Err(io::Error::new_const(ErrorKind::InvalidInput, &"nul byte found in provided data"))
Expand Down
1 change: 0 additions & 1 deletion library/std/src/sys_common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ pub mod fs;
pub mod io;
pub mod memchr;
pub mod mutex;
pub mod process;
pub mod remutex;
#[macro_use]
pub mod rt;
Expand Down
Loading