Skip to content
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

Add higher level environment handling #30

Merged
merged 21 commits into from
Aug 31, 2021
Merged
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
120 changes: 120 additions & 0 deletions src/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use std::collections::HashMap;
use std::env;
use std::env::VarsOs;
use std::ffi::{OsStr, OsString};

/// Generic collection of environment variables.
Malax marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Examples
/// ```
/// use std::process::Command;
/// use libcnb::Env;
///
/// let mut env = Env::new();
/// env.insert("FOO", "BAR");
/// env.insert("BAZ", "BLAH");
///
/// let output = Command::new("printenv")
/// .env_clear()
/// .envs(&env)
/// .output()
/// .unwrap();
///
/// assert_eq!(
/// "BAZ=BLAH\nFOO=BAR\n",
/// String::from_utf8_lossy(&output.stdout)
/// );
/// ```
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Env {
inner: HashMap<OsString, OsString>,
}

impl Env {
/// Creates a new `Env` from all the environment variables of the current process.
///
/// The returned `Env` contains a snapshot of the process's environment
/// variables at the time of this invocation. Modifications to environment
/// variables afterwards will not be reflected in the returned value.
///
/// See [`std::env::vars_os`]
pub fn from_current() -> Self {
env::vars_os().into()
}

/// Creates an empty `Env` struct.
pub fn new() -> Self {
Env {
inner: HashMap::new(),
}
}

/// Inserts a key-value pair into the environment, overriding the value if `key` was already
/// present.
pub fn insert(&mut self, key: impl Into<OsString>, value: impl Into<OsString>) -> &mut Self {
self.inner.insert(key.into(), value.into());
self
}

/// Returns a cloned value corresponding to the given key.
pub fn get(&self, key: impl AsRef<OsStr>) -> Option<OsString> {
self.inner.get(key.as_ref()).cloned()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Malax Isn't it usually common here to return a borrowed and not cloned from an object?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #74

}

/// Returns true if the environment contains a value for the specified key.
pub fn contains_key(&self, key: impl AsRef<OsStr>) -> bool {
self.inner.contains_key(key.as_ref())
}

pub fn iter(&self) -> std::collections::hash_map::Iter<'_, OsString, OsString> {
self.inner.iter()
}
}

impl Default for Env {
fn default() -> Self {
Self::new()
}
}

impl From<VarsOs> for Env {
fn from(vars_os: VarsOs) -> Self {
Env {
inner: vars_os.collect(),
}
}
}

impl<'a> IntoIterator for &'a Env {
type Item = (&'a OsString, &'a OsString);
type IntoIter = std::collections::hash_map::Iter<'a, OsString, OsString>;
Malax marked this conversation as resolved.
Show resolved Hide resolved

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

mod test {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be in #[cfg(test)] so it's not built for non test envs?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where rust-lang/rust-clippy#1719 would be useful :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #75

#[test]
#[cfg(target_family = "unix")]
fn test_into_iterator() {
edmorley marked this conversation as resolved.
Show resolved Hide resolved
use crate::Env;
use std::process::Command;

let mut env = Env::new();
env.insert("FOO", "FOO");
env.insert("FOO", "BAR");
env.insert("BAZ", "BLAH");

let output = Command::new("printenv")
.env_clear()
.envs(&env)
.output()
.unwrap();

assert_eq!(
"BAZ=BLAH\nFOO=BAR\n",
String::from_utf8_lossy(&output.stdout)
);
}
}
Loading