| 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 | |