use std::collections::HashMap;
use std::fmt::Write as _;
pub fn parse_ini(text: &str) -> HashMap<String, HashMap<String, Option<String>>> {
ini::inistr!(text)
}
pub fn stringify_ini(map: &HashMap<String, HashMap<String, Option<String>>>) -> String {
let mut out = String::new();
for (section, kv) in map {
if !section.eq_ignore_ascii_case("default") {
let _ = writeln!(&mut out, "[{}]", section);
}
for (key, value) in kv {
match value {
Some(v) => {
let _ = writeln!(&mut out, "{} = {}", key, v);
}
None => {
let _ = writeln!(&mut out, "{}", key);
}
}
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
const FIXTURE: &str = r#"
[types]
boolean = true
integer = 1
float = 3.14
string = hello
array[] = 1
array[] = 2
array[] = 3
object.key = value
null = null
date = 1979-05-27T15:32:00.000Z
"#;
#[test]
fn parse_ok() {
let map = parse_ini(FIXTURE);
assert!(map.contains_key("types"));
let types = &map["types"];
assert_eq!(
types.get("boolean").and_then(|v| v.as_deref()),
Some("true")
);
assert_eq!(types.get("integer").and_then(|v| v.as_deref()), Some("1"));
assert_eq!(types.get("float").and_then(|v| v.as_deref()), Some("3.14"));
assert_eq!(
types.get("string").and_then(|v| v.as_deref()),
Some("hello")
);
}
#[test]
fn stringify_roundtrip() {
let map = parse_ini(FIXTURE);
let out = stringify_ini(&map);
let reparsed = parse_ini(&out);
assert_eq!(reparsed, map);
}
#[test]
fn handles_default_section_without_header() {
let ini = r#"
key1 = value1
[section]
key2 = value2
"#;
let map = parse_ini(ini);
assert!(map.contains_key("default"));
let default = &map["default"];
assert_eq!(
default.get("key1").and_then(|v| v.as_deref()),
Some("value1")
);
}
}