Skip to main content

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}