config_lib/parsers/
toml_parser.rs1use crate::error::Result;
7use crate::value::Value;
8use std::collections::BTreeMap;
9
10#[cfg(feature = "noml")]
12pub fn parse(source: &str) -> Result<Value> {
13 let noml_value = noml::parse(source)?;
15 convert_noml_value(noml_value)
16}
17
18#[cfg(not(feature = "noml"))]
20pub fn parse(_source: &str) -> Result<Value> {
21 Err(Error::general(
22 "TOML parsing requires either the 'noml' feature or a dedicated TOML parser",
23 ))
24}
25
26#[cfg(feature = "noml")]
28pub fn parse_with_preservation(source: &str) -> Result<(Value, noml::Document)> {
29 let document = noml::parse_string(source, None)?;
31
32 let mut resolver = noml::Resolver::new();
34 let resolved = resolver.resolve(&document)?;
35
36 let value = convert_noml_value(resolved)?;
38
39 Ok((value, document))
40}
41
42#[cfg(not(feature = "noml"))]
44pub fn parse_with_preservation(_source: &str) -> Result<(Value, ())> {
45 Err(Error::general(
46 "TOML format preservation requires the 'noml' feature",
47 ))
48}
49
50#[cfg(feature = "noml")]
52fn convert_noml_value(noml_value: noml::Value) -> Result<Value> {
53 match noml_value {
54 noml::Value::Null => Ok(Value::Null),
55 noml::Value::Bool(b) => Ok(Value::Bool(b)),
56 noml::Value::Integer(i) => Ok(Value::Integer(i)),
57 noml::Value::Float(f) => Ok(Value::Float(f)),
58 noml::Value::String(s) => Ok(Value::String(s)),
59 noml::Value::Array(arr) => {
60 let converted: Result<Vec<Value>> = arr.into_iter().map(convert_noml_value).collect();
61 Ok(Value::Array(converted?))
62 }
63 noml::Value::Table(table) => {
64 let mut converted = BTreeMap::new();
65 for (key, value) in table {
66 converted.insert(key, convert_noml_value(value)?);
67 }
68 Ok(Value::Table(converted))
69 }
70 #[cfg(feature = "chrono")]
71 noml::Value::DateTime(dt) => Ok(Value::DateTime(dt)),
72 noml::Value::Binary(_) => Ok(Value::String("binary_data".to_string())),
74 noml::Value::Size(size) => Ok(Value::Integer(size as i64)),
75 noml::Value::Duration(duration) => Ok(Value::Float(duration)),
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_basic_toml() {
85 let config = parse(
86 r#"
87 name = "test"
88 port = 8080
89 debug = true
90
91 [database]
92 host = "localhost"
93 max_connections = 100
94 "#,
95 )
96 .unwrap();
97
98 assert_eq!(config.get("name").unwrap().as_string().unwrap(), "test");
99 assert_eq!(config.get("port").unwrap().as_integer().unwrap(), 8080);
100 assert_eq!(
101 config.get("database.host").unwrap().as_string().unwrap(),
102 "localhost"
103 );
104 }
105
106 #[test]
107 fn test_toml_arrays() {
108 let config = parse(
109 r#"
110 servers = ["alpha", "beta", "gamma"]
111 ports = [8001, 8002, 8003]
112 "#,
113 )
114 .unwrap();
115
116 let servers = config.get("servers").unwrap().as_array().unwrap();
117 assert_eq!(servers.len(), 3);
118 assert_eq!(servers[0].as_string().unwrap(), "alpha");
119
120 let ports = config.get("ports").unwrap().as_array().unwrap();
121 assert_eq!(ports[0].as_integer().unwrap(), 8001);
122 }
123}