| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
use std::collections::BTreeMap; |
| 5 |
|
| 6 |
pub type Sections = BTreeMap<String, BTreeMap<String, String>>; |
| 7 |
|
| 8 |
pub fn parse(text: &str) -> Sections { |
| 9 |
let mut out = Sections::new(); |
| 10 |
let mut current: Option<String> = None; |
| 11 |
for raw in text.lines() { |
| 12 |
let line = raw.trim(); |
| 13 |
if line.is_empty() || line.starts_with('#') || line.starts_with(';') { |
| 14 |
continue; |
| 15 |
} |
| 16 |
if let Some(name) = line.strip_prefix('[').and_then(|l| l.strip_suffix(']')) { |
| 17 |
current = Some(name.trim().to_string()); |
| 18 |
out.entry(name.trim().to_string()).or_default(); |
| 19 |
continue; |
| 20 |
} |
| 21 |
if let (Some(section), Some((k, v))) = (¤t, line.split_once('=')) { |
| 22 |
out.entry(section.clone()) |
| 23 |
.or_default() |
| 24 |
.insert(k.trim().to_string(), v.trim().to_string()); |
| 25 |
} |
| 26 |
} |
| 27 |
out |
| 28 |
} |
| 29 |
|
| 30 |
#[cfg(test)] |
| 31 |
mod tests { |
| 32 |
use super::parse; |
| 33 |
|
| 34 |
#[test] |
| 35 |
fn sections_and_keys() { |
| 36 |
let s = parse("[default]\nregion = eu-west-2\n\n# a comment\n[profile work]\nregion=us-east-1\nsso_start_url = https://x\n"); |
| 37 |
assert_eq!(s["default"]["region"], "eu-west-2"); |
| 38 |
assert_eq!(s["profile work"]["region"], "us-east-1"); |
| 39 |
assert_eq!(s["profile work"]["sso_start_url"], "https://x"); |
| 40 |
assert!(parse("").is_empty()); |
| 41 |
} |
| 42 |
} |