Skip to main content

standout_input/sources/
env.rs

1use std::sync::Arc;
2
3use clap::ArgMatches;
4
5use crate::collector::InputCollector;
6use crate::env::{EnvReader, RealEnv};
7use crate::InputError;
8
9#[derive(Clone)]
10pub struct EnvSource<R: EnvReader = RealEnv> {
11    var_name: String,
12    reader: Arc<R>,
13}
14
15impl EnvSource<RealEnv> {
16    pub fn new(var_name: impl Into<String>) -> Self {
17        Self {
18            var_name: var_name.into(),
19            reader: Arc::new(RealEnv),
20        }
21    }
22}
23
24impl<R: EnvReader> EnvSource<R> {
25    pub fn with_reader(var_name: impl Into<String>, reader: R) -> Self {
26        Self {
27            var_name: var_name.into(),
28            reader: Arc::new(reader),
29        }
30    }
31
32    pub fn var_name(&self) -> &str {
33        &self.var_name
34    }
35}
36
37impl<R: EnvReader + 'static> InputCollector<String> for EnvSource<R> {
38    fn name(&self) -> &'static str {
39        "environment variable"
40    }
41
42    fn is_available(&self, _matches: &ArgMatches) -> bool {
43        self.reader
44            .var(&self.var_name)
45            .map(|v| !v.is_empty())
46            .unwrap_or(false)
47    }
48
49    fn collect(&self, _matches: &ArgMatches) -> Result<Option<String>, InputError> {
50        match self.reader.var(&self.var_name) {
51            Some(value) if !value.is_empty() => Ok(Some(value)),
52            _ => Ok(None),
53        }
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use crate::env::MockEnv;
61    use clap::Command;
62
63    fn empty_matches() -> ArgMatches {
64        Command::new("test").try_get_matches_from(["test"]).unwrap()
65    }
66
67    #[test]
68    fn env_available_when_set() {
69        let env = MockEnv::new().with_var("MY_VAR", "value");
70        let source = EnvSource::with_reader("MY_VAR", env);
71
72        assert!(source.is_available(&empty_matches()));
73    }
74
75    #[test]
76    fn env_unavailable_when_unset() {
77        let env = MockEnv::new();
78        let source = EnvSource::with_reader("MY_VAR", env);
79
80        assert!(!source.is_available(&empty_matches()));
81    }
82
83    #[test]
84    fn env_unavailable_when_empty() {
85        let env = MockEnv::new().with_var("MY_VAR", "");
86        let source = EnvSource::with_reader("MY_VAR", env);
87
88        assert!(!source.is_available(&empty_matches()));
89    }
90
91    #[test]
92    fn env_collects_value() {
93        let env = MockEnv::new().with_var("MY_VAR", "hello");
94        let source = EnvSource::with_reader("MY_VAR", env);
95
96        let result = source.collect(&empty_matches()).unwrap();
97        assert_eq!(result, Some("hello".to_string()));
98    }
99
100    #[test]
101    fn env_returns_none_when_unset() {
102        let env = MockEnv::new();
103        let source = EnvSource::with_reader("MY_VAR", env);
104
105        let result = source.collect(&empty_matches()).unwrap();
106        assert_eq!(result, None);
107    }
108
109    #[test]
110    fn env_returns_none_when_empty() {
111        let env = MockEnv::new().with_var("MY_VAR", "");
112        let source = EnvSource::with_reader("MY_VAR", env);
113
114        let result = source.collect(&empty_matches()).unwrap();
115        assert_eq!(result, None);
116    }
117}