Skip to main content

kovra_core/
env_source.rs

1//! The execution environment, behind a trait (spec §4.1 line type 3,
2//! `${env:NAME}`).
3//!
4//! `${env:NAME}` passthrough reads from the process environment at resolution
5//! time. Behind [`EnvSource`] so the resolver is tested deterministically with
6//! [`MockEnvSource`]; production uses [`SystemEnvSource`].
7
8/// A read-only view of named environment variables.
9pub trait EnvSource {
10    /// The value of `name`, or `None` if unset.
11    fn get(&self, name: &str) -> Option<String>;
12}
13
14/// The real process environment (`std::env::var`).
15#[derive(Debug, Default, Clone, Copy)]
16pub struct SystemEnvSource;
17
18impl EnvSource for SystemEnvSource {
19    fn get(&self, name: &str) -> Option<String> {
20        std::env::var(name).ok()
21    }
22}
23
24/// An in-memory environment for tests.
25#[derive(Debug, Default, Clone)]
26pub struct MockEnvSource {
27    vars: std::collections::HashMap<String, String>,
28}
29
30impl MockEnvSource {
31    /// An empty environment.
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    /// Set a variable (builder style).
37    pub fn with(mut self, name: &str, value: &str) -> Self {
38        self.vars.insert(name.to_string(), value.to_string());
39        self
40    }
41}
42
43impl EnvSource for MockEnvSource {
44    fn get(&self, name: &str) -> Option<String> {
45        self.vars.get(name).cloned()
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn mock_env_source_reads_set_vars() {
55        let env = MockEnvSource::new().with("CI_TOKEN", "abc");
56        assert_eq!(env.get("CI_TOKEN").as_deref(), Some("abc"));
57        assert_eq!(env.get("MISSING"), None);
58    }
59}