mod configured;
mod diagnostics;
mod lifecycle;
#[cfg(feature = "watch")]
mod watching;
pub use configured::Configured;
use std::marker::PhantomData;
use std::path::Path;
use serde::de::DeserializeOwned;
use crate::cache::CacheMode;
use crate::error::Error;
use crate::source::{Format, LoadSpec, Source};
type Validator<T> = fn(&T) -> Result<(), Error>;
pub(crate) enum Installer<T> {
Static(fn(T)),
Cell(std::sync::Arc<crate::cell::ConfigCell<T>>),
}
impl<T> Installer<T> {
pub(super) fn install(&self, value: T) {
match self {
Self::Static(install) => install(value),
Self::Cell(cell) => cell.store(value),
}
}
}
impl<T> Clone for Installer<T> {
fn clone(&self) -> Self {
match self {
Self::Static(install) => Self::Static(*install),
Self::Cell(cell) => Self::Cell(std::sync::Arc::clone(cell)),
}
}
}
pub struct Builder<T> {
key: String,
files: Vec<(String, bool)>,
env: Option<String>,
nest: Option<String>,
allow_empty_env: bool,
strict_env: bool,
env_files: Vec<String>,
profile_env: Option<String>,
search: Option<(String, Vec<String>)>,
cache: Option<(String, CacheMode)>,
#[cfg(feature = "decrypt")]
cache_encryptor: Option<std::sync::Arc<dyn crate::Encryptor>>,
secrets: Option<Vec<String>>,
validate: Option<Validator<T>>,
fields: &'static [&'static str],
install: Option<Installer<T>>,
register: Option<fn(&Self)>,
defaults: Option<&'static crate::Layer>,
overrides: Option<&'static crate::Layer>,
flags: Option<&'static crate::Layer>,
bindings: Option<&'static crate::EnvBindings>,
aliases: Option<&'static crate::Aliases>,
remote: Option<&'static crate::Remote>,
_marker: PhantomData<fn() -> T>,
}
impl<T> Clone for Builder<T> {
fn clone(&self) -> Self {
Self {
key: self.key.clone(),
files: self.files.clone(),
env: self.env.clone(),
nest: self.nest.clone(),
allow_empty_env: self.allow_empty_env,
strict_env: self.strict_env,
env_files: self.env_files.clone(),
profile_env: self.profile_env.clone(),
search: self.search.clone(),
cache: self.cache.clone(),
#[cfg(feature = "decrypt")]
cache_encryptor: self.cache_encryptor.clone(),
secrets: self.secrets.clone(),
validate: self.validate,
fields: self.fields,
install: self.install.clone(),
register: self.register,
defaults: self.defaults,
overrides: self.overrides,
flags: self.flags,
bindings: self.bindings,
aliases: self.aliases,
remote: self.remote,
_marker: PhantomData,
}
}
}
impl<T: DeserializeOwned> Builder<T> {
#[must_use]
pub fn new(key: impl Into<String>) -> Self {
Self {
key: key.into(),
files: Vec::new(),
env: None,
nest: None,
allow_empty_env: false,
strict_env: false,
env_files: Vec::new(),
profile_env: None,
search: None,
cache: None,
#[cfg(feature = "decrypt")]
cache_encryptor: None,
secrets: None,
validate: None,
fields: &[],
install: None,
register: None,
defaults: None,
overrides: None,
flags: None,
bindings: None,
aliases: None,
remote: None,
_marker: PhantomData,
}
}
#[doc(hidden)]
#[must_use]
pub fn with_installer(mut self, install: fn(T)) -> Self {
self.install = Some(Installer::Static(install));
self
}
pub(crate) fn with_cell(mut self, cell: std::sync::Arc<crate::cell::ConfigCell<T>>) -> Self {
self.install = Some(Installer::Cell(cell));
self.register = None;
self
}
#[doc(hidden)]
#[must_use]
pub fn with_secrets(mut self, secrets: &[&str]) -> Self {
self.secrets = Some(secrets.iter().map(|name| (*name).to_owned()).collect());
self
}
#[doc(hidden)]
#[must_use]
#[allow(clippy::too_many_arguments)]
pub fn with_type_statics(
mut self,
defaults: &'static crate::Layer,
overrides: &'static crate::Layer,
flags: &'static crate::Layer,
bindings: &'static crate::EnvBindings,
aliases: &'static crate::Aliases,
remote: &'static crate::Remote,
register: fn(&Self),
) -> Self {
self.defaults = Some(defaults);
self.overrides = Some(overrides);
self.flags = Some(flags);
self.bindings = Some(bindings);
self.aliases = Some(aliases);
self.remote = Some(remote);
self.register = Some(register);
self
}
#[must_use]
pub fn key(&self) -> &str {
&self.key
}
#[must_use]
pub fn validate(mut self, check: Validator<T>) -> Self {
self.validate = Some(check);
self
}
#[must_use]
pub fn file(mut self, path: impl Into<String>) -> Self {
self.files.push((path.into(), false));
self
}
#[cfg(feature = "decrypt")]
#[cfg_attr(docsrs, doc(cfg(feature = "decrypt")))]
#[must_use]
pub fn encrypted_file(mut self, path: impl Into<String>) -> Self {
self.files.push((path.into(), true));
self
}
#[must_use]
pub fn env(mut self, prefix: impl Into<String>) -> Self {
self.env = Some(prefix.into());
self
}
#[must_use]
pub fn nest(mut self, separator: impl Into<String>) -> Self {
self.nest = Some(separator.into());
self
}
#[must_use]
pub fn allow_empty_env(mut self) -> Self {
self.allow_empty_env = true;
self
}
#[must_use]
pub fn strict_env(mut self) -> Self {
self.strict_env = true;
self
}
#[must_use]
pub fn env_file(mut self, path: impl Into<String>) -> Self {
self.env_files.push(path.into());
self
}
#[must_use]
pub fn profile_env(mut self, variable: impl Into<String>) -> Self {
self.profile_env = Some(variable.into());
self
}
#[must_use]
pub fn discover(
mut self,
name: impl Into<String>,
paths: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
self.search = Some((name.into(), paths.into_iter().map(Into::into).collect()));
self
}
#[must_use]
pub fn cache(mut self, path: impl Into<String>, mode: CacheMode) -> Self {
self.cache = Some((path.into(), mode));
#[cfg(feature = "decrypt")]
{
self.cache_encryptor = None;
}
self
}
#[cfg(feature = "decrypt")]
#[cfg_attr(docsrs, doc(cfg(feature = "decrypt")))]
#[must_use]
pub fn cache_encrypted(
mut self,
path: impl Into<String>,
encryptor: impl crate::Encryptor + 'static,
) -> Self {
self.cache = Some((path.into(), CacheMode::Full));
self.cache_encryptor = Some(std::sync::Arc::new(encryptor));
self
}
#[doc(hidden)]
#[must_use]
pub fn with_fields(mut self, fields: &'static [&'static str]) -> Self {
self.fields = fields;
self
}
fn with_spec<R>(
&self,
operation: impl FnOnce(&LoadSpec<'_>) -> Result<R, Error>,
) -> Result<R, Error> {
let sources = self
.files
.iter()
.map(|(file, encrypted)| {
Format::from_path(Path::new(file)).map(|format| {
if *encrypted {
Source::encrypted(file, format)
} else {
Source::file(file, format)
}
})
})
.collect::<Result<Vec<_>, _>>()?;
let env_files: Vec<&str> = self.env_files.iter().map(String::as_str).collect();
let mut spec = LoadSpec::new(&self.key, &sources)
.with_empty_env(self.allow_empty_env)
.with_strict_env(self.strict_env)
.with_env_files(&env_files);
if let Some(prefix) = &self.env {
spec = spec.with_env(prefix);
}
if let Some(separator) = &self.nest {
spec = spec.with_nest(separator);
}
if let Some(variable) = &self.profile_env {
spec = spec.with_profile_env(variable);
}
let search_paths: Vec<&str>;
if let Some((name, paths)) = &self.search {
search_paths = paths.iter().map(String::as_str).collect();
spec = spec.with_search(name, &search_paths);
}
if let Some(layer) = self.defaults {
spec = spec.with_defaults(layer);
}
if let Some(layer) = self.overrides {
spec = spec.with_overrides(layer);
}
if let Some(layer) = self.flags {
spec = spec.with_flags(layer);
}
if let Some(bindings) = self.bindings {
spec = spec.with_env_bindings(bindings);
}
if let Some(aliases) = self.aliases {
spec = spec.with_aliases(aliases);
}
if let Some(remote) = self.remote {
spec = spec.with_remote(remote);
}
operation(&spec)
}
}
impl<T> std::fmt::Debug for Builder<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Builder")
.field("key", &self.key)
.field("files", &self.files)
.field("env", &self.env)
.field("env_files", &self.env_files)
.field("strict_env", &self.strict_env)
.field("installs", &self.install.is_some())
.finish_non_exhaustive()
}
}