Skip to main content

relay_knowledge/env/
platform.rs

1//! Platform-specific environment inputs and normalization.
2
3use std::{env as process_env, ffi::OsString};
4
5use super::{
6    EnvError,
7    value_parser::{EnvironmentValues, first_path_var, path_var},
8};
9
10pub(super) const HOME: &str = "HOME";
11const WINDOWS_SYSTEM_ROOT: &str = "SystemRoot";
12const XDG_CONFIG_HOME: &str = "XDG_CONFIG_HOME";
13const XDG_DATA_HOME: &str = "XDG_DATA_HOME";
14const XDG_STATE_HOME: &str = "XDG_STATE_HOME";
15const XDG_CACHE_HOME: &str = "XDG_CACHE_HOME";
16const XDG_RUNTIME_DIR: &str = "XDG_RUNTIME_DIR";
17const APPDATA: &str = "APPDATA";
18const LOCALAPPDATA: &str = "LOCALAPPDATA";
19pub(super) const TMPDIR: &str = "TMPDIR";
20pub(super) const TEMP: &str = "TEMP";
21pub(super) const TMP: &str = "TMP";
22
23pub(crate) fn windows_system_root_from_process() -> Option<OsString> {
24    process_env::var_os(WINDOWS_SYSTEM_ROOT).filter(|value| !value.is_empty())
25}
26
27/// Operating-system family used by path resolution.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum PlatformKind {
30    Unix,
31    Macos,
32    Windows,
33    Other,
34}
35
36impl PlatformKind {
37    /// Detects the current target platform without consulting environment state.
38    pub const fn current() -> Self {
39        if cfg!(target_os = "windows") {
40            Self::Windows
41        } else if cfg!(target_os = "macos") {
42            Self::Macos
43        } else if cfg!(unix) {
44            Self::Unix
45        } else {
46            Self::Other
47        }
48    }
49}
50
51/// Platform directory inputs captured from the environment.
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct PlatformEnvironment {
54    pub platform: PlatformKind,
55    pub home_dir: Option<std::path::PathBuf>,
56    pub xdg_config_home: Option<std::path::PathBuf>,
57    pub xdg_data_home: Option<std::path::PathBuf>,
58    pub xdg_state_home: Option<std::path::PathBuf>,
59    pub xdg_cache_home: Option<std::path::PathBuf>,
60    pub xdg_runtime_dir: Option<std::path::PathBuf>,
61    pub app_data: Option<std::path::PathBuf>,
62    pub local_app_data: Option<std::path::PathBuf>,
63    pub temp_dir: Option<std::path::PathBuf>,
64}
65
66pub(super) fn platform_environment(
67    values: &EnvironmentValues,
68    platform: PlatformKind,
69) -> Result<PlatformEnvironment, EnvError> {
70    let temp_variables: &[&'static str] = if platform == PlatformKind::Windows {
71        &[TEMP, TMP, TMPDIR]
72    } else {
73        &[TMPDIR, TEMP, TMP]
74    };
75
76    Ok(PlatformEnvironment {
77        platform,
78        home_dir: path_var(values, HOME)?,
79        xdg_config_home: path_var(values, XDG_CONFIG_HOME)?,
80        xdg_data_home: path_var(values, XDG_DATA_HOME)?,
81        xdg_state_home: path_var(values, XDG_STATE_HOME)?,
82        xdg_cache_home: path_var(values, XDG_CACHE_HOME)?,
83        xdg_runtime_dir: path_var(values, XDG_RUNTIME_DIR)?,
84        app_data: path_var(values, APPDATA)?,
85        local_app_data: path_var(values, LOCALAPPDATA)?,
86        temp_dir: first_path_var(values, temp_variables)?,
87    })
88}
89
90pub(super) fn normalize_key(platform: PlatformKind, key: OsString) -> OsString {
91    if platform == PlatformKind::Windows {
92        key.to_str()
93            .map(|value| OsString::from(value.to_ascii_uppercase()))
94            .unwrap_or(key)
95    } else {
96        key
97    }
98}
99
100#[cfg(test)]
101#[path = "platform_tests.rs"]
102mod platform_tests;