use indexmap::IndexMap;
use toml::Value as TomlValue;
use super::{config_from_object, AdapterError};
use crate::value::{HoconValue, ScalarValue};
use crate::Config;
pub fn parse(input: &str, origin: Option<&str>) -> Result<Config, AdapterError> {
let table: toml::Table = super::strip_bom(input)
.parse()
.map_err(|e| AdapterError::new(format!("toml: {e}")))?;
let doc = TomlValue::Table(table);
Ok(config_from_object(convert(&doc, "")?, origin))
}
pub fn parse_file(path: impl AsRef<std::path::Path>) -> Result<Config, AdapterError> {
let path = path.as_ref();
let text = std::fs::read_to_string(path)
.map_err(|e| AdapterError::new(format!("toml: {}: {e}", path.display())))?;
parse(&text, Some(&path.display().to_string()))
}
fn convert(v: &TomlValue, at: &str) -> Result<HoconValue, AdapterError> {
match v {
TomlValue::Table(t) => {
let mut out: IndexMap<String, HoconValue> = IndexMap::new();
for (k, e) in t {
let path = if at.is_empty() {
k.clone()
} else {
format!("{at}.{k}")
};
out.insert(k.clone(), convert(e, &path)?);
}
Ok(HoconValue::Object(out))
}
TomlValue::Array(items) => {
let mut out = Vec::with_capacity(items.len());
for (i, e) in items.iter().enumerate() {
out.push(convert(e, &format!("{at}[{i}]"))?);
}
Ok(HoconValue::Array(out))
}
TomlValue::String(s) => Ok(HoconValue::Scalar(ScalarValue::string(s.clone()))),
TomlValue::Integer(i) => Ok(HoconValue::Scalar(ScalarValue::number(i.to_string()))),
TomlValue::Float(f) => {
if f.is_nan() || f.is_infinite() {
return Err(AdapterError::new(format!(
"toml: at {at}: {f} is not representable in HOCON (spec F0.6)"
)));
}
Ok(HoconValue::Scalar(ScalarValue::number(f.to_string())))
}
TomlValue::Boolean(b) => Ok(HoconValue::Scalar(ScalarValue::boolean(*b))),
TomlValue::Datetime(dt) => Ok(HoconValue::Scalar(ScalarValue::string(dt.to_string()))),
}
}