use figment::{
Error, Metadata, Profile, Provider,
value::{Dict, Map, Value},
};
pub struct Empty<T> {
inner: T,
metadata: Metadata,
}
impl<T: Provider> Empty<T> {
pub fn new(inner: T) -> Self {
Self {
inner,
metadata: Metadata::named("Filter::Empty"),
}
}
pub fn named<S: Into<String>>(mut self, name: S) -> Self {
self.metadata = Metadata::named(name.into());
self
}
fn is_empty_value(value: &Value) -> bool {
match value {
Value::String(_, s) if s.is_empty() => true,
Value::Array(_, arr) if arr.is_empty() => true,
Value::Dict(_, dict) if dict.is_empty() => true,
_ => false,
}
}
fn filter_empty_values(value: Value) -> Value {
match value {
Value::Dict(tag, dict) => {
let filtered_dict: Dict = dict
.into_iter()
.filter_map(|(k, v)| {
let filtered_value = Self::filter_empty_values(v);
if Self::is_empty_value(&filtered_value) {
None } else {
Some((k, filtered_value))
}
})
.collect();
Value::Dict(tag, filtered_dict)
}
Value::Array(tag, arr) => {
let filtered_array: Vec<Value> = arr
.into_iter()
.filter_map(|v| {
let filtered_value = Self::filter_empty_values(v);
if Self::is_empty_value(&filtered_value) {
None } else {
Some(filtered_value)
}
})
.collect();
Value::Array(tag, filtered_array)
}
other => other,
}
}
}
impl<T: Provider> Provider for Empty<T> {
fn metadata(&self) -> Metadata {
self.metadata.clone()
}
fn data(&self) -> Result<Map<Profile, Map<String, Value>>, Error> {
let inner_data = self.inner.data()?;
let filtered_data: Result<Map<Profile, Map<String, Value>>, Error> = inner_data
.into_iter()
.map(|(profile, profile_data)| {
let filtered_profile_data: Map<String, Value> = profile_data
.into_iter()
.filter_map(|(key, value)| {
let filtered_value = Self::filter_empty_values(value);
if Self::is_empty_value(&filtered_value) {
None } else {
Some((key, filtered_value))
}
})
.collect();
Ok((profile, filtered_profile_data))
})
.collect();
filtered_data
}
fn profile(&self) -> Option<Profile> {
self.inner.profile()
}
}