| //! 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 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")), |
| } |
| } |