qubit_config/
config_prefix_view.rs1#![allow(private_bounds)]
11
12use std::borrow::Cow;
13
14use qubit_value::MultiValues;
15use qubit_value::multi_values::{MultiValuesFirstGetter, MultiValuesGetter};
16
17use crate::config::Config;
18use crate::config_reader::ConfigReader;
19use crate::from::FromConfig;
20use crate::options::ConfigReadOptions;
21use crate::{ConfigResult, Property};
22
23#[derive(Debug, Clone)]
33pub struct ConfigPrefixView<'a> {
34 config: &'a Config,
35 prefix: String,
36 full_prefix: Option<String>,
37}
38
39impl<'a> ConfigPrefixView<'a> {
40 #[inline]
52 pub(crate) fn new(config: &'a Config, prefix: &str) -> Self {
53 let normalized_prefix = prefix.trim_matches('.').to_string();
54 let full_prefix = if normalized_prefix.is_empty() {
55 None
56 } else {
57 Some(format!("{normalized_prefix}."))
58 };
59 Self {
60 config,
61 prefix: normalized_prefix,
62 full_prefix,
63 }
64 }
65
66 #[inline]
72 pub fn prefix(&self) -> &str {
73 &self.prefix
74 }
75
76 pub fn prefix_view(&self, prefix: &str) -> ConfigPrefixView<'a> {
87 let child = prefix.trim_matches('.');
88 if self.prefix.is_empty() {
89 ConfigPrefixView::new(self.config, child)
90 } else if child.is_empty() {
91 ConfigPrefixView::new(self.config, self.prefix.as_str())
92 } else {
93 ConfigPrefixView::new(self.config, &format!("{}.{}", self.prefix, child))
94 }
95 }
96
97 fn resolve_key_cow<'b>(&'b self, name: &'b str) -> Cow<'b, str> {
111 if self.prefix.is_empty() || name.is_empty() {
112 return Cow::Borrowed(name);
113 }
114 if name == self.prefix {
115 return Cow::Borrowed(name);
116 }
117 let full_prefix = self
118 .full_prefix
119 .as_deref()
120 .expect("full_prefix must exist for non-empty prefix");
121 if name.starts_with(full_prefix) {
122 return Cow::Borrowed(name);
123 }
124 Cow::Owned(format!("{}.{}", self.prefix, name))
125 }
126
127 fn visible_entries<'b>(&'b self) -> Box<dyn Iterator<Item = (&'b str, &'b Property)> + 'b> {
128 let prefix = self.prefix.as_str();
129 if prefix.is_empty() {
130 return Box::new(self.config.properties.iter().map(|(k, v)| (k.as_str(), v)));
131 }
132 let full_prefix = self
133 .full_prefix
134 .as_deref()
135 .expect("full_prefix must exist for non-empty prefix");
136 Box::new(self.config.properties.iter().filter_map(move |(k, v)| {
137 if k == prefix {
138 Some((prefix, v))
139 } else {
140 k.strip_prefix(full_prefix).map(|stripped| (stripped, v))
141 }
142 }))
143 }
144
145 fn effective_root_prefix(&self, sub_prefix: &str) -> String {
148 let child = sub_prefix.trim_matches('.');
149 if self.prefix.is_empty() {
150 child.to_string()
151 } else if child.is_empty() {
152 self.prefix.clone()
153 } else {
154 format!("{}.{}", self.prefix, child)
155 }
156 }
157}
158
159impl<'a> ConfigReader for ConfigPrefixView<'a> {
160 #[inline]
161 fn is_enable_variable_substitution(&self) -> bool {
162 self.config.is_enable_variable_substitution()
163 }
164
165 #[inline]
166 fn max_substitution_depth(&self) -> usize {
167 self.config.max_substitution_depth()
168 }
169
170 #[inline]
171 fn read_options(&self) -> &ConfigReadOptions {
172 self.config.read_options()
173 }
174
175 #[inline]
176 fn description(&self) -> Option<&str> {
177 self.config.description()
178 }
179
180 fn get_property(&self, name: &str) -> Option<&Property> {
181 let key = self.resolve_key_cow(name);
182 self.config.get_property(key.as_ref())
183 }
184
185 fn len(&self) -> usize {
186 self.visible_entries().count()
187 }
188
189 fn is_empty(&self) -> bool {
190 self.visible_entries().next().is_none()
191 }
192
193 fn keys(&self) -> Vec<String> {
194 self.visible_entries().map(|(k, _)| k.to_string()).collect()
195 }
196
197 fn contains(&self, name: &str) -> bool {
198 let key = self.resolve_key_cow(name);
199 self.config.contains(key.as_ref())
200 }
201
202 fn get_strict<T>(&self, name: &str) -> ConfigResult<T>
203 where
204 MultiValues: MultiValuesFirstGetter<T>,
205 {
206 let key = self.resolve_key_cow(name);
207 self.config.get_strict(key.as_ref())
208 }
209
210 fn get_list<T>(&self, name: &str) -> ConfigResult<Vec<T>>
211 where
212 T: FromConfig,
213 {
214 let key = self.resolve_key_cow(name);
215 self.config.get_list(key.as_ref())
216 }
217
218 fn get_list_strict<T>(&self, name: &str) -> ConfigResult<Vec<T>>
219 where
220 MultiValues: MultiValuesGetter<T>,
221 {
222 let key = self.resolve_key_cow(name);
223 self.config.get_list_strict(key.as_ref())
224 }
225
226 fn get_optional_list<T>(&self, name: &str) -> ConfigResult<Option<Vec<T>>>
227 where
228 T: FromConfig,
229 {
230 let key = self.resolve_key_cow(name);
231 self.config.get_optional_list(key.as_ref())
232 }
233
234 fn contains_prefix(&self, prefix: &str) -> bool {
235 self.visible_entries().any(|(k, _)| k.starts_with(prefix))
236 }
237
238 fn iter_prefix<'b>(
239 &'b self,
240 prefix: &'b str,
241 ) -> Box<dyn Iterator<Item = (&'b str, &'b Property)> + 'b> {
242 Box::new(
243 self.visible_entries()
244 .filter(move |(k, _)| k.starts_with(prefix)),
245 )
246 }
247
248 fn iter<'b>(&'b self) -> Box<dyn Iterator<Item = (&'b str, &'b Property)> + 'b> {
249 self.visible_entries()
250 }
251
252 fn is_null(&self, name: &str) -> bool {
253 let key = self.resolve_key_cow(name);
254 self.config.is_null(key.as_ref())
255 }
256
257 fn subconfig(&self, prefix: &str, strip_prefix: bool) -> ConfigResult<Config> {
258 let full = self.effective_root_prefix(prefix);
259 self.config.subconfig(&full, strip_prefix)
260 }
261
262 fn deserialize<T>(&self, prefix: &str) -> ConfigResult<T>
263 where
264 T: serde::de::DeserializeOwned,
265 {
266 let full = self.effective_root_prefix(prefix);
267 self.config.deserialize(&full)
268 }
269
270 #[inline]
271 fn prefix_view(&self, prefix: &str) -> ConfigPrefixView<'a> {
272 ConfigPrefixView::prefix_view(self, prefix)
273 }
274
275 fn resolve_key(&self, name: &str) -> String {
276 if name.is_empty() {
277 return self.prefix.clone();
278 }
279 self.resolve_key_cow(name).into_owned()
280 }
281}