1 | // Copyright 2024 Peter Tripp |
2 | //! env_home is a general purpose crate for determining the current user |
3 | //! home directory in a platform independant manner via enviornment variables. |
4 | //! |
5 | //! This crate is implemented in pure-rust and has no external dependencies. |
6 | //! |
7 | //! It is meant as a lightweight, drop-in replacement for `std::env::home_dir` |
8 | //! provided by the Rust Standard Library which was |
9 | //! [deprecated](https://doc.rust-lang.org/std/env/fn.home_dir.html#deprecation) |
10 | //! in Rust 1.29.0 (Sept 2018). |
11 | //! |
12 | //! ## Usage |
13 | //! ```rust |
14 | //! use env_home::env_home_dir as home_dir; |
15 | //! fn main() { |
16 | //! match home_dir() { |
17 | //! Some(path) => println!("User home directory: {}" , path.display()), |
18 | //! None => println!("No home found. HOME/USERPROFILE not set or empty" ), |
19 | //! } |
20 | //! } |
21 | //! ``` |
22 | |
23 | #[cfg (unix)] |
24 | /// Returns the path of the current user’s home directory if known. |
25 | /// |
26 | /// * On Unix, this function will check the `HOME` environment variable |
27 | /// * On Windows, it will check the `USERPROFILE` environment variable |
28 | /// * On other platforms, this function will always return `None` |
29 | /// * If the environment variable is unset, return `None` |
30 | /// * If the environment variable is set to an empty string, return `None` |
31 | /// |
32 | /// Note: the behavior of this function differs from |
33 | /// [`std::env::home_dir`](https://doc.rust-lang.org/std/env/fn.home_dir.html), |
34 | /// [`home::home_dir`](https://docs.rs/home/latest/home/fn.home_dir.html), and |
35 | /// [`dirs::home_dir`](https://docs.rs/dirs/latest/dirs/fn.home_dir.html). |
36 | /// |
37 | /// This function returns `None` when the environment variable is set but empty. |
38 | /// Those implementations return the empty string `""` instead. |
39 | pub fn env_home_dir() -> Option<std::path::PathBuf> { |
40 | let home: Result = std::env::var(key:"HOME" ); |
41 | match home { |
42 | Ok(val: String) if !val.is_empty() => Some(std::path::PathBuf::from(val)), |
43 | _ => None, |
44 | } |
45 | } |
46 | |
47 | #[cfg (windows)] |
48 | /// Returns the path of the current user’s home directory if known. |
49 | pub fn env_home_dir() -> Option<std::path::PathBuf> { |
50 | let home = std::env::var("USERPROFILE" ); |
51 | match home { |
52 | Ok(val) if !val.is_empty() => Some(std::path::PathBuf::from(val)), |
53 | _ => None, |
54 | } |
55 | } |
56 | |
57 | #[cfg (all(not(windows), not(unix)))] |
58 | /// Returns the path of the current user’s home directory if known. |
59 | pub fn env_home_dir() -> Option<std::path::PathBuf> { |
60 | None |
61 | } |
62 | |
63 | #[cfg (test)] |
64 | mod tests { |
65 | use super::env_home_dir; |
66 | use std::env; |
67 | use std::path::PathBuf; |
68 | |
69 | /* |
70 | Note! Do not run these tests in parallel, as they modify the environment. |
71 | By default `cargo test` will run tests in parallel (multi-threaded) which |
72 | is unsafe and will cause intermittent panics. To run tests sequentially |
73 | use `cargo test -- --test-threads=1`. |
74 | |
75 | More info: |
76 | - https://doc.rust-lang.org/std/env/fn.set_var.html |
77 | - https://github.com/rust-lang/rust/issues/27970 |
78 | |
79 | Possible future test cases: |
80 | - Test non-windows/non-unix platforms (WASM, etc.) |
81 | - Test non-utf8 paths (should return None) |
82 | */ |
83 | |
84 | #[cfg (any(unix, windows))] |
85 | #[test ] |
86 | fn env_home_test() { |
87 | let home_var = if cfg!(windows) { "USERPROFILE" } else { "HOME" }; |
88 | let old = std::env::var(home_var).unwrap(); |
89 | |
90 | // Sanity checks |
91 | assert_ne!(env_home_dir(), None, "HOME/USERPROFILE is unset" ); |
92 | assert_eq!(env_home_dir(), Some(PathBuf::from(old.clone()))); |
93 | |
94 | // Test when var unset. |
95 | env::remove_var(home_var); |
96 | assert_eq!(env_home_dir(), None); |
97 | |
98 | // Test when var set to empty string |
99 | env::set_var(home_var, "" ); |
100 | assert_eq!(env_home_dir(), None); |
101 | |
102 | // Tests a sensible platform specific home directory. |
103 | let temp_dir = if cfg!(windows) { "C: \\temp" } else { "/tmp" }; |
104 | std::env::set_var(home_var, temp_dir); |
105 | assert_eq!(env_home_dir(), Some(std::path::PathBuf::from(temp_dir))); |
106 | |
107 | env::set_var(home_var, old); |
108 | } |
109 | } |
110 | |