use crate::{AnyResult, Case, CowString, Parse, Value, DEFAULT_KEYS_SEPARATOR};
use derive_builder::Builder;
use serde_yaml::Value as YamlValue;
use std::env;
#[derive(Builder, Default)]
#[builder(setter(into, strip_option), default)]
pub struct Parser {
default_prefix: String,
prefix_option: Option<String>,
#[builder(default = "DEFAULT_KEYS_SEPARATOR.to_string()")]
keys_delimiter: String,
#[builder(default = "\"__\".to_string()")]
env_keys_delimiter: String,
case_sensitive: bool,
#[builder(setter(skip))]
value: Option<Value>,
}
impl Case for Parser {
#[inline]
fn is_case_sensitive(&self) -> bool {
self.case_sensitive
}
}
impl Parse for Parser {
fn parse(&mut self, value: &Value) -> AnyResult<Value> {
if let Some(ref v) = self.value {
return Ok(v.clone());
}
let prefix = if let Some(ref p) = self.prefix_option {
value.get_by_key_path_with_delim(p, &self.keys_delimiter)?
} else {
None
}
.unwrap_or(CowString::Borrowed(&self.default_prefix));
let case_on = self.is_case_sensitive();
let prefix = crate::normalize_case(&prefix, case_on);
let mut result = Value::with_case(case_on);
for (k, v) in env::vars_os().filter_map(|(k, v)| {
let k = k.to_string_lossy();
let norm_key = crate::normalize_case(&k, case_on);
if !norm_key.starts_with(prefix.as_ref()) {
return None;
}
Some((norm_key.into_owned(), v.to_string_lossy().to_string()))
}) {
let path = k.trim_start_matches(prefix.as_ref());
let val: YamlValue = serde_yaml::from_str(&v)?;
result.set_by_key_path_with_delim(path, &self.env_keys_delimiter, val)?;
}
self.value = Some(result.clone());
Ok(result)
}
}