Skip to main content

clawft_platform/
env.rs

1//! Environment variable abstraction and native implementation.
2//!
3//! Provides a platform-agnostic [`Environment`] trait for reading and writing
4//! environment variables. The native implementation delegates to [`std::env`].
5//! A WASM implementation could use a config map or browser-based storage.
6
7/// Platform-agnostic environment variable access.
8///
9/// Implementations provide read/write access to environment-style key-value
10/// configuration. The native implementation maps directly to OS environment
11/// variables; alternative implementations can use in-memory maps for testing
12/// or WASM compatibility.
13pub trait Environment: Send + Sync {
14    /// Get the value of an environment variable, or `None` if it is not set.
15    fn get_var(&self, name: &str) -> Option<String>;
16
17    /// Set an environment variable.
18    fn set_var(&self, name: &str, value: &str);
19
20    /// Remove (unset) an environment variable.
21    fn remove_var(&self, name: &str);
22}
23
24/// Native environment implementation using [`std::env`].
25#[cfg(feature = "native")]
26pub struct NativeEnvironment;
27
28#[cfg(feature = "native")]
29impl Environment for NativeEnvironment {
30    fn get_var(&self, name: &str) -> Option<String> {
31        std::env::var(name).ok()
32    }
33
34    fn set_var(&self, name: &str, value: &str) {
35        // SAFETY: This is the standard library function. We accept the caveats
36        // around concurrent mutation of the environment in multi-threaded programs.
37        // In practice, env var mutation is done during initialization only.
38        unsafe {
39            std::env::set_var(name, value);
40        }
41    }
42
43    fn remove_var(&self, name: &str) {
44        // SAFETY: Same caveat as set_var above.
45        unsafe {
46            std::env::remove_var(name);
47        }
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    // Note: These tests mutate process-global environment variables, so they
56    // use unique variable names to avoid interference with other tests.
57
58    #[test]
59    fn test_get_var_existing() {
60        let env = NativeEnvironment;
61        // PATH is universally available on all platforms
62        assert!(env.get_var("PATH").is_some());
63    }
64
65    #[test]
66    fn test_get_var_missing() {
67        let env = NativeEnvironment;
68        assert!(env.get_var("CLAWFT_DEFINITELY_NOT_SET_12345").is_none());
69    }
70
71    #[test]
72    fn test_set_and_get_var() {
73        let env = NativeEnvironment;
74        let key = "CLAWFT_TEST_SET_GET_VAR";
75
76        env.set_var(key, "test_value");
77        assert_eq!(env.get_var(key), Some("test_value".to_string()));
78
79        // Cleanup
80        env.remove_var(key);
81    }
82
83    #[test]
84    fn test_remove_var() {
85        let env = NativeEnvironment;
86        let key = "CLAWFT_TEST_REMOVE_VAR";
87
88        env.set_var(key, "to_remove");
89        assert!(env.get_var(key).is_some());
90
91        env.remove_var(key);
92        assert!(env.get_var(key).is_none());
93    }
94
95    #[test]
96    fn test_set_var_overwrites() {
97        let env = NativeEnvironment;
98        let key = "CLAWFT_TEST_OVERWRITE_VAR";
99
100        env.set_var(key, "first");
101        assert_eq!(env.get_var(key), Some("first".to_string()));
102
103        env.set_var(key, "second");
104        assert_eq!(env.get_var(key), Some("second".to_string()));
105
106        // Cleanup
107        env.remove_var(key);
108    }
109}