use toml_rs::{self as toml, Value};
use crate::value::{Array, Object, UniNode, Expected};
use super::{UniNodeFormat, UniNodeFmtError};
#[derive(Clone, Copy, Default)]
pub struct TomlFormat;
impl UniNodeFormat for TomlFormat {
fn extensions(&self) -> &[&str] {
&["toml"]
}
fn parse(&self, text: &str) -> Result<UniNode, UniNodeFmtError> {
let root = toml::from_str(text)
.map_err(|e| UniNodeFmtError::Format(e.into()))?;
fn convert(node: Value) -> Result<UniNode, UniNodeFmtError> {
match node {
Value::Boolean(v) => Ok(UniNode::Boolean(v)),
Value::Integer(v) => Ok(UniNode::Integer(v)),
Value::Float(v) => Ok(UniNode::Float(v)),
Value::String(v) => Ok(UniNode::String(v)),
Value::Array(v) => {
let mut data = Array::with_capacity(v.len());
for node in v {
data.push(convert(node)?);
}
Ok(UniNode::Array(data))
},
Value::Table(v) => {
let mut data = Object::new();
for (key, node) in v {
data.insert(key, convert(node)?);
}
Ok(UniNode::Object(data))
},
Value::Datetime(v) => Ok(UniNode::String(v.to_string())),
}
}
convert(root)
}
fn build(&self, node: &UniNode) -> Result<String, UniNodeFmtError> {
fn convert(node: &UniNode) -> Result<Value, UniNodeFmtError> {
match node {
UniNode::Null => {
Err(UniNodeFmtError::NotSupportedType(Expected::Null))
},
UniNode::Boolean(v) => Ok(Value::Boolean(*v)),
UniNode::Integer(v) => Ok(Value::Integer(*v)),
UniNode::UInteger(v) => Ok(Value::Integer(i64::try_from(*v)?)),
UniNode::Float(v) => Ok(Value::Float(*v)),
UniNode::String(v) => Ok(Value::String(v.clone())),
UniNode::Bytes(_) => {
Err(UniNodeFmtError::NotSupportedType(Expected::Bytes))
},
UniNode::Array(v) => {
let mut array = toml::value::Array::with_capacity(v.len());
for sub in v {
array.push(convert(sub)?);
}
Ok(Value::Array(array))
},
UniNode::Object(v) => {
let mut table = toml::value::Table::with_capacity(v.len());
for (key, sub) in v {
table.insert(key.clone(), convert(sub)?);
}
Ok(Value::Table(table))
},
}
}
let value = convert(node)?;
toml::to_string(&value).map_err(|e| UniNodeFmtError::Format(e.into()))
}
}
pub fn parse_toml<S>(text: S) -> Result<UniNode, UniNodeFmtError>
where
S: AsRef<str>,
{
TomlFormat.parse(text.as_ref())
}
pub fn compile_toml(node: &UniNode) -> Result<String, UniNodeFmtError> {
TomlFormat.build(node)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn toml_format() {
let text = r#"
[client]
host = "localhost"
port = 404
can = 2001-12-15T02:59:43.1Z
collection = ["one", "two"]
"#;
let node = parse_toml(text).unwrap();
assert_eq!(node.find_int("client.port").unwrap(), 404);
assert_eq!(node.find_str("client.host").unwrap(), "localhost");
let text = compile_toml(&node).unwrap();
let node = parse_toml(&text).unwrap();
assert_eq!(node.find_int("client.port").unwrap(), 404);
assert_eq!(node.find_str("client.host").unwrap(), "localhost");
}
}