standout_input/sources/
env.rs1use std::sync::Arc;
4
5use clap::ArgMatches;
6
7use crate::collector::InputCollector;
8use crate::env::{EnvReader, RealEnv};
9use crate::InputError;
10
11#[derive(Clone)]
38pub struct EnvSource<R: EnvReader = RealEnv> {
39 var_name: String,
40 reader: Arc<R>,
41}
42
43impl EnvSource<RealEnv> {
44 pub fn new(var_name: impl Into<String>) -> Self {
46 Self {
47 var_name: var_name.into(),
48 reader: Arc::new(RealEnv),
49 }
50 }
51}
52
53impl<R: EnvReader> EnvSource<R> {
54 pub fn with_reader(var_name: impl Into<String>, reader: R) -> Self {
58 Self {
59 var_name: var_name.into(),
60 reader: Arc::new(reader),
61 }
62 }
63
64 pub fn var_name(&self) -> &str {
66 &self.var_name
67 }
68}
69
70impl<R: EnvReader + 'static> InputCollector<String> for EnvSource<R> {
71 fn name(&self) -> &'static str {
72 "environment variable"
73 }
74
75 fn is_available(&self, _matches: &ArgMatches) -> bool {
76 self.reader
77 .var(&self.var_name)
78 .map(|v| !v.is_empty())
79 .unwrap_or(false)
80 }
81
82 fn collect(&self, _matches: &ArgMatches) -> Result<Option<String>, InputError> {
83 match self.reader.var(&self.var_name) {
84 Some(value) if !value.is_empty() => Ok(Some(value)),
85 _ => Ok(None),
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::env::MockEnv;
94 use clap::Command;
95
96 fn empty_matches() -> ArgMatches {
97 Command::new("test").try_get_matches_from(["test"]).unwrap()
98 }
99
100 #[test]
101 fn env_available_when_set() {
102 let env = MockEnv::new().with_var("MY_VAR", "value");
103 let source = EnvSource::with_reader("MY_VAR", env);
104
105 assert!(source.is_available(&empty_matches()));
106 }
107
108 #[test]
109 fn env_unavailable_when_unset() {
110 let env = MockEnv::new();
111 let source = EnvSource::with_reader("MY_VAR", env);
112
113 assert!(!source.is_available(&empty_matches()));
114 }
115
116 #[test]
117 fn env_unavailable_when_empty() {
118 let env = MockEnv::new().with_var("MY_VAR", "");
119 let source = EnvSource::with_reader("MY_VAR", env);
120
121 assert!(!source.is_available(&empty_matches()));
122 }
123
124 #[test]
125 fn env_collects_value() {
126 let env = MockEnv::new().with_var("MY_VAR", "hello");
127 let source = EnvSource::with_reader("MY_VAR", env);
128
129 let result = source.collect(&empty_matches()).unwrap();
130 assert_eq!(result, Some("hello".to_string()));
131 }
132
133 #[test]
134 fn env_returns_none_when_unset() {
135 let env = MockEnv::new();
136 let source = EnvSource::with_reader("MY_VAR", env);
137
138 let result = source.collect(&empty_matches()).unwrap();
139 assert_eq!(result, None);
140 }
141
142 #[test]
143 fn env_returns_none_when_empty() {
144 let env = MockEnv::new().with_var("MY_VAR", "");
145 let source = EnvSource::with_reader("MY_VAR", env);
146
147 let result = source.collect(&empty_matches()).unwrap();
148 assert_eq!(result, None);
149 }
150}