use crate::engine::{Engine, Folded, Layer};
use crate::error::{Error, ErrorKind};
use crate::value::Value;
use std::collections::BTreeMap;
#[derive(Debug)]
pub(crate) struct Figment;
impl Engine for Figment {
fn name(&self) -> &str {
"figment"
}
fn fold(&self, layers: &[Layer<'_>]) -> Result<Folded, Error> {
let mut figment = figment::Figment::new();
for layer in layers {
let dict: figment::value::Dict = match layer.values {
Value::Table(table) => table
.iter()
.map(|(key, value)| (key.clone(), super::to_figment(value)))
.collect(),
_ => figment::value::Dict::new(),
};
figment = figment.merge(Tagged {
tag: layer.tag,
dict,
});
}
let merged: figment::value::Dict = figment.extract().map_err(|_| {
Error::new(
ErrorKind::Backend,
"the resolution engine refused the layers it was given",
)
})?;
let mut values = BTreeMap::new();
let mut tags = BTreeMap::new();
for (key, value) in &merged {
let mut path = vec![key.clone()];
values.insert(key.clone(), unpack(&figment, value, &mut path, &mut tags));
}
Ok(Folded {
values: Value::Table(values),
tags,
})
}
}
struct Tagged {
tag: usize,
dict: figment::value::Dict,
}
impl figment::Provider for Tagged {
fn metadata(&self) -> figment::Metadata {
figment::Metadata::named(self.tag.to_string())
}
fn data(&self) -> figment::Result<figment::value::Map<figment::Profile, figment::value::Dict>> {
Ok(figment::value::Map::from([(
figment::Profile::Default,
self.dict.clone(),
)]))
}
}
fn unpack(
figment: &figment::Figment,
value: &figment::value::Value,
path: &mut Vec<String>,
tags: &mut BTreeMap<String, usize>,
) -> Value {
if let figment::value::Value::Dict(_, dict) = value {
if dict.is_empty() {
remember(figment, path, tags);
return Value::Table(BTreeMap::new());
}
let mut values = BTreeMap::new();
for (key, value) in dict {
path.push(key.clone());
values.insert(key.clone(), unpack(figment, value, path, tags));
path.pop();
}
return Value::Table(values);
}
remember(figment, path, tags);
super::from_figment(value)
}
fn remember(figment: &figment::Figment, path: &[String], tags: &mut BTreeMap<String, usize>) {
let dotted = path.join(".");
if let Some(tag) = figment
.find_metadata(&dotted)
.and_then(|metadata| metadata.name.parse().ok())
{
tags.insert(dotted, tag);
}
}