1use std::{env, path::Path};
2
3use config::{ConfigError, File, Map, Source, Value, ValueKind};
4
5#[derive(Clone, Debug, Default)]
6pub struct EnvironmentSecretFile {
7 prefix: Option<String>,
15
16 prefix_separator: Option<String>,
19
20 suffix: Option<String>,
31
32 suffix_separator: Option<String>,
35
36 separator: Option<String>,
40
41 keep_prefix: bool,
43}
44
45impl EnvironmentSecretFile {
46 pub fn with_prefix(s: &str) -> Self {
47 Self {
48 prefix: Some(s.into()),
49 ..Self::default()
50 }
51 }
52
53 pub fn prefix(mut self, s: &str) -> Self {
54 self.prefix = Some(s.into());
55 self
56 }
57
58 pub fn prefix_separator(mut self, s: &str) -> Self {
59 self.prefix_separator = Some(s.into());
60 self
61 }
62
63 pub fn suffix(mut self, s: &str) -> Self {
64 self.suffix = Some(s.into());
65 self
66 }
67
68 pub fn suffix_separator(mut self, s: &str) -> Self {
69 self.suffix_separator = Some(s.into());
70 self
71 }
72
73 pub fn separator(mut self, s: &str) -> Self {
74 self.separator = Some(s.into());
75 self
76 }
77
78 pub fn keep_prefix(mut self, keep: bool) -> Self {
79 self.keep_prefix = keep;
80 self
81 }
82}
83
84impl Source for EnvironmentSecretFile {
85 fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
86 Box::new((*self).clone())
87 }
88
89 fn collect(&self) -> Result<Map<String, Value>, ConfigError> {
90 let mut m = Map::new();
91
92 let separator = self.separator.as_deref().unwrap_or("");
93 let prefix_separator = match (self.prefix_separator.as_deref(), self.separator.as_deref()) {
94 (Some(pre), _) => pre,
95 (None, Some(sep)) => sep,
96 (None, None) => "_",
97 };
98 let suffix_separator = match (self.suffix_separator.as_deref(), self.separator.as_deref()) {
99 (Some(suf), _) => suf,
100 (None, Some(sep)) => sep,
101 (None, None) => "_",
102 };
103
104 let prefix_pattern = self
105 .prefix
106 .as_ref()
107 .map(|prefix| format!("{}{}", prefix, prefix_separator).to_lowercase());
108
109 let suffix = self.suffix.as_ref().map_or_else(|| "FILE", |s| s.as_str());
110 let suffix_pattern = format!("{}{}", suffix_separator, suffix).to_lowercase();
111
112 let full_pattern = if let Some(prefix) = self.prefix.as_ref() {
113 if prefix_separator == suffix_separator {
114 format!("{}{}{}", prefix, prefix_separator, suffix).to_lowercase()
115 } else {
116 format!("{}{}", prefix, suffix)
117 }
118 } else {
119 suffix.to_string()
120 };
121
122 let mut error: Option<ConfigError> = None;
123
124 env::vars().for_each(|(key, value): (String, String)| {
125 if error.as_ref().is_some() {
127 return;
128 }
129
130 if value.is_empty() {
132 return;
133 }
134
135 let mut key = key.to_lowercase();
136
137 if key == full_pattern {
138 let path = Path::new(&value);
139 let file = File::from(path);
140 let map = file.collect();
141
142 match map {
143 Ok(map) => {
144 for (key, value) in map.into_iter() {
145 m.insert(key, value);
146 }
147 }
148 Err(err) => {
149 error = Some(err);
150 }
151 }
152
153 return;
154 }
155
156 if let Some(ref prefix_pattern) = prefix_pattern {
158 if key.starts_with(prefix_pattern) {
159 if !self.keep_prefix {
160 key = key[prefix_pattern.len()..].to_string();
162 }
163 } else {
164 return;
166 }
167 }
168
169 if key.ends_with(&suffix_pattern) {
171 let len = key.len() - suffix_pattern.len();
173 key = key[..len].to_string();
174 } else {
175 return;
177 }
178
179 if !separator.is_empty() {
181 key = key.replace(separator, ".");
182 }
183
184 let path = Path::new(&value);
185 let file = File::from(path);
186 let map = file.collect();
187
188 match map {
189 Ok(map) => {
190 let uri = format!("secret:{}:{}", key, value);
191 m.insert(key, Value::new(Some(&uri), ValueKind::Table(map)));
192 }
193 Err(err) => {
194 error = Some(err);
195 }
196 }
197 });
198
199 match error {
200 Some(err) => Err(err),
201 None => Ok(m),
202 }
203 }
204}