summaryrefslogtreecommitdiffstats
path: root/crates/home
diff options
context:
space:
mode:
Diffstat (limited to 'crates/home')
-rw-r--r--crates/home/CHANGELOG.md46
-rw-r--r--crates/home/Cargo.toml20
l---------crates/home/LICENSE-APACHE1
l---------crates/home/LICENSE-MIT1
-rw-r--r--crates/home/README.md27
-rw-r--r--crates/home/src/env.rs106
-rw-r--r--crates/home/src/lib.rs149
-rw-r--r--crates/home/src/windows.rs66
8 files changed, 416 insertions, 0 deletions
diff --git a/crates/home/CHANGELOG.md b/crates/home/CHANGELOG.md
new file mode 100644
index 0000000..7674667
--- /dev/null
+++ b/crates/home/CHANGELOG.md
@@ -0,0 +1,46 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+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] -->
+
+## [0.5.4] - 2022-10-10
+- 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.
+
+## [0.5.2] - 2020-01-05
+
+*YANKED since it cannot be built on Rust 1.36.0*
+
+### Changed
+- Check for emptiness of `CARGO_HOME` and `RUSTUP_HOME` environment variables.
+- Windows: Use `SHGetFolderPath` to replace `GetUserProfileDirectory` syscall.
+ * Remove `scopeguard` dependency.
+
+## [0.5.1] - 2019-10-12
+### Changed
+- Disable unnecessary features for `scopeguard`. Thanks @mati865.
+
+## [0.5.0] - 2019-08-21
+### Added
+- Add `home_dir` implementation for Windows UWP platforms.
+
+### Fixed
+- Fix `rustup_home` implementation when `RUSTUP_HOME` is an absolute directory.
+- Fix `cargo_home` implementation when `CARGO_HOME` is an absolute directory.
+
+### Removed
+- Remove support for `multirust` folder used in old version of `rustup`.
+
+[Unreleased]: https://github.com/brson/home/compare/v0.5.4...HEAD
+[0.5.4]: https://github.com/brson/home/compare/v0.5.3...v0.5.4
+[0.5.3]: https://github.com/brson/home/compare/v0.5.2...v0.5.3
+[0.5.2]: https://github.com/brson/home/compare/v0.5.1...v0.5.2
+[0.5.1]: https://github.com/brson/home/compare/v0.5.0...v0.5.1
+[0.5.0]: https://github.com/brson/home/compare/0.4.2...v0.5.0
diff --git a/crates/home/Cargo.toml b/crates/home/Cargo.toml
new file mode 100644
index 0000000..18459df
--- /dev/null
+++ b/crates/home/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "home"
+version = "0.5.4" # also update `html_root_url` in `src/lib.rs`
+authors = [ "Brian Anderson <andersrb@gmail.com>" ]
+documentation = "https://docs.rs/home"
+edition = "2018"
+include = [
+ "/src",
+ "/Cargo.toml",
+ "/CHANGELOG",
+ "/LICENSE-*",
+ "/README.md",
+]
+license = "MIT OR Apache-2.0"
+readme = "README.md"
+repository = "https://github.com/brson/home"
+description = "Shared definitions of home directories"
+
+[target.'cfg(windows)'.dependencies]
+windows-sys = { version = "0.45.0", features = ["Win32_Foundation", "Win32_UI_Shell"] }
diff --git a/crates/home/LICENSE-APACHE b/crates/home/LICENSE-APACHE
new file mode 120000
index 0000000..1cd601d
--- /dev/null
+++ b/crates/home/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/crates/home/LICENSE-MIT b/crates/home/LICENSE-MIT
new file mode 120000
index 0000000..b2cfbdc
--- /dev/null
+++ b/crates/home/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/crates/home/README.md b/crates/home/README.md
new file mode 100644
index 0000000..db2ba92
--- /dev/null
+++ b/crates/home/README.md
@@ -0,0 +1,27 @@
+[![Documentation](https://docs.rs/home/badge.svg)](https://docs.rs/home)
+[![Crates.io](https://img.shields.io/crates/v/home.svg)](https://crates.io/crates/home)
+
+Canonical definitions of `home_dir`, `cargo_home`, and `rustup_home`.
+
+This provides the definition of `home_dir` used by Cargo and rustup,
+as well functions to find the correct value of `CARGO_HOME` and
+`RUSTUP_HOME`.
+
+The definition of `home_dir` provided by the standard library is
+incorrect because it considers the `HOME` environment variable on
+Windows. This causes surprising situations where a Rust program will
+behave differently depending on whether it is run under a Unix
+emulation environment like Cygwin or MinGW. Neither Cargo nor rustup
+use the standard libraries definition - they use the definition here.
+
+This crate further provides two functions, `cargo_home` and
+`rustup_home`, which are the canonical way to determine the location
+that Cargo and rustup store their data.
+
+See [rust-lang/rust#43321].
+
+[rust-lang/rust#43321]: https://github.com/rust-lang/rust/issues/43321
+
+## License
+
+MIT OR Apache-2.0
diff --git a/crates/home/src/env.rs b/crates/home/src/env.rs
new file mode 100644
index 0000000..e47273b
--- /dev/null
+++ b/crates/home/src/env.rs
@@ -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")),
+ }
+}
diff --git a/crates/home/src/lib.rs b/crates/home/src/lib.rs
new file mode 100644
index 0000000..306026e
--- /dev/null
+++ b/crates/home/src/lib.rs
@@ -0,0 +1,149 @@
+//! Canonical definitions of `home_dir`, `cargo_home`, and `rustup_home`.
+//!
+//! This provides the definition of `home_dir` used by Cargo and
+//! rustup, as well functions to find the correct value of
+//! `CARGO_HOME` and `RUSTUP_HOME`.
+//!
+//! See also the [`dirs`](https://docs.rs/dirs) crate.
+//!
+//! _Note that as of 2019/08/06 it appears that cargo uses this crate. And
+//! rustup has used this crate since 2019/08/21._
+//!
+//! The definition of `home_dir` provided by the standard library is
+//! incorrect because it considers the `HOME` environment variable on
+//! Windows. This causes surprising situations where a Rust program
+//! will behave differently depending on whether it is run under a
+//! Unix emulation environment like Cygwin or MinGW. Neither Cargo nor
+//! rustup use the standard libraries definition - they use the
+//! definition here.
+//!
+//! This crate further provides two functions, `cargo_home` and
+//! `rustup_home`, which are the canonical way to determine the
+//! location that Cargo and rustup store their data.
+//!
+//! See also this [discussion].
+//!
+//! [discussion]: https://github.com/rust-lang/rust/pull/46799#issuecomment-361156935
+
+#![doc(html_root_url = "https://docs.rs/home/0.5.4")]
+#![deny(rust_2018_idioms)]
+
+pub mod env;
+
+#[cfg(target_os = "windows")]
+mod windows;
+
+use std::io;
+use std::path::{Path, PathBuf};
+
+/// Returns the path of the current user's home directory if known.
+///
+/// # Unix
+///
+/// Returns the value of the `HOME` environment variable if it is set
+/// and not equal to the empty string. Otherwise, it tries to determine the
+/// home directory by invoking the `getpwuid_r` function on the UID of the
+/// current user.
+///
+/// # Windows
+///
+/// Returns the value of the `USERPROFILE` environment variable if it
+/// is set and not equal to the empty string. If both do not exist,
+/// [`SHGetFolderPathW`][msdn] is used to return the appropriate path.
+///
+/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpathw
+///
+/// # Examples
+///
+/// ```
+/// match home::home_dir() {
+/// Some(path) => println!("{}", path.display()),
+/// None => println!("Impossible to get your home dir!"),
+/// }
+/// ```
+pub fn home_dir() -> Option<PathBuf> {
+ env::home_dir_with_env(&env::OS_ENV)
+}
+
+#[cfg(windows)]
+use windows::home_dir_inner;
+
+#[cfg(any(unix, target_os = "redox"))]
+fn home_dir_inner() -> Option<PathBuf> {
+ #[allow(deprecated)]
+ std::env::home_dir()
+}
+
+/// Returns the storage directory used by Cargo, often knowns as
+/// `.cargo` or `CARGO_HOME`.
+///
+/// It returns one of the following values, in this order of
+/// preference:
+///
+/// - The value of the `CARGO_HOME` environment variable, if it is
+/// an absolute path.
+/// - The value of the current working directory joined with the value
+/// of the `CARGO_HOME` environment variable, if `CARGO_HOME` is a
+/// relative directory.
+/// - The `.cargo` directory in the user's home directory, as reported
+/// by the `home_dir` function.
+///
+/// # Errors
+///
+/// This function fails if it fails to retrieve the current directory,
+/// or if the home directory cannot be determined.
+///
+/// # Examples
+///
+/// ```
+/// match home::cargo_home() {
+/// Ok(path) => println!("{}", path.display()),
+/// Err(err) => eprintln!("Cannot get your cargo home dir: {:?}", err),
+/// }
+/// ```
+pub fn cargo_home() -> io::Result<PathBuf> {
+ 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> {
+ env::cargo_home_with_cwd_env(&env::OS_ENV, cwd)
+}
+
+/// Returns the storage directory used by rustup, often knowns as
+/// `.rustup` or `RUSTUP_HOME`.
+///
+/// It returns one of the following values, in this order of
+/// preference:
+///
+/// - The value of the `RUSTUP_HOME` environment variable, if it is
+/// an absolute path.
+/// - The value of the current working directory joined with the value
+/// of the `RUSTUP_HOME` environment variable, if `RUSTUP_HOME` is a
+/// relative directory.
+/// - The `.rustup` directory in the user's home directory, as reported
+/// by the `home_dir` function.
+///
+/// # Errors
+///
+/// This function fails if it fails to retrieve the current directory,
+/// or if the home directory cannot be determined.
+///
+/// # Examples
+///
+/// ```
+/// match home::rustup_home() {
+/// Ok(path) => println!("{}", path.display()),
+/// Err(err) => eprintln!("Cannot get your rustup home dir: {:?}", err),
+/// }
+/// ```
+pub fn rustup_home() -> io::Result<PathBuf> {
+ 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> {
+ env::rustup_home_with_cwd_env(&env::OS_ENV, cwd)
+}
diff --git a/crates/home/src/windows.rs b/crates/home/src/windows.rs
new file mode 100644
index 0000000..a35dc9c
--- /dev/null
+++ b/crates/home/src/windows.rs
@@ -0,0 +1,66 @@
+use std::env;
+use std::ffi::OsString;
+use std::os::windows::ffi::OsStringExt;
+use std::path::PathBuf;
+
+use windows_sys::Win32::Foundation::{MAX_PATH, S_OK};
+use windows_sys::Win32::UI::Shell::{SHGetFolderPathW, CSIDL_PROFILE};
+
+pub fn home_dir_inner() -> Option<PathBuf> {
+ env::var_os("USERPROFILE")
+ .filter(|s| !s.is_empty())
+ .map(PathBuf::from)
+ .or_else(home_dir_crt)
+}
+
+#[cfg(not(target_vendor = "uwp"))]
+fn home_dir_crt() -> Option<PathBuf> {
+ unsafe {
+ let mut path: Vec<u16> = Vec::with_capacity(MAX_PATH as usize);
+ match SHGetFolderPathW(0, CSIDL_PROFILE as i32, 0, 0, path.as_mut_ptr()) {
+ S_OK => {
+ let len = wcslen(path.as_ptr());
+ path.set_len(len);
+ let s = OsString::from_wide(&path);
+ Some(PathBuf::from(s))
+ }
+ _ => None,
+ }
+ }
+}
+
+#[cfg(target_vendor = "uwp")]
+fn home_dir_crt() -> Option<PathBuf> {
+ None
+}
+
+extern "C" {
+ fn wcslen(buf: *const u16) -> usize;
+}
+
+#[cfg(not(target_vendor = "uwp"))]
+#[cfg(test)]
+mod tests {
+ use super::home_dir_inner;
+ use std::env;
+ use std::ops::Deref;
+ use std::path::{Path, PathBuf};
+
+ #[test]
+ fn test_with_without() {
+ let olduserprofile = env::var_os("USERPROFILE").unwrap();
+
+ env::remove_var("HOME");
+ env::remove_var("USERPROFILE");
+
+ assert_eq!(home_dir_inner(), Some(PathBuf::from(olduserprofile)));
+
+ let home = Path::new(r"C:\Users\foo tar baz");
+
+ env::set_var("HOME", home.as_os_str());
+ assert_ne!(home_dir_inner().as_ref().map(Deref::deref), Some(home));
+
+ env::set_var("USERPROFILE", home.as_os_str());
+ assert_eq!(home_dir_inner().as_ref().map(Deref::deref), Some(home));
+ }
+}