use crate::env_vars::StringWithEnv;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fmt::Formatter;
use std::{env, fmt};
#[derive(Debug, thiserror::Error)]
pub struct EnvVarDefaultsError(BTreeMap<String, StringWithEnv>);
impl fmt::Display for EnvVarDefaultsError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
assert!(!self.0.is_empty());
write!(f, "Could not apply environment variable defaults. One or more default values requires an unset variable.")?;
for (var, value) in &self.0 {
write!(f, "\n{var}: {value:?}")?;
}
Ok(())
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(transparent)]
#[repr(transparent)]
#[allow(clippy::module_name_repetitions)]
pub struct EnvVarDefaults(BTreeMap<String, StringWithEnv>);
impl EnvVarDefaults {
pub fn insert(&mut self, var: String, value: StringWithEnv) -> Option<StringWithEnv> {
self.0.insert(var, value)
}
pub fn merge_with(&mut self, other: Self) {
for (var, value) in other.0 {
self.0.insert(var, value);
}
}
pub fn apply(self) -> Result<(), EnvVarDefaultsError> {
let mut remaining_last_loop = self.0.len() + 1;
let mut this_loop = self.0;
while remaining_last_loop != this_loop.len() {
let last_loop = std::mem::take(&mut this_loop);
remaining_last_loop = last_loop.len();
for (var, value) in last_loop {
if env::var_os(&var).is_none() {
match value.clone().process() {
Err(_) => _ = this_loop.insert(var, value),
Ok(value) => {
env::set_var(var, value);
}
}
}
}
}
if this_loop.is_empty() {
Ok(())
} else {
Err(EnvVarDefaultsError(this_loop))
}
}
}