#[cfg(not(feature = "noml"))]
use crate::error::Error;
use crate::error::Result;
use crate::value::Value;
use std::collections::BTreeMap;
#[cfg(feature = "noml")]
pub fn parse(source: &str) -> Result<Value> {
let document = noml::parse_string(source, None)?;
let mut resolver = noml::Resolver::new();
let resolved = resolver.resolve(&document)?;
convert_noml_value(resolved)
}
#[cfg(not(feature = "noml"))]
pub fn parse(_source: &str) -> Result<Value> {
Err(Error::general(
"NOML parsing requires the 'noml' feature to be enabled",
))
}
#[cfg(feature = "noml")]
fn convert_noml_value(noml_value: noml::Value) -> Result<Value> {
match noml_value {
noml::Value::Null => Ok(Value::Null),
noml::Value::Bool(b) => Ok(Value::Bool(b)),
noml::Value::Integer(i) => Ok(Value::Integer(i)),
noml::Value::Float(f) => Ok(Value::Float(f)),
noml::Value::String(s) => Ok(Value::String(s)),
noml::Value::Array(arr) => {
let converted: Result<Vec<Value>> = arr.into_iter().map(convert_noml_value).collect();
Ok(Value::Array(converted?))
}
noml::Value::Table(table) => {
let mut converted = BTreeMap::new();
for (key, value) in table {
converted.insert(key, convert_noml_value(value)?);
}
Ok(Value::Table(converted))
}
#[cfg(feature = "chrono")]
noml::Value::DateTime(_) => Ok(Value::String("datetime_value".to_string())),
noml::Value::Binary(_data) => {
#[cfg(feature = "base64")]
{
use base64::{engine::general_purpose, Engine as _};
Ok(Value::String(general_purpose::STANDARD.encode(_data)))
}
#[cfg(not(feature = "base64"))]
Ok(Value::String("<binary data>".to_string()))
}
noml::Value::Size(size) => {
Ok(Value::Integer(size as i64))
}
noml::Value::Duration(duration) => {
Ok(Value::Float(duration))
}
}
}
pub fn parse_with_preservation(source: &str) -> Result<(Value, noml::Document)> {
let document = noml::parse_string(source, None)?;
let mut resolver = noml::Resolver::new();
let resolved = resolver.resolve(&document)?;
let value = convert_noml_value(resolved)?;
Ok((value, document))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_noml() {
let config = parse(
r#"
name = "test"
port = 8080
debug = true
"#,
)
.unwrap();
assert_eq!(config.get("name").unwrap().as_string().unwrap(), "test");
assert_eq!(config.get("port").unwrap().as_integer().unwrap(), 8080);
assert!(config.get("debug").unwrap().as_bool().unwrap());
}
#[test]
fn test_noml_features() {
std::env::set_var("TEST_VAR", "hello");
let config = parse(
r#"
greeting = env("TEST_VAR", "world")
size = @size("10MB")
timeout = @duration("30s")
"#,
)
.unwrap();
assert_eq!(
config.get("greeting").unwrap().as_string().unwrap(),
"hello"
);
assert_eq!(config.get("size").unwrap().as_integer().unwrap(), 10485760);
assert_eq!(config.get("timeout").unwrap().as_float().unwrap(), 30.0);
}
}