Skip to main content

config_get/
value.rs

1use indexmap::IndexMap;
2use serde::{Deserialize, Serialize};
3
4/// Internal data model for configuration values.
5///
6/// Supports two access patterns:
7/// - **Flat** — top-level `KEY = value` (`.env`, root of JSON/TOML/YAML)
8/// - **Sectioned** — `[section] key = value` (INI, nested TOML/JSON/YAML)
9#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10pub struct ConfigMap {
11    flat: IndexMap<String, String>,
12    sections: IndexMap<String, IndexMap<String, String>>,
13}
14
15impl ConfigMap {
16    /// Create an empty map.
17    #[must_use]
18    pub fn new() -> Self {
19        Self::default()
20    }
21
22    // ── insert ────────────────────────────────────────────────────────────────
23
24    /// Insert a flat (top-level) key/value pair.
25    pub fn insert_flat(&mut self, key: String, value: String) {
26        self.flat.insert(key, value);
27    }
28
29    /// Insert a sectioned key/value pair.
30    pub fn insert_sectioned(&mut self, section: String, key: String, value: String) {
31        self.sections.entry(section).or_default().insert(key, value);
32    }
33
34    // ── get ───────────────────────────────────────────────────────────────────
35
36    /// Look up a flat key.
37    pub fn get_flat(&self, key: &str) -> Option<&str> {
38        self.flat.get(key).map(String::as_str)
39    }
40
41    /// Look up `key` inside `section`.
42    pub fn get_in_section(&self, section: &str, key: &str) -> Option<&str> {
43        self.sections
44            .get(section)
45            .and_then(|s| s.get(key))
46            .map(String::as_str)
47    }
48
49    /// Return all key/value pairs for `section`.
50    #[must_use]
51    pub fn get_section(&self, section: &str) -> Option<&IndexMap<String, String>> {
52        self.sections.get(section)
53    }
54
55    /// True if `section` exists.
56    #[must_use]
57    pub fn has_section(&self, section: &str) -> bool {
58        self.sections.contains_key(section)
59    }
60
61    // ── iteration ─────────────────────────────────────────────────────────────
62
63    /// All flat key/value pairs in insertion order.
64    pub fn flat_iter(&self) -> impl Iterator<Item = (&str, &str)> {
65        self.flat.iter().map(|(k, v)| (k.as_str(), v.as_str()))
66    }
67
68    /// All sections, each yielding its key/value pairs.
69    pub fn sections(&self) -> impl Iterator<Item = (&str, &IndexMap<String, String>)> {
70        self.sections.iter().map(|(k, v)| (k.as_str(), v))
71    }
72
73    /// Total number of top-level keys (flat + section names).
74    #[must_use]
75    pub fn len(&self) -> usize {
76        self.flat.len() + self.sections.len()
77    }
78
79    /// True if there are no flat keys and no sections.
80    #[must_use]
81    pub fn is_empty(&self) -> bool {
82        self.flat.is_empty() && self.sections.is_empty()
83    }
84
85    /// True if the flat map contains `key` or any section contains `key`.
86    #[must_use]
87    pub fn contains_key(&self, key: &str) -> bool {
88        if self.flat.contains_key(key) {
89            return true;
90        }
91        self.sections.values().any(|s| s.contains_key(key))
92    }
93}