Skip to content

Commit

Permalink
Merge pull request #29 from brson/lucio/env
Browse files Browse the repository at this point in the history
Add mockable home fn and Env trait
  • Loading branch information
LucioFranco committed Oct 10, 2022
2 parents 9afbd90 + fb34fe9 commit de6dd85
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 33 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

<!-- ## [Unreleased] -->
- Add `_with_env` variants of functions to support in-process threaded tests for
rustup.

## [0.5.3] - 2020-01-07

Use Rust 1.36.0 as minimum Rust version.
Expand Down
106 changes: 106 additions & 0 deletions src/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//! Lower-level utilities for mocking the process environment.

use std::{
ffi::OsString,
io,
path::{Path, PathBuf},
};

/// Permits parameterizing the home functions via the _from variants - used for
/// in-process unit testing by rustup.
pub trait Env {
/// Return the path to the the users home dir, or None if any error occurs:
/// see home_inner.
fn home_dir(&self) -> Option<PathBuf>;
/// Return the current working directory.
fn current_dir(&self) -> io::Result<PathBuf>;
/// Get an environment variable, as per std::env::var_os.
fn var_os(&self, key: &str) -> Option<OsString>;
}

/// Implements Env for the OS context, both Unix style and Windows.
///
/// This is trait permits in-process testing by providing a control point to
/// allow in-process divergence on what is normally process wide state.
///
/// Implementations should be provided by whatever testing framework the caller
/// is using. Code that is not performing in-process threaded testing requiring
/// isolated rustup/cargo directories does not need this trait or the _from
/// functions.
pub struct OsEnv;
impl Env for OsEnv {
fn home_dir(&self) -> Option<PathBuf> {
crate::home_dir_inner()
}
fn current_dir(&self) -> io::Result<PathBuf> {
std::env::current_dir()
}
fn var_os(&self, key: &str) -> Option<OsString> {
std::env::var_os(key)
}
}

pub const OS_ENV: OsEnv = OsEnv {};

/// Returns the path of the current user's home directory from [`Env::home_dir`].
pub fn home_dir_with_env(env: &dyn Env) -> Option<PathBuf> {
env.home_dir()
}

/// Variant of cargo_home where the environment source is parameterized. This is
/// specifically to support in-process testing scenarios as environment
/// variables and user home metadata are normally process global state. See the
/// [`Env`] trait.
pub fn cargo_home_with_env(env: &dyn Env) -> io::Result<PathBuf> {
let cwd = env.current_dir()?;
cargo_home_with_cwd_env(env, &cwd)
}

/// Variant of cargo_home_with_cwd where the environment source is
/// parameterized. This is specifically to support in-process testing scenarios
/// as environment variables and user home metadata are normally process global
/// state. See the OsEnv trait.
pub fn cargo_home_with_cwd_env(env: &dyn Env, cwd: &Path) -> io::Result<PathBuf> {
match env.var_os("CARGO_HOME").filter(|h| !h.is_empty()) {
Some(home) => {
let home = PathBuf::from(home);
if home.is_absolute() {
Ok(home)
} else {
Ok(cwd.join(&home))
}
}
_ => home_dir_with_env(env)
.map(|p| p.join(".cargo"))
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "could not find cargo home dir")),
}
}

/// Variant of cargo_home_with_cwd where the environment source is
/// parameterized. This is specifically to support in-process testing scenarios
/// as environment variables and user home metadata are normally process global
/// state. See the OsEnv trait.
pub fn rustup_home_with_env(env: &dyn Env) -> io::Result<PathBuf> {
let cwd = env.current_dir()?;
rustup_home_with_cwd_env(env, &cwd)
}

/// Variant of cargo_home_with_cwd where the environment source is
/// parameterized. This is specifically to support in-process testing scenarios
/// as environment variables and user home metadata are normally process global
/// state. See the OsEnv trait.
pub fn rustup_home_with_cwd_env(env: &dyn Env, cwd: &Path) -> io::Result<PathBuf> {
match env.var_os("RUSTUP_HOME").filter(|h| !h.is_empty()) {
Some(home) => {
let home = PathBuf::from(home);
if home.is_absolute() {
Ok(home)
} else {
Ok(cwd.join(&home))
}
}
_ => home_dir_with_env(env)
.map(|d| d.join(".rustup"))
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "could not find rustup home dir")),
}
}
41 changes: 8 additions & 33 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@
#![doc(html_root_url = "https://docs.rs/home/0.5.3")]
#![deny(rust_2018_idioms)]

pub mod env;

#[cfg(windows)]
mod windows;

use std::env;
use std::io;
use std::path::{Path, PathBuf};

Expand Down Expand Up @@ -61,7 +62,7 @@ use std::path::{Path, PathBuf};
/// }
/// ```
pub fn home_dir() -> Option<PathBuf> {
home_dir_inner()
env::home_dir_with_env(&env::OS_ENV)
}

#[cfg(windows)]
Expand All @@ -70,7 +71,7 @@ use windows::home_dir_inner;
#[cfg(any(unix, target_os = "redox"))]
fn home_dir_inner() -> Option<PathBuf> {
#[allow(deprecated)]
env::home_dir()
std::env::home_dir()
}

/// Returns the storage directory used by Cargo, often knowns as
Expand Down Expand Up @@ -101,26 +102,13 @@ fn home_dir_inner() -> Option<PathBuf> {
/// }
/// ```
pub fn cargo_home() -> io::Result<PathBuf> {
let cwd = env::current_dir()?;
cargo_home_with_cwd(&cwd)
env::cargo_home_with_env(&env::OS_ENV)
}

/// Returns the storage directory used by Cargo within `cwd`.
/// For more details, see [`cargo_home`](fn.cargo_home.html).
pub fn cargo_home_with_cwd(cwd: &Path) -> io::Result<PathBuf> {
match env::var_os("CARGO_HOME").filter(|h| !h.is_empty()) {
Some(home) => {
let home = PathBuf::from(home);
if home.is_absolute() {
Ok(home)
} else {
Ok(cwd.join(&home))
}
}
_ => home_dir()
.map(|p| p.join(".cargo"))
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "could not find cargo home dir")),
}
env::cargo_home_with_cwd_env(&env::OS_ENV, cwd)
}

/// Returns the storage directory used by rustup, often knowns as
Expand Down Expand Up @@ -151,24 +139,11 @@ pub fn cargo_home_with_cwd(cwd: &Path) -> io::Result<PathBuf> {
/// }
/// ```
pub fn rustup_home() -> io::Result<PathBuf> {
let cwd = env::current_dir()?;
rustup_home_with_cwd(&cwd)
env::rustup_home_with_env(&env::OS_ENV)
}

/// Returns the storage directory used by rustup within `cwd`.
/// For more details, see [`rustup_home`](fn.rustup_home.html).
pub fn rustup_home_with_cwd(cwd: &Path) -> io::Result<PathBuf> {
match env::var_os("RUSTUP_HOME").filter(|h| !h.is_empty()) {
Some(home) => {
let home = PathBuf::from(home);
if home.is_absolute() {
Ok(home)
} else {
Ok(cwd.join(&home))
}
}
_ => home_dir()
.map(|d| d.join(".rustup"))
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "could not find rustup home dir")),
}
env::rustup_home_with_cwd_env(&env::OS_ENV, cwd)
}

0 comments on commit de6dd85

Please sign in to comment.