use crate::config::Config;
use indexmap::IndexMap;
#[cfg(feature = "serde")]
use crate::error::ConfigError;
#[cfg(feature = "serde")]
use crate::value::{HoconValue, ScalarType, ScalarValue};
pub fn empty(origin_description: Option<&str>) -> Config {
Config::new_with_meta(IndexMap::new(), origin_description.map(|s| s.to_owned()))
}
#[cfg(feature = "serde")]
pub fn from_map(
values: serde_json::Map<String, serde_json::Value>,
origin_description: Option<&str>,
) -> Result<Config, ConfigError> {
let root = coerce_map(values)?;
Ok(Config::new_with_meta(
root,
origin_description.map(|s| s.to_owned()),
))
}
#[cfg(feature = "serde")]
fn coerce_map(
map: serde_json::Map<String, serde_json::Value>,
) -> Result<IndexMap<String, HoconValue>, ConfigError> {
let mut keys: Vec<String> = map.keys().cloned().collect();
keys.sort();
let mut result = IndexMap::new();
for k in keys {
let v = map.get(&k).unwrap().clone();
let hv = coerce_value(v).map_err(|msg| ConfigError {
path: k.clone(),
message: msg,
})?;
result.insert(k, hv);
}
Ok(result)
}
#[cfg(feature = "serde")]
fn coerce_value(v: serde_json::Value) -> Result<HoconValue, String> {
use serde_json::Value;
match v {
Value::Null => Ok(HoconValue::Scalar(ScalarValue {
raw: "null".to_owned(),
value_type: ScalarType::Null,
})),
Value::Bool(b) => Ok(HoconValue::Scalar(ScalarValue {
raw: if b { "true" } else { "false" }.to_owned(),
value_type: ScalarType::Boolean,
})),
Value::String(s) => Ok(HoconValue::Scalar(ScalarValue {
raw: s,
value_type: ScalarType::String,
})),
Value::Number(n) => {
if n.is_i64() || n.is_u64() {
Ok(HoconValue::Scalar(ScalarValue {
raw: n.to_string(),
value_type: ScalarType::Number,
}))
} else if let Some(f) = n.as_f64() {
if !f.is_finite() {
return Err(format!(
"number {} is not finite (NaN/Inf not representable in HOCON)",
n
));
}
Ok(HoconValue::Scalar(ScalarValue {
raw: n.to_string(),
value_type: ScalarType::Number,
}))
} else {
Err(format!(
"number {} cannot be represented as i64, u64, or f64",
n
))
}
}
Value::Array(arr) => {
let mut items = Vec::with_capacity(arr.len());
for (i, elem) in arr.into_iter().enumerate() {
let hv = coerce_value(elem).map_err(|msg| format!("element[{}]: {}", i, msg))?;
items.push(hv);
}
Ok(HoconValue::Array(items))
}
Value::Object(obj) => {
let inner = coerce_map(obj).map_err(|e| e.message)?;
Ok(HoconValue::Object(inner))
}
}
}