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