qubit_config/config_reader.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9#![allow(private_bounds)]
10
11use qubit_value::multi_values::{MultiValuesFirstGetter, MultiValuesGetter};
12use qubit_value::MultiValues;
13use serde::de::DeserializeOwned;
14
15use crate::config_prefix_view::ConfigPrefixView;
16use crate::{utils, Config, ConfigResult, Property};
17
18/// Read-only configuration interface.
19///
20/// This trait allows consumers to read configuration values without requiring
21/// ownership of a [`crate::Config`]. Both [`crate::Config`] and
22/// [`crate::ConfigPrefixView`] implement it.
23///
24/// Its required methods mirror the read-only surface of [`crate::Config`]
25/// (metadata, raw properties, iteration, subtree extraction, and serde
26/// deserialization), with prefix views resolving keys relative to their
27/// logical prefix.
28///
29/// Author: Haixing Hu
30pub trait ConfigReader {
31 /// Returns whether `${...}` variable substitution is applied when reading
32 /// string values.
33 ///
34 /// # Returns
35 ///
36 /// `true` if substitution is enabled for this reader.
37 fn is_enable_variable_substitution(&self) -> bool;
38
39 /// Returns the maximum recursion depth allowed when resolving nested
40 /// `${...}` references.
41 ///
42 /// # Returns
43 ///
44 /// Maximum substitution depth (see
45 /// [`crate::constants::DEFAULT_MAX_SUBSTITUTION_DEPTH`] for the default
46 /// used by [`crate::Config`]).
47 fn max_substitution_depth(&self) -> usize;
48
49 /// Returns the optional human-readable description attached to this
50 /// configuration (the whole document; prefix views expose the same value
51 /// as the underlying [`crate::Config`]).
52 fn description(&self) -> Option<&str>;
53
54 /// Returns a reference to the raw [`Property`] for `name`, if present.
55 ///
56 /// For a [`ConfigPrefixView`], `name` is resolved relative to the view
57 /// prefix (same rules as [`Self::get`]).
58 fn get_property(&self, name: &str) -> Option<&Property>;
59
60 /// Number of configuration entries visible to this reader (all keys for
61 /// [`crate::Config`]; relative keys only for a [`ConfigPrefixView`]).
62 fn len(&self) -> usize;
63
64 /// Returns `true` when [`Self::len`] is zero.
65 fn is_empty(&self) -> bool;
66
67 /// All keys visible to this reader (relative keys for a prefix view).
68 fn keys(&self) -> Vec<String>;
69
70 /// Returns whether a property exists for the given key.
71 ///
72 /// # Parameters
73 ///
74 /// * `name` - Full configuration key (for [`crate::ConfigPrefixView`],
75 /// relative keys are resolved against the view prefix).
76 ///
77 /// # Returns
78 ///
79 /// `true` if the key is present.
80 fn contains(&self, name: &str) -> bool;
81
82 /// Reads the first stored value for `name` and converts it to `T`.
83 ///
84 /// # Type parameters
85 ///
86 /// * `T` - Target type; requires `MultiValues` to implement
87 /// `MultiValuesFirstGetter` for `T`.
88 ///
89 /// # Parameters
90 ///
91 /// * `name` - Configuration key.
92 ///
93 /// # Returns
94 ///
95 /// The converted value on success, or a [`crate::ConfigError`] if the key
96 /// is missing, empty, or not convertible.
97 fn get<T>(&self, name: &str) -> ConfigResult<T>
98 where
99 MultiValues: MultiValuesFirstGetter<T>;
100
101 /// Reads all stored values for `name` and converts each element to `T`.
102 ///
103 /// # Type parameters
104 ///
105 /// * `T` - Element type; requires `MultiValues` to implement
106 /// `MultiValuesGetter` for `T`.
107 ///
108 /// # Parameters
109 ///
110 /// * `name` - Configuration key.
111 ///
112 /// # Returns
113 ///
114 /// A vector of values on success, or a [`crate::ConfigError`] on failure.
115 fn get_list<T>(&self, name: &str) -> ConfigResult<Vec<T>>
116 where
117 MultiValues: MultiValuesGetter<T>;
118
119 /// Gets a value or `default` if the key is missing or conversion fails (same
120 /// as [`crate::Config::get_or`]).
121 #[inline]
122 fn get_or<T>(&self, name: &str, default: T) -> T
123 where
124 MultiValues: MultiValuesFirstGetter<T>,
125 {
126 self.get(name).unwrap_or(default)
127 }
128
129 /// Gets an optional value with the same semantics as [`crate::Config::get_optional`].
130 ///
131 /// # Type parameters
132 ///
133 /// * `T` - Target type; requires `MultiValues` to implement
134 /// `MultiValuesFirstGetter` for `T`.
135 ///
136 /// # Parameters
137 ///
138 /// * `name` - Configuration key (relative for a prefix view).
139 ///
140 /// # Returns
141 ///
142 /// `Ok(Some(v))`, `Ok(None)` when missing or empty, or `Err` on conversion failure.
143 fn get_optional<T>(&self, name: &str) -> ConfigResult<Option<T>>
144 where
145 MultiValues: MultiValuesFirstGetter<T>;
146
147 /// Gets an optional list with the same semantics as [`crate::Config::get_optional_list`].
148 ///
149 /// # Type parameters
150 ///
151 /// * `T` - Element type; requires `MultiValues` to implement `MultiValuesGetter` for `T`.
152 ///
153 /// # Parameters
154 ///
155 /// * `name` - Configuration key.
156 ///
157 /// # Returns
158 ///
159 /// `Ok(Some(vec))`, `Ok(None)` when missing or empty, or `Err` on failure.
160 fn get_optional_list<T>(&self, name: &str) -> ConfigResult<Option<Vec<T>>>
161 where
162 MultiValues: MultiValuesGetter<T>;
163
164 /// Returns whether any key visible to this reader starts with `prefix`.
165 ///
166 /// # Parameters
167 ///
168 /// * `prefix` - Key prefix to test (for a prefix view, keys are relative to
169 /// that view).
170 ///
171 /// # Returns
172 ///
173 /// `true` if at least one matching key exists.
174 fn contains_prefix(&self, prefix: &str) -> bool;
175
176 /// Iterates `(key, property)` pairs for keys that start with `prefix`.
177 ///
178 /// # Parameters
179 ///
180 /// * `prefix` - Key prefix filter.
181 ///
182 /// # Returns
183 ///
184 /// A boxed iterator over matching entries.
185 fn iter_prefix<'a>(
186 &'a self,
187 prefix: &'a str,
188 ) -> Box<dyn Iterator<Item = (&'a str, &'a Property)> + 'a>;
189
190 /// Iterates all `(key, property)` pairs visible to this reader (same scope
191 /// as [`Self::keys`]).
192 fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (&'a str, &'a Property)> + 'a>;
193
194 /// Returns `true` if the key exists and the property has no values (same
195 /// as [`crate::Config::is_null`]).
196 fn is_null(&self, name: &str) -> bool;
197
198 /// Extracts a subtree as a new [`Config`] (same semantics as
199 /// [`crate::Config::subconfig`]; on a prefix view, `prefix` is relative to
200 /// the view).
201 fn subconfig(&self, prefix: &str, strip_prefix: bool) -> ConfigResult<Config>;
202
203 /// Deserializes the subtree at `prefix` with serde (same as
204 /// [`crate::Config::deserialize`]; on a prefix view, `prefix` is relative).
205 fn deserialize<T>(&self, prefix: &str) -> ConfigResult<T>
206 where
207 T: DeserializeOwned;
208
209 /// Creates a read-only prefix view; relative keys resolve under `prefix`.
210 ///
211 /// Semantics match [`crate::Config::prefix_view`] and
212 /// [`crate::ConfigPrefixView::prefix_view`] (nested prefix when called on a
213 /// view).
214 ///
215 /// # Parameters
216 ///
217 /// * `prefix` - Logical prefix; empty means the full configuration (same as
218 /// root).
219 ///
220 /// # Returns
221 ///
222 /// A [`ConfigPrefixView`] borrowing this reader's underlying
223 /// [`crate::Config`].
224 fn prefix_view(&self, prefix: &str) -> ConfigPrefixView<'_>;
225
226 /// Resolves `name` into the canonical key path against the root
227 /// [`crate::Config`].
228 ///
229 /// For a root [`crate::Config`], this returns `name` unchanged. For a
230 /// [`crate::ConfigPrefixView`], this prepends the effective view prefix so
231 /// callers can report root-relative key paths in diagnostics.
232 ///
233 /// # Parameters
234 ///
235 /// * `name` - Relative or absolute key in the current reader scope.
236 ///
237 /// # Returns
238 ///
239 /// Root-relative key path string.
240 #[inline]
241 fn resolve_key(&self, name: &str) -> String {
242 name.to_string()
243 }
244
245 /// Gets a string value, applying variable substitution when enabled.
246 ///
247 /// # Parameters
248 ///
249 /// * `name` - Configuration key.
250 ///
251 /// # Returns
252 ///
253 /// The string after `${...}` resolution, or a [`crate::ConfigError`].
254 fn get_string(&self, name: &str) -> ConfigResult<String> {
255 let value: String = self.get(name)?;
256 if self.is_enable_variable_substitution() {
257 utils::substitute_variables(&value, self, self.max_substitution_depth())
258 } else {
259 Ok(value)
260 }
261 }
262
263 /// Gets a string value with substitution, or `default` if lookup or
264 /// substitution fails.
265 ///
266 /// # Parameters
267 ///
268 /// * `name` - Configuration key.
269 /// * `default` - Fallback string when [`Self::get_string`] would error.
270 ///
271 /// # Returns
272 ///
273 /// The resolved string or a clone of `default`.
274 #[inline]
275 fn get_string_or(&self, name: &str, default: &str) -> String {
276 self.get_string(name)
277 .unwrap_or_else(|_| default.to_string())
278 }
279
280 /// Gets all string values for `name`, applying substitution to each element
281 /// when enabled.
282 ///
283 /// # Parameters
284 ///
285 /// * `name` - Configuration key.
286 ///
287 /// # Returns
288 ///
289 /// A vector of resolved strings, or a [`crate::ConfigError`].
290 fn get_string_list(&self, name: &str) -> ConfigResult<Vec<String>> {
291 let values: Vec<String> = self.get_list(name)?;
292 if self.is_enable_variable_substitution() {
293 values
294 .into_iter()
295 .map(|v| utils::substitute_variables(&v, self, self.max_substitution_depth()))
296 .collect()
297 } else {
298 Ok(values)
299 }
300 }
301
302 /// Gets a string list with substitution, or copies `default` if lookup
303 /// fails.
304 ///
305 /// # Parameters
306 ///
307 /// * `name` - Configuration key.
308 /// * `default` - Fallback string slices when [`Self::get_string_list`]
309 /// would error.
310 ///
311 /// # Returns
312 ///
313 /// The resolved list or `default` converted to owned `String`s.
314 #[inline]
315 fn get_string_list_or(&self, name: &str, default: &[&str]) -> Vec<String> {
316 self.get_string_list(name)
317 .unwrap_or_else(|_| default.iter().map(|s| s.to_string()).collect())
318 }
319
320 /// Gets an optional string with the same three-way semantics as
321 /// [`crate::Config::get_optional_string`].
322 ///
323 /// # Parameters
324 ///
325 /// * `name` - Configuration key.
326 ///
327 /// # Returns
328 ///
329 /// `Ok(None)` if the key is missing or empty; `Ok(Some(s))` with
330 /// substitution applied; or `Err` if the value exists but cannot be read as
331 /// a string.
332 #[inline]
333 fn get_optional_string(&self, name: &str) -> ConfigResult<Option<String>> {
334 match self.get_property(name) {
335 None => Ok(None),
336 Some(prop) if prop.is_empty() => Ok(None),
337 Some(_) => self.get_string(name).map(Some),
338 }
339 }
340
341 /// Gets an optional string list with per-element substitution when enabled.
342 ///
343 /// # Parameters
344 ///
345 /// * `name` - Configuration key.
346 ///
347 /// # Returns
348 ///
349 /// `Ok(None)` if the key is missing or empty; `Ok(Some(vec))` otherwise; or
350 /// `Err` on conversion/substitution failure.
351 #[inline]
352 fn get_optional_string_list(&self, name: &str) -> ConfigResult<Option<Vec<String>>> {
353 match self.get_property(name) {
354 None => Ok(None),
355 Some(prop) if prop.is_empty() => Ok(None),
356 Some(_) => self.get_string_list(name).map(Some),
357 }
358 }
359}