1use crate::{context, path, Configuration, Settings};
2use std::fmt::{self, Debug, Display, Formatter, Write};
3use tokens::{ChangeToken, NeverChangeToken};
4
5macro_rules! section {
6 ($self:ident) => {
7 #[inline]
9 pub fn key(&$self) -> &str {
10 path::last(&$self.path)
11 }
12
13 #[inline]
15 pub fn path(&$self) -> &str {
16 &$self.path
17 }
18
19 #[inline]
21 pub fn value(&$self) -> &str {
22 $self.config.settings.get(&$self.path).unwrap_or_default()
23 }
24
25 pub fn exists(&$self) -> bool {
31 !$self.value().is_empty() || !$self.sections().is_empty()
32 }
33
34 #[inline]
40 pub fn get(&$self, key: &str) -> Option<&str> {
41 $self.config.settings.get_subkey(&$self.path, key)
42 }
43 };
44}
45
46macro_rules! diagnostic {
47 ($self:ty, $name:literal) => {
48 impl Debug for $self {
49 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
50 f.debug_struct($name)
51 .field("key", &self.key())
52 .field("path", &self.path())
53 .field("value", &self.value())
54 .field("exists", &self.exists())
55 .finish()
56 }
57 }
58
59 impl Display for $self {
60 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
61 let (value, providers) = self.config.settings.get_with_id(&self.path).unwrap_or_default();
62
63 f.write_str(self.key())?;
64
65 if value.is_empty() {
66 f.write_char(':')?;
67 } else {
68 f.write_str(" = ")?;
69 f.write_str(value)?;
70 }
71
72 if providers > 0 && !self.config.providers.is_empty() && f.alternate() {
73 f.write_str(" (")?;
74 context::expand(providers, &self.config.providers, f)?;
75 f.write_char(')')?;
76 }
77
78 Ok(())
79 }
80 }
81 };
82}
83
84fn collect_section_keys(config: &Configuration, parent: &str) -> Vec<String> {
85 let mut keys = Vec::new();
86
87 for (path, _) in config {
88 let Some(key) = path::next(path, Some(parent)) else {
89 continue;
90 };
91
92 if !keys.iter().any(|k: &String| k.eq_ignore_ascii_case(key)) {
93 keys.push(key.to_owned());
94 }
95 }
96
97 keys
98}
99
100#[derive(Clone)]
102pub struct Section<'a> {
103 config: &'a Configuration,
104 path: String,
105}
106
107impl<'a> Section<'a> {
108 #[inline]
109 pub(crate) fn new(config: &'a Configuration, path: String) -> Self {
110 Self { config, path }
111 }
112
113 #[inline]
119 pub fn section(&self, key: &str) -> Section<'a> {
120 self.config.section(path::combine(&[&self.path, key]))
121 }
122
123 pub fn sections(&self) -> Vec<Section<'a>> {
125 collect_section_keys(self.config, &self.path)
126 .into_iter()
127 .map(|key| self.config.section(key))
128 .collect()
129 }
130
131 pub fn to_owned(&self) -> OwnedSection {
139 let len = self.path.len();
140 let token: Box<dyn ChangeToken> = Box::new(NeverChangeToken);
141 let mut settings = Settings::new();
142
143 for (key, value) in self.config {
144 if key.len() > len && path::starts_with(key, &self.path) {
145 settings.insert(key, value);
146 }
147 }
148
149 OwnedSection {
150 config: Configuration::new(settings, [token], self.config.providers.clone()),
151 path: self.path.clone(),
152 }
153 }
154
155 section!(self);
156}
157
158impl<'a> From<Section<'a>> for Vec<Section<'a>> {
159 #[inline]
160 fn from(section: Section<'a>) -> Self {
161 section.sections()
162 }
163}
164
165impl<'a> From<&'a Section<'a>> for Vec<Section<'a>> {
166 #[inline]
167 fn from(section: &'a Section<'a>) -> Self {
168 section.sections()
169 }
170}
171
172#[derive(Clone)]
174pub struct OwnedSection {
175 config: Configuration,
176 path: String,
177}
178
179impl OwnedSection {
180 #[inline]
186 pub fn section(&self, key: &str) -> Section<'_> {
187 self.config.section(path::combine(&[&self.path, key]))
188 }
189
190 pub fn sections(&self) -> Vec<Section<'_>> {
192 collect_section_keys(&self.config, &self.path)
193 .into_iter()
194 .map(|key| self.config.section(key))
195 .collect()
196 }
197
198 section!(self);
199}
200
201diagnostic!(Section<'_>, "Section");
202diagnostic!(OwnedSection, "OwnedSection");