blob: ad31d3d2eb1c9b2aaadf3a3af07b14ffb0b6e196 [file] [log] [blame] [edit]
//! 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.3")]
#![deny(rust_2018_idioms)]
#[cfg(windows)]
mod windows;
use std::env;
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,
/// [`GetUserProfileDirectory`][msdn] is used to return the
/// appropriate path.
///
/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-getuserprofiledirectoryw
///
/// # Examples
///
/// ```
/// match home::home_dir() {
/// Some(path) => println!("{}", path.display()),
/// None => println!("Impossible to get your home dir!"),
/// }
/// ```
pub fn home_dir() -> Option<PathBuf> {
home_dir_inner()
}
#[cfg(windows)]
use windows::home_dir_inner;
#[cfg(any(unix, target_os = "redox"))]
fn home_dir_inner() -> Option<PathBuf> {
#[allow(deprecated)]
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> {
let cwd = env::current_dir()?;
cargo_home_with_cwd(&cwd)
}
/// 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")),
}
}
/// 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> {
let cwd = env::current_dir()?;
rustup_home_with_cwd(&cwd)
}
/// 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")),
}
}