subx_cli/config/
environment.rs

1//! Environment variable provider module.
2//!
3//! This module defines traits for abstracting environment variable access,
4//! along with corresponding production and test implementations.
5
6use std::collections::HashMap;
7
8/// Environment variable provider trait.
9///
10/// This trait abstracts environment variable access, allowing for mock implementations
11/// to be injected during testing.
12pub trait EnvironmentProvider: Send + Sync {
13    /// Get the value of the specified environment variable.
14    ///
15    /// # Arguments
16    /// * `key` - Environment variable name
17    ///
18    /// # Returns
19    /// Returns `Some(value)` if the environment variable exists and is valid,
20    /// otherwise returns `None`.
21    fn get_var(&self, key: &str) -> Option<String>;
22
23    /// Check if an environment variable exists.
24    ///
25    /// # Arguments
26    /// * `key` - Environment variable name
27    ///
28    /// # Returns
29    /// Returns `true` if the environment variable exists, otherwise `false`.
30    fn has_var(&self, key: &str) -> bool {
31        self.get_var(key).is_some()
32    }
33}
34
35/// System environment variable provider implementation.
36///
37/// This implementation directly reads system environment variables,
38/// intended for use in production environments.
39#[derive(Debug, Default)]
40pub struct SystemEnvironmentProvider;
41
42impl SystemEnvironmentProvider {
43    /// Create a new system environment variable provider.
44    pub fn new() -> Self {
45        Self
46    }
47}
48
49impl EnvironmentProvider for SystemEnvironmentProvider {
50    fn get_var(&self, key: &str) -> Option<String> {
51        std::env::var(key).ok()
52    }
53}
54
55/// Test environment variable provider implementation.
56///
57/// This implementation uses a predefined variable mapping,
58/// intended for complete isolation in test environments.
59#[derive(Debug)]
60pub struct TestEnvironmentProvider {
61    variables: HashMap<String, String>,
62}
63
64impl TestEnvironmentProvider {
65    /// Create a new test environment variable provider.
66    pub fn new() -> Self {
67        Self {
68            variables: HashMap::new(),
69        }
70    }
71
72    /// Create a test provider containing specified variables.
73    ///
74    /// # Arguments
75    /// * `variables` - Environment variable mapping
76    pub fn with_variables(variables: HashMap<String, String>) -> Self {
77        Self { variables }
78    }
79
80    /// Set an environment variable.
81    ///
82    /// # Arguments
83    /// * `key` - Environment variable name
84    /// * `value` - Environment variable value
85    pub fn set_var(&mut self, key: &str, value: &str) {
86        self.variables.insert(key.to_string(), value.to_string());
87    }
88
89    /// Remove an environment variable.
90    ///
91    /// # Arguments
92    /// * `key` - Environment variable name
93    pub fn remove_var(&mut self, key: &str) {
94        self.variables.remove(key);
95    }
96
97    /// Clear all environment variables.
98    pub fn clear(&mut self) {
99        self.variables.clear();
100    }
101}
102
103impl EnvironmentProvider for TestEnvironmentProvider {
104    fn get_var(&self, key: &str) -> Option<String> {
105        self.variables.get(key).cloned()
106    }
107}
108
109impl Default for TestEnvironmentProvider {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_system_environment_provider_existing_var() {
121        let provider = SystemEnvironmentProvider::new();
122        // Test using a commonly existing environment variable
123        let path = provider.get_var("PATH");
124        assert!(path.is_some());
125        assert!(!path.unwrap().is_empty());
126    }
127
128    #[test]
129    fn test_system_environment_provider_non_existing_var() {
130        let provider = SystemEnvironmentProvider::new();
131        let result = provider.get_var("NON_EXISTING_VAR_12345");
132        assert!(result.is_none());
133    }
134
135    #[test]
136    fn test_test_environment_provider_empty() {
137        let provider = TestEnvironmentProvider::new();
138        assert!(provider.get_var("ANY_VAR").is_none());
139        assert!(!provider.has_var("ANY_VAR"));
140    }
141
142    #[test]
143    fn test_test_environment_provider_with_variables() {
144        let mut vars = HashMap::new();
145        vars.insert("TEST_VAR".to_string(), "test_value".to_string());
146        let provider = TestEnvironmentProvider::with_variables(vars);
147        assert_eq!(provider.get_var("TEST_VAR"), Some("test_value".to_string()));
148        assert!(provider.has_var("TEST_VAR"));
149        assert!(provider.get_var("OTHER_VAR").is_none());
150    }
151
152    #[test]
153    fn test_test_environment_provider_set_and_remove() {
154        let mut provider = TestEnvironmentProvider::new();
155        provider.set_var("DYNAMIC_VAR", "dynamic_value");
156        assert_eq!(
157            provider.get_var("DYNAMIC_VAR"),
158            Some("dynamic_value".to_string())
159        );
160        provider.remove_var("DYNAMIC_VAR");
161        assert!(provider.get_var("DYNAMIC_VAR").is_none());
162    }
163
164    #[test]
165    fn test_test_environment_provider_clear() {
166        let mut provider = TestEnvironmentProvider::new();
167        provider.set_var("VAR1", "value1");
168        provider.set_var("VAR2", "value2");
169        provider.clear();
170        assert!(provider.get_var("VAR1").is_none());
171        assert!(provider.get_var("VAR2").is_none());
172    }
173}