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> = std::sync::Arc<dyn Fn(&T) -> Result<(), Error> + Send + Sync>;
pub(crate) enum Installer<T> {
Static {
install: fn(T, crate::ReloadReason) -> std::sync::Arc<T>,
record_failure: fn(&Error),
},
Cell(std::sync::Arc<crate::cell::ConfigCell<T>>),
}
impl<T> Installer<T> {
pub(super) fn install(&self, value: T, reason: crate::ReloadReason) -> std::sync::Arc<T> {
match self {
Self::Static { install, .. } => install(value, reason),
Self::Cell(cell) => cell.store_with(value, reason),
}
}
pub(super) fn record_failure(&self, error: &Error) {
match self {
Self::Static { record_failure, .. } => record_failure(error),
Self::Cell(cell) => cell.record_failure(error),
}
}
}
impl<T> Clone for Installer<T> {
fn clone(&self) -> Self {
match self {
Self::Static {
install,
record_failure,
} => Self::Static {
install: *install,
record_failure: *record_failure,
},
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,
whole_document: bool,
env_files: Vec<String>,
secrets_dir: Option<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,
whole_document: self.whole_document,
env_files: self.env_files.clone(),
secrets_dir: self.secrets_dir.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.clone(),
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,
whole_document: false,
env_files: Vec::new(),
secrets_dir: None,
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, crate::ReloadReason) -> std::sync::Arc<T>,
record_failure: fn(&Error),
) -> Self {
self.install = Some(Installer::Static {
install,
record_failure,
});
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
}
#[must_use]
pub fn secrets(self, secrets: &[&str]) -> Self {
self.with_secrets(secrets)
}
#[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: impl Fn(&T) -> Result<(), Error> + Send + Sync + 'static,
) -> Self {
self.validate = Some(std::sync::Arc::new(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 whole_document(mut self) -> Self {
self.whole_document = 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 secrets_dir(mut self, path: impl Into<String>) -> Self {
self.secrets_dir = Some(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_whole_document(self.whole_document)
.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);
}
if let Some(directory) = &self.secrets_dir {
spec = spec.with_secrets_dir(directory);
}
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 Builder<crate::Value> {
#[must_use]
pub fn values(key: impl Into<String>) -> Self {
Self::new(key)
}
}
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("whole_document", &self.whole_document)
.field("installs", &self.install.is_some())
.finish_non_exhaustive()
}
}