1mod configured;
35mod diagnostics;
36mod lifecycle;
37#[cfg(feature = "watch")]
38mod watching;
39
40pub use configured::Configured;
41
42use std::marker::PhantomData;
43use std::path::Path;
44
45use serde::de::DeserializeOwned;
46
47use crate::cache::CacheMode;
48use crate::error::Error;
49use crate::source::{Format, LoadSpec, Source};
50
51type Validator<T> = fn(&T) -> Result<(), Error>;
53
54pub struct Builder<T> {
65 key: String,
66 files: Vec<(String, bool)>,
67 env: Option<String>,
68 nest: Option<String>,
69 allow_empty_env: bool,
70 strict_env: bool,
71 env_files: Vec<String>,
72 profile_env: Option<String>,
73 search: Option<(String, Vec<String>)>,
74 cache: Option<(String, CacheMode)>,
75 secrets: Option<Vec<String>>,
78 validate: Option<Validator<T>>,
79 fields: &'static [&'static str],
80 install: Option<fn(T)>,
81 register: Option<fn(&Self)>,
85 defaults: Option<&'static crate::Layer>,
86 overrides: Option<&'static crate::Layer>,
87 flags: Option<&'static crate::Layer>,
88 bindings: Option<&'static crate::EnvBindings>,
89 aliases: Option<&'static crate::Aliases>,
90 remote: Option<&'static crate::Remote>,
91 _marker: PhantomData<fn() -> T>,
92}
93
94impl<T> Clone for Builder<T> {
95 fn clone(&self) -> Self {
96 Self {
97 key: self.key.clone(),
98 files: self.files.clone(),
99 env: self.env.clone(),
100 nest: self.nest.clone(),
101 allow_empty_env: self.allow_empty_env,
102 strict_env: self.strict_env,
103 env_files: self.env_files.clone(),
104 profile_env: self.profile_env.clone(),
105 search: self.search.clone(),
106 cache: self.cache.clone(),
107 secrets: self.secrets.clone(),
108 validate: self.validate,
109 fields: self.fields,
110 install: self.install,
111 register: self.register,
112 defaults: self.defaults,
113 overrides: self.overrides,
114 flags: self.flags,
115 bindings: self.bindings,
116 aliases: self.aliases,
117 remote: self.remote,
118 _marker: PhantomData,
119 }
120 }
121}
122
123impl<T: DeserializeOwned> Builder<T> {
124 #[must_use]
129 pub fn new(key: impl Into<String>) -> Self {
130 Self {
131 key: key.into(),
132 files: Vec::new(),
133 env: None,
134 nest: None,
135 allow_empty_env: false,
136 strict_env: false,
137 env_files: Vec::new(),
138 profile_env: None,
139 search: None,
140 cache: None,
141 secrets: None,
142 validate: None,
143 fields: &[],
144 install: None,
145 register: None,
146 defaults: None,
147 overrides: None,
148 flags: None,
149 bindings: None,
150 aliases: None,
151 remote: None,
152 _marker: PhantomData,
153 }
154 }
155
156 #[doc(hidden)]
158 #[must_use]
159 pub fn with_installer(mut self, install: fn(T)) -> Self {
160 self.install = Some(install);
161 self
162 }
163
164 #[doc(hidden)]
167 #[must_use]
168 pub fn with_secrets(mut self, secrets: &[&str]) -> Self {
169 self.secrets = Some(secrets.iter().map(|name| (*name).to_owned()).collect());
170 self
171 }
172
173 #[doc(hidden)]
176 #[must_use]
177 #[allow(clippy::too_many_arguments)]
178 pub fn with_type_statics(
179 mut self,
180 defaults: &'static crate::Layer,
181 overrides: &'static crate::Layer,
182 flags: &'static crate::Layer,
183 bindings: &'static crate::EnvBindings,
184 aliases: &'static crate::Aliases,
185 remote: &'static crate::Remote,
186 register: fn(&Self),
187 ) -> Self {
188 self.defaults = Some(defaults);
189 self.overrides = Some(overrides);
190 self.flags = Some(flags);
191 self.bindings = Some(bindings);
192 self.aliases = Some(aliases);
193 self.remote = Some(remote);
194 self.register = Some(register);
195 self
196 }
197
198 #[must_use]
203 pub fn validate(mut self, check: Validator<T>) -> Self {
204 self.validate = Some(check);
205 self
206 }
207
208 #[must_use]
213 pub fn file(mut self, path: impl Into<String>) -> Self {
214 self.files.push((path.into(), false));
215 self
216 }
217
218 #[cfg(feature = "decrypt")]
223 #[cfg_attr(docsrs, doc(cfg(feature = "decrypt")))]
224 #[must_use]
225 pub fn encrypted_file(mut self, path: impl Into<String>) -> Self {
226 self.files.push((path.into(), true));
227 self
228 }
229
230 #[must_use]
232 pub fn env(mut self, prefix: impl Into<String>) -> Self {
233 self.env = Some(prefix.into());
234 self
235 }
236
237 #[must_use]
239 pub fn nest(mut self, separator: impl Into<String>) -> Self {
240 self.nest = Some(separator.into());
241 self
242 }
243
244 #[must_use]
246 pub fn allow_empty_env(mut self) -> Self {
247 self.allow_empty_env = true;
248 self
249 }
250
251 #[must_use]
254 pub fn strict_env(mut self) -> Self {
255 self.strict_env = true;
256 self
257 }
258
259 #[must_use]
261 pub fn env_file(mut self, path: impl Into<String>) -> Self {
262 self.env_files.push(path.into());
263 self
264 }
265
266 #[must_use]
268 pub fn profile_env(mut self, variable: impl Into<String>) -> Self {
269 self.profile_env = Some(variable.into());
270 self
271 }
272
273 #[must_use]
277 pub fn discover(
278 mut self,
279 name: impl Into<String>,
280 paths: impl IntoIterator<Item = impl Into<String>>,
281 ) -> Self {
282 self.search = Some((name.into(), paths.into_iter().map(Into::into).collect()));
283 self
284 }
285
286 #[must_use]
294 pub fn cache(mut self, path: impl Into<String>, mode: CacheMode) -> Self {
295 self.cache = Some((path.into(), mode));
296 self
297 }
298
299 #[doc(hidden)]
302 #[must_use]
303 pub fn with_fields(mut self, fields: &'static [&'static str]) -> Self {
304 self.fields = fields;
305 self
306 }
307
308 fn with_spec<R>(
313 &self,
314 operation: impl FnOnce(&LoadSpec<'_>) -> Result<R, Error>,
315 ) -> Result<R, Error> {
316 let sources = self
317 .files
318 .iter()
319 .map(|(file, encrypted)| {
320 Format::from_path(Path::new(file)).map(|format| {
321 if *encrypted {
322 Source::encrypted(file, format)
323 } else {
324 Source::file(file, format)
325 }
326 })
327 })
328 .collect::<Result<Vec<_>, _>>()?;
329 let env_files: Vec<&str> = self.env_files.iter().map(String::as_str).collect();
330
331 let mut spec = LoadSpec::new(&self.key, &sources)
332 .with_empty_env(self.allow_empty_env)
333 .with_strict_env(self.strict_env)
334 .with_env_files(&env_files);
335
336 if let Some(prefix) = &self.env {
337 spec = spec.with_env(prefix);
338 }
339 if let Some(separator) = &self.nest {
340 spec = spec.with_nest(separator);
341 }
342 if let Some(variable) = &self.profile_env {
343 spec = spec.with_profile_env(variable);
344 }
345
346 let search_paths: Vec<&str>;
347 if let Some((name, paths)) = &self.search {
348 search_paths = paths.iter().map(String::as_str).collect();
349 spec = spec.with_search(name, &search_paths);
350 }
351
352 if let Some(layer) = self.defaults {
353 spec = spec.with_defaults(layer);
354 }
355 if let Some(layer) = self.overrides {
356 spec = spec.with_overrides(layer);
357 }
358 if let Some(layer) = self.flags {
359 spec = spec.with_flags(layer);
360 }
361 if let Some(bindings) = self.bindings {
362 spec = spec.with_env_bindings(bindings);
363 }
364 if let Some(aliases) = self.aliases {
365 spec = spec.with_aliases(aliases);
366 }
367 if let Some(remote) = self.remote {
368 spec = spec.with_remote(remote);
369 }
370
371 operation(&spec)
372 }
373}
374
375impl<T> std::fmt::Debug for Builder<T> {
376 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
377 f.debug_struct("Builder")
378 .field("key", &self.key)
379 .field("files", &self.files)
380 .field("env", &self.env)
381 .field("env_files", &self.env_files)
382 .field("strict_env", &self.strict_env)
383 .field("installs", &self.install.is_some())
384 .finish_non_exhaustive()
385 }
386}