standout_input/sources/
env.rs1use 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}