1use indexmap::IndexMap;
8use toml::Value as TomlValue;
9
10use super::{config_from_object, AdapterError};
11use crate::value::{HoconValue, ScalarValue};
12use crate::Config;
13
14pub fn parse(input: &str, origin: Option<&str>) -> Result<Config, AdapterError> {
16 let table: toml::Table = super::strip_bom(input)
20 .parse()
21 .map_err(|e| AdapterError::new(format!("toml: {e}")))?;
22 let doc = TomlValue::Table(table);
23 Ok(config_from_object(convert(&doc, "")?, origin))
24}
25
26pub fn parse_file(path: impl AsRef<std::path::Path>) -> Result<Config, AdapterError> {
28 let path = path.as_ref();
29 let text = std::fs::read_to_string(path)
30 .map_err(|e| AdapterError::new(format!("toml: {}: {e}", path.display())))?;
31 parse(&text, Some(&path.display().to_string()))
32}
33
34fn convert(v: &TomlValue, at: &str) -> Result<HoconValue, AdapterError> {
35 match v {
36 TomlValue::Table(t) => {
37 let mut out: IndexMap<String, HoconValue> = IndexMap::new();
38 for (k, e) in t {
39 let path = if at.is_empty() {
40 k.clone()
41 } else {
42 format!("{at}.{k}")
43 };
44 out.insert(k.clone(), convert(e, &path)?);
45 }
46 Ok(HoconValue::Object(out))
47 }
48 TomlValue::Array(items) => {
49 let mut out = Vec::with_capacity(items.len());
50 for (i, e) in items.iter().enumerate() {
51 out.push(convert(e, &format!("{at}[{i}]"))?);
52 }
53 Ok(HoconValue::Array(out))
54 }
55 TomlValue::String(s) => Ok(HoconValue::Scalar(ScalarValue::string(s.clone()))),
56 TomlValue::Integer(i) => Ok(HoconValue::Scalar(ScalarValue::number(i.to_string()))),
57 TomlValue::Float(f) => {
58 if f.is_nan() || f.is_infinite() {
59 return Err(AdapterError::new(format!(
60 "toml: at {at}: {f} is not representable in HOCON (spec F0.6)"
61 )));
62 }
63 Ok(HoconValue::Scalar(ScalarValue::number(f.to_string())))
64 }
65 TomlValue::Boolean(b) => Ok(HoconValue::Scalar(ScalarValue::boolean(*b))),
66 TomlValue::Datetime(dt) => Ok(HoconValue::Scalar(ScalarValue::string(dt.to_string()))),
68 }
69}