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 mockable home fn and Env trait #29

Merged
merged 6 commits into from
Oct 10, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
}