1//! Canonical definitions of `home_dir`, `cargo_home`, and `rustup_home`.
2//!
3//! The definition of `home_dir` provided by the standard library is
4//! incorrect because it considers the `HOME` environment variable on
5//! Windows. This causes surprising situations where a Rust program
6//! will behave differently depending on whether it is run under a
7//! Unix emulation environment like Cygwin or MinGW. Neither Cargo nor
8//! rustup use the standard libraries definition - they use the
9//! definition here.
10//!
11//! This crate provides two additional functions, `cargo_home` and
12//! `rustup_home`, which are the canonical way to determine the
13//! location that Cargo and rustup use to store their data.
14//! The `env` module contains utilities for mocking the process environment
15//! by Cargo and rustup.
16//!
17//! See also this [discussion].
18//!
19//! [discussion]: https://github.com/rust-lang/rust/pull/46799#issuecomment-361156935
20
21#![allow(clippy::disallowed_methods)]
22
23pub mod env;
24
25#[cfg(target_os = "windows")]
26mod windows;
27
28use std::io;
29use std::path::{Path, PathBuf};
30
31/// Returns the path of the current user's home directory using environment
32/// variables or OS-specific APIs.
33///
34/// # Unix
35///
36/// Returns the value of the `HOME` environment variable if it is set
37/// **even** if it is an empty string. Otherwise, it tries to determine the
38/// home directory by invoking the [`getpwuid_r`][getpwuid] function with
39/// the UID of the current user.
40///
41/// [getpwuid]: https://linux.die.net/man/3/getpwuid_r
42///
43/// # Windows
44///
45/// Returns the value of the `USERPROFILE` environment variable if it is set
46/// **and** it is not an empty string. Otherwise, it tries to determine the
47/// home directory by invoking the [`SHGetKnownFolderPath`][shgkfp] function with
48/// [`FOLDERID_Profile`][knownfolderid].
49///
50/// [shgkfp]: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
51/// [knownfolderid]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
52///
53/// # Examples
54///
55/// ```
56/// match home::home_dir() {
57/// Some(path) if !path.as_os_str().is_empty() => println!("{}", path.display()),
58/// _ => println!("Unable to get your home dir!"),
59/// }
60/// ```
61pub fn home_dir() -> Option<PathBuf> {
62 env::home_dir_with_env(&env::OS_ENV)
63}
64
65#[cfg(windows)]
66use windows::home_dir_inner;
67
68#[cfg(any(unix, target_os = "redox"))]
69fn home_dir_inner() -> Option<PathBuf> {
70 #[allow(deprecated)]
71 std::env::home_dir()
72}
73
74/// Returns the storage directory used by Cargo, often knowns as
75/// `.cargo` or `CARGO_HOME`.
76///
77/// It returns one of the following values, in this order of
78/// preference:
79///
80/// - The value of the `CARGO_HOME` environment variable, if it is
81/// an absolute path.
82/// - The value of the current working directory joined with the value
83/// of the `CARGO_HOME` environment variable, if `CARGO_HOME` is a
84/// relative directory.
85/// - The `.cargo` directory in the user's home directory, as reported
86/// by the `home_dir` function.
87///
88/// # Errors
89///
90/// This function fails if it fails to retrieve the current directory,
91/// or if the home directory cannot be determined.
92///
93/// # Examples
94///
95/// ```
96/// match home::cargo_home() {
97/// Ok(path) => println!("{}", path.display()),
98/// Err(err) => eprintln!("Cannot get your cargo home dir: {:?}", err),
99/// }
100/// ```
101pub fn cargo_home() -> io::Result<PathBuf> {
102 env::cargo_home_with_env(&env::OS_ENV)
103}
104
105/// Returns the storage directory used by Cargo within `cwd`.
106/// For more details, see [`cargo_home`](fn.cargo_home.html).
107pub fn cargo_home_with_cwd(cwd: &Path) -> io::Result<PathBuf> {
108 env::cargo_home_with_cwd_env(&env::OS_ENV, cwd)
109}
110
111/// Returns the storage directory used by rustup, often knowns as
112/// `.rustup` or `RUSTUP_HOME`.
113///
114/// It returns one of the following values, in this order of
115/// preference:
116///
117/// - The value of the `RUSTUP_HOME` environment variable, if it is
118/// an absolute path.
119/// - The value of the current working directory joined with the value
120/// of the `RUSTUP_HOME` environment variable, if `RUSTUP_HOME` is a
121/// relative directory.
122/// - The `.rustup` directory in the user's home directory, as reported
123/// by the `home_dir` function.
124///
125/// # Errors
126///
127/// This function fails if it fails to retrieve the current directory,
128/// or if the home directory cannot be determined.
129///
130/// # Examples
131///
132/// ```
133/// match home::rustup_home() {
134/// Ok(path) => println!("{}", path.display()),
135/// Err(err) => eprintln!("Cannot get your rustup home dir: {:?}", err),
136/// }
137/// ```
138pub fn rustup_home() -> io::Result<PathBuf> {
139 env::rustup_home_with_env(&env::OS_ENV)
140}
141
142/// Returns the storage directory used by rustup within `cwd`.
143/// For more details, see [`rustup_home`](fn.rustup_home.html).
144pub fn rustup_home_with_cwd(cwd: &Path) -> io::Result<PathBuf> {
145 env::rustup_home_with_cwd_env(&env::OS_ENV, cwd)
146}
147