scope/paths.rs
1//! The user's directories, resolved in one place.
2//!
3//! Every file scope keeps for the user (config, address book, token
4//! aliases, session, journal, venues) is found through these functions.
5//! Code calls them instead of `dirs::*` (clippy's `disallowed-methods`
6//! enforces this).
7//!
8//! In test builds (`cfg(test)` or the `test-support` feature, which every
9//! test crate turns on) they resolve under a sandbox directory for each
10//! process, never the real home. Tests that wrote the real directories
11//! replaced a user's API keys, address book and token aliases with test
12//! data.
13
14use std::path::PathBuf;
15
16/// In test builds, the environment variable that moves the sandbox, for a
17/// test that needs a home it controls (for example one that cannot be
18/// written).
19pub const TEST_HOME_VAR: &str = "SCOPE_TEST_HOME";
20
21/// The home directory.
22#[allow(clippy::disallowed_methods)]
23pub fn home_dir() -> Option<PathBuf> {
24 #[cfg(any(test, feature = "test-support"))]
25 return Some(sandbox());
26 #[cfg(not(any(test, feature = "test-support")))]
27 dirs::home_dir()
28}
29
30/// The platform's config directory (for example
31/// `~/Library/Application Support` on macOS).
32#[allow(clippy::disallowed_methods)]
33pub fn config_dir() -> Option<PathBuf> {
34 #[cfg(any(test, feature = "test-support"))]
35 return Some(sandbox().join("config"));
36 #[cfg(not(any(test, feature = "test-support")))]
37 dirs::config_dir()
38}
39
40/// The platform's data directory (for example
41/// `~/Library/Application Support` on macOS).
42#[allow(clippy::disallowed_methods)]
43pub fn data_dir() -> Option<PathBuf> {
44 #[cfg(any(test, feature = "test-support"))]
45 return Some(sandbox().join("data"));
46 #[cfg(not(any(test, feature = "test-support")))]
47 dirs::data_dir()
48}
49
50/// The test sandbox: `SCOPE_TEST_HOME` when set, else a directory for this
51/// process under the system temp directory.
52#[cfg(any(test, feature = "test-support"))]
53fn sandbox() -> PathBuf {
54 std::env::var_os(TEST_HOME_VAR)
55 .map(PathBuf::from)
56 .unwrap_or_else(|| {
57 std::env::temp_dir().join(format!("scope-test-home-{}", std::process::id()))
58 })
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn test_test_builds_never_resolve_the_real_home() {
67 // The guard against tests writing the user's files: every
68 // directory is in the sandbox under the temp directory.
69 let temp = std::env::temp_dir();
70 for dir in [home_dir(), config_dir(), data_dir()] {
71 let dir = dir.unwrap();
72 assert!(
73 dir.starts_with(&temp),
74 "{} is outside the sandbox",
75 dir.display()
76 );
77 }
78 }
79}