qubit_config/config_reader.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10
11#![allow(private_bounds)]
12
13use qubit_value::MultiValues;
14use qubit_value::multi_values::{MultiValuesFirstGetter, MultiValuesGetter};
15use serde::de::DeserializeOwned;
16
17use crate::config_prefix_view::ConfigPrefixView;
18use crate::field::ConfigField;
19use crate::from::{FromConfig, is_effectively_missing, parse_property_from_reader};
20use crate::options::ConfigReadOptions;
21use crate::{Config, ConfigError, ConfigResult, Property};
22
23/// Read-only configuration interface.
24///
25/// This trait allows consumers to read configuration values without requiring
26/// ownership of a [`crate::Config`]. Both [`crate::Config`] and
27/// [`crate::ConfigPrefixView`] implement it.
28///
29/// Its required methods mirror the read-only surface of [`crate::Config`]
30/// (metadata, raw properties, iteration, subtree extraction, and serde
31/// deserialization), with prefix views resolving keys relative to their
32/// logical prefix.
33///
34pub trait ConfigReader {
35 /// Returns whether `${...}` variable substitution is applied when reading
36 /// string values.
37 ///
38 /// # Returns
39 ///
40 /// `true` if substitution is enabled for this reader.
41 fn is_enable_variable_substitution(&self) -> bool;
42
43 /// Returns the maximum recursion depth allowed when resolving nested
44 /// `${...}` references.
45 ///
46 /// # Returns
47 ///
48 /// Maximum substitution depth (see
49 /// `DEFAULT_MAX_SUBSTITUTION_DEPTH` for the default used by
50 /// [`crate::Config`]).
51 fn max_substitution_depth(&self) -> usize;
52
53 /// Returns the optional human-readable description attached to this
54 /// configuration (the whole document; prefix views expose the same value
55 /// as the underlying [`crate::Config`]).
56 fn description(&self) -> Option<&str>;
57
58 /// Returns a reference to the raw [`Property`] for `name`, if present.
59 ///
60 /// For a [`ConfigPrefixView`], `name` is resolved relative to the view
61 /// prefix (same rules as [`Self::get`]).
62 fn get_property(&self, name: &str) -> Option<&Property>;
63
64 /// Number of configuration entries visible to this reader (all keys for
65 /// [`crate::Config`]; relative keys only for a [`ConfigPrefixView`]).
66 fn len(&self) -> usize;
67
68 /// Returns `true` when [`Self::len`] is zero.
69 fn is_empty(&self) -> bool;
70
71 /// All keys visible to this reader (relative keys for a prefix view).
72 fn keys(&self) -> Vec<String>;
73
74 /// Returns whether a property exists for the given key.
75 ///
76 /// # Parameters
77 ///
78 /// * `name` - Full configuration key (for [`crate::ConfigPrefixView`],
79 /// relative keys are resolved against the view prefix).
80 ///
81 /// # Returns
82 ///
83 /// `true` if the key is present.
84 fn contains(&self, name: &str) -> bool;
85
86 /// Reads the first stored value for `name` and converts it to `T`.
87 ///
88 /// # Type parameters
89 ///
90 /// * `T` - Target type parsed by [`FromConfig`].
91 ///
92 /// # Parameters
93 ///
94 /// * `name` - Configuration key.
95 ///
96 /// # Returns
97 ///
98 /// The converted value on success, or a [`crate::ConfigError`] if the key
99 /// is missing, empty, or not convertible.
100 fn get<T>(&self, name: &str) -> ConfigResult<T>
101 where
102 T: FromConfig,
103 {
104 let resolved = self.resolve_key(name);
105 let property = self
106 .get_property(name)
107 .ok_or_else(|| ConfigError::PropertyNotFound(resolved.clone()))?;
108 if !property.is_empty()
109 && is_effectively_missing(self, &resolved, property, self.read_options())?
110 {
111 return Err(ConfigError::PropertyHasNoValue(resolved));
112 }
113 parse_property_from_reader(self, &resolved, property, self.read_options())
114 }
115
116 /// Reads the first stored value for `name` without cross-type conversion.
117 ///
118 /// # Type parameters
119 ///
120 /// * `T` - Exact target type; requires `MultiValues` to implement
121 /// `MultiValuesFirstGetter` for `T`.
122 ///
123 /// # Parameters
124 ///
125 /// * `name` - Configuration key.
126 ///
127 /// # Returns
128 ///
129 /// The exact stored value on success, or a [`crate::ConfigError`] if the
130 /// key is missing, empty, or has a different stored type.
131 fn get_strict<T>(&self, name: &str) -> ConfigResult<T>
132 where
133 MultiValues: MultiValuesFirstGetter<T>;
134
135 /// Reads all stored values for `name` and converts each element to `T`.
136 ///
137 /// # Type parameters
138 ///
139 /// * `T` - Element type supported by the shared conversion layer.
140 ///
141 /// # Parameters
142 ///
143 /// * `name` - Configuration key.
144 ///
145 /// # Returns
146 ///
147 /// A vector of values on success, or a [`crate::ConfigError`] on failure.
148 fn get_list<T>(&self, name: &str) -> ConfigResult<Vec<T>>
149 where
150 T: FromConfig;
151
152 /// Reads all stored values for `name` without cross-type conversion.
153 ///
154 /// # Type parameters
155 ///
156 /// * `T` - Exact element type; requires `MultiValues` to implement
157 /// `MultiValuesGetter` for `T`.
158 ///
159 /// # Parameters
160 ///
161 /// * `name` - Configuration key.
162 ///
163 /// # Returns
164 ///
165 /// A vector of exact stored values on success, or a
166 /// [`crate::ConfigError`] on failure.
167 fn get_list_strict<T>(&self, name: &str) -> ConfigResult<Vec<T>>
168 where
169 MultiValues: MultiValuesGetter<T>;
170
171 /// Gets a value or `default` if the key is missing or empty.
172 ///
173 /// Conversion and substitution errors are returned instead of being hidden by
174 /// the default.
175 #[inline]
176 fn get_or<T>(&self, name: &str, default: T) -> ConfigResult<T>
177 where
178 T: FromConfig,
179 {
180 self.get_optional(name)
181 .map(|value| value.unwrap_or(default))
182 }
183
184 /// Gets an optional value with the same semantics as [`crate::Config::get_optional`].
185 ///
186 /// # Type parameters
187 ///
188 /// * `T` - Target type parsed by [`FromConfig`].
189 ///
190 /// # Parameters
191 ///
192 /// * `name` - Configuration key (relative for a prefix view).
193 ///
194 /// # Returns
195 ///
196 /// `Ok(Some(v))`, `Ok(None)` when missing or empty, or `Err` on conversion failure.
197 fn get_optional<T>(&self, name: &str) -> ConfigResult<Option<T>>
198 where
199 T: FromConfig,
200 {
201 let resolved = self.resolve_key(name);
202 match self.get_property(name) {
203 None => Ok(None),
204 Some(property)
205 if is_effectively_missing(self, &resolved, property, self.read_options())? =>
206 {
207 Ok(None)
208 }
209 Some(property) => {
210 parse_property_from_reader(self, &resolved, property, self.read_options()).map(Some)
211 }
212 }
213 }
214
215 /// Gets the read options active for this reader.
216 ///
217 /// # Returns
218 ///
219 /// Global read options inherited by field-less reads.
220 fn read_options(&self) -> &ConfigReadOptions;
221
222 /// Reads a value from the first present and non-empty key in `names`.
223 ///
224 /// # Parameters
225 ///
226 /// * `names` - Candidate keys in priority order.
227 ///
228 /// # Returns
229 ///
230 /// Parsed value from the first configured key. Conversion errors stop the
231 /// search and are returned directly.
232 fn get_any<T>(&self, names: &[&str]) -> ConfigResult<T>
233 where
234 T: FromConfig,
235 {
236 self.get_optional_any(names)?
237 .ok_or_else(|| ConfigError::PropertyNotFound(format!("one of: {}", names.join(", "))))
238 }
239
240 /// Reads an optional value from the first present and non-empty key.
241 ///
242 /// # Parameters
243 ///
244 /// * `names` - Candidate keys in priority order.
245 ///
246 /// # Returns
247 ///
248 /// `Ok(None)` only when all keys are missing or empty.
249 fn get_optional_any<T>(&self, names: &[&str]) -> ConfigResult<Option<T>>
250 where
251 T: FromConfig,
252 {
253 self.get_optional_any_with_options(names, self.read_options())
254 }
255
256 /// Reads a value from any key, using `default` only when all keys are
257 /// absent or empty.
258 ///
259 /// # Parameters
260 ///
261 /// * `names` - Candidate keys in priority order.
262 /// * `default` - Fallback when no candidate is configured.
263 ///
264 /// # Returns
265 ///
266 /// Parsed value or `default`; parsing errors are never swallowed.
267 fn get_any_or<T>(&self, names: &[&str], default: T) -> ConfigResult<T>
268 where
269 T: FromConfig,
270 {
271 self.get_optional_any(names)
272 .map(|value| value.unwrap_or(default))
273 }
274
275 /// Reads a value from any key with explicit read options, using `default`
276 /// only when all keys are absent or empty.
277 ///
278 /// # Parameters
279 ///
280 /// * `names` - Candidate keys in priority order.
281 /// * `default` - Fallback when no candidate is configured.
282 /// * `read_options` - Parsing options for this read.
283 ///
284 /// # Returns
285 ///
286 /// Parsed value or `default`; parsing errors are never swallowed.
287 fn get_any_or_with<T>(
288 &self,
289 names: &[&str],
290 default: T,
291 read_options: &ConfigReadOptions,
292 ) -> ConfigResult<T>
293 where
294 T: FromConfig,
295 {
296 self.get_optional_any_with_options(names, read_options)
297 .map(|value| value.unwrap_or(default))
298 }
299
300 /// Reads a declared field.
301 ///
302 /// # Parameters
303 ///
304 /// * `field` - Field declaration containing name, aliases, defaults, and
305 /// optional field-level read options.
306 ///
307 /// # Returns
308 ///
309 /// Parsed field value or its default.
310 fn read<T>(&self, field: ConfigField<T>) -> ConfigResult<T>
311 where
312 T: FromConfig,
313 {
314 let ConfigField {
315 name,
316 aliases,
317 default,
318 read_options,
319 } = field;
320 let options = read_options.as_ref().unwrap_or_else(|| self.read_options());
321 let mut names = Vec::with_capacity(1 + aliases.len());
322 names.push(name.as_str());
323 names.extend(aliases.iter().map(String::as_str));
324 self.get_optional_any_with_options(&names, options)?
325 .or(default)
326 .ok_or_else(|| ConfigError::PropertyNotFound(format!("one of: {}", names.join(", "))))
327 }
328
329 /// Reads an optional declared field.
330 ///
331 /// # Parameters
332 ///
333 /// * `field` - Field declaration.
334 ///
335 /// # Returns
336 ///
337 /// Parsed field value, its default, or `None`.
338 fn read_optional<T>(&self, field: ConfigField<T>) -> ConfigResult<Option<T>>
339 where
340 T: FromConfig,
341 {
342 let ConfigField {
343 name,
344 aliases,
345 default,
346 read_options,
347 } = field;
348 let options = read_options.as_ref().unwrap_or_else(|| self.read_options());
349 let mut names = Vec::with_capacity(1 + aliases.len());
350 names.push(name.as_str());
351 names.extend(aliases.iter().map(String::as_str));
352 self.get_optional_any_with_options(&names, options)
353 .map(|value| value.or(default))
354 }
355
356 /// Shared implementation for field-level and global multi-key reads.
357 fn get_optional_any_with_options<T>(
358 &self,
359 names: &[&str],
360 options: &ConfigReadOptions,
361 ) -> ConfigResult<Option<T>>
362 where
363 T: FromConfig,
364 {
365 for name in names {
366 let Some(property) = self.get_property(name) else {
367 continue;
368 };
369 let resolved = self.resolve_key(name);
370 if is_effectively_missing(self, &resolved, property, options)? {
371 continue;
372 }
373 return parse_property_from_reader(self, &resolved, property, options).map(Some);
374 }
375 Ok(None)
376 }
377
378 /// Gets an optional list with the same semantics as [`crate::Config::get_optional_list`].
379 ///
380 /// # Type parameters
381 ///
382 /// * `T` - Element type supported by the shared conversion layer.
383 ///
384 /// # Parameters
385 ///
386 /// * `name` - Configuration key.
387 ///
388 /// # Returns
389 ///
390 /// `Ok(Some(vec))`, `Ok(None)` when missing or empty, or `Err` on failure.
391 fn get_optional_list<T>(&self, name: &str) -> ConfigResult<Option<Vec<T>>>
392 where
393 T: FromConfig;
394
395 /// Returns whether any key visible to this reader starts with `prefix`.
396 ///
397 /// # Parameters
398 ///
399 /// * `prefix` - Key prefix to test (for a prefix view, keys are relative to
400 /// that view).
401 ///
402 /// # Returns
403 ///
404 /// `true` if at least one matching key exists.
405 fn contains_prefix(&self, prefix: &str) -> bool;
406
407 /// Iterates `(key, property)` pairs for keys that start with `prefix`.
408 ///
409 /// # Parameters
410 ///
411 /// * `prefix` - Key prefix filter.
412 ///
413 /// # Returns
414 ///
415 /// A boxed iterator over matching entries.
416 fn iter_prefix<'a>(
417 &'a self,
418 prefix: &'a str,
419 ) -> Box<dyn Iterator<Item = (&'a str, &'a Property)> + 'a>;
420
421 /// Iterates all `(key, property)` pairs visible to this reader (same scope
422 /// as [`Self::keys`]).
423 fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (&'a str, &'a Property)> + 'a>;
424
425 /// Returns `true` if the key exists and the property has no values (same
426 /// as [`crate::Config::is_null`]).
427 fn is_null(&self, name: &str) -> bool;
428
429 /// Extracts a subtree as a new [`Config`] (same semantics as
430 /// [`crate::Config::subconfig`]; on a prefix view, `prefix` is relative to
431 /// the view).
432 fn subconfig(&self, prefix: &str, strip_prefix: bool) -> ConfigResult<Config>;
433
434 /// Deserializes the subtree at `prefix` with serde (same as
435 /// [`crate::Config::deserialize`]; on a prefix view, `prefix` is relative).
436 fn deserialize<T>(&self, prefix: &str) -> ConfigResult<T>
437 where
438 T: DeserializeOwned;
439
440 /// Creates a read-only prefix view; relative keys resolve under `prefix`.
441 ///
442 /// Semantics match [`crate::Config::prefix_view`] and
443 /// [`crate::ConfigPrefixView::prefix_view`] (nested prefix when called on a
444 /// view).
445 ///
446 /// # Parameters
447 ///
448 /// * `prefix` - Logical prefix; empty means the full configuration (same as
449 /// root).
450 ///
451 /// # Returns
452 ///
453 /// A [`ConfigPrefixView`] borrowing this reader's underlying
454 /// [`crate::Config`].
455 fn prefix_view(&self, prefix: &str) -> ConfigPrefixView<'_>;
456
457 /// Resolves `name` into the canonical key path against the root
458 /// [`crate::Config`].
459 ///
460 /// For a root [`crate::Config`], this returns `name` unchanged. For a
461 /// [`crate::ConfigPrefixView`], this prepends the effective view prefix so
462 /// callers can report root-relative key paths in diagnostics.
463 ///
464 /// # Parameters
465 ///
466 /// * `name` - Relative or absolute key in the current reader scope.
467 ///
468 /// # Returns
469 ///
470 /// Root-relative key path string.
471 #[inline]
472 fn resolve_key(&self, name: &str) -> String {
473 name.to_string()
474 }
475
476 /// Gets a string value, applying variable substitution when enabled.
477 ///
478 /// # Parameters
479 ///
480 /// * `name` - Configuration key.
481 ///
482 /// # Returns
483 ///
484 /// The string after `${...}` resolution, or a [`crate::ConfigError`].
485 fn get_string(&self, name: &str) -> ConfigResult<String> {
486 self.get(name)
487 }
488
489 /// Gets a string value from the first present and non-empty key in `names`.
490 ///
491 /// # Parameters
492 ///
493 /// * `names` - Candidate keys in priority order.
494 ///
495 /// # Returns
496 ///
497 /// The resolved string from the first configured key.
498 #[inline]
499 fn get_string_any(&self, names: &[&str]) -> ConfigResult<String> {
500 self.get_any(names)
501 }
502
503 /// Gets an optional string value from the first present and non-empty key.
504 ///
505 /// # Parameters
506 ///
507 /// * `names` - Candidate keys in priority order.
508 ///
509 /// # Returns
510 ///
511 /// `Ok(None)` only when all keys are missing or empty.
512 #[inline]
513 fn get_optional_string_any(&self, names: &[&str]) -> ConfigResult<Option<String>> {
514 self.get_optional_any(names)
515 }
516
517 /// Gets a string from any key, or `default` when all keys are missing or
518 /// empty.
519 ///
520 /// # Parameters
521 ///
522 /// * `names` - Candidate keys in priority order.
523 /// * `default` - Fallback string used only when every key is missing or
524 /// empty.
525 ///
526 /// # Returns
527 ///
528 /// The resolved string or a clone of `default`; substitution errors are
529 /// returned.
530 #[inline]
531 fn get_string_any_or(&self, names: &[&str], default: &str) -> ConfigResult<String> {
532 self.get_any_or(names, default.to_string())
533 }
534
535 /// Gets a string value with substitution, or `default` if the key is
536 /// missing or empty.
537 ///
538 /// # Parameters
539 ///
540 /// * `name` - Configuration key.
541 /// * `default` - Fallback string used only when the key is missing or empty.
542 ///
543 /// # Returns
544 ///
545 /// The resolved string or a clone of `default`; parsing and substitution
546 /// errors are returned.
547 #[inline]
548 fn get_string_or(&self, name: &str, default: &str) -> ConfigResult<String> {
549 self.get_or(name, default.to_string())
550 }
551
552 /// Gets all string values for `name`, applying substitution to each element
553 /// when enabled.
554 ///
555 /// # Parameters
556 ///
557 /// * `name` - Configuration key.
558 ///
559 /// # Returns
560 ///
561 /// A vector of resolved strings, or a [`crate::ConfigError`].
562 fn get_string_list(&self, name: &str) -> ConfigResult<Vec<String>> {
563 self.get(name)
564 }
565
566 /// Gets a string list with substitution, or copies `default` if the key is
567 /// missing or empty.
568 ///
569 /// # Parameters
570 ///
571 /// * `name` - Configuration key.
572 /// * `default` - Fallback string slices used only when the key is missing or
573 /// empty.
574 ///
575 /// # Returns
576 ///
577 /// The resolved list or `default` converted to owned `String`s`; parsing and
578 /// substitution errors are returned.
579 #[inline]
580 fn get_string_list_or(&self, name: &str, default: &[&str]) -> ConfigResult<Vec<String>> {
581 self.get_or(name, default.iter().map(|s| s.to_string()).collect())
582 }
583
584 /// Gets an optional string with the same three-way semantics as
585 /// [`crate::Config::get_optional_string`].
586 ///
587 /// # Parameters
588 ///
589 /// * `name` - Configuration key.
590 ///
591 /// # Returns
592 ///
593 /// `Ok(None)` if the key is missing or empty; `Ok(Some(s))` with
594 /// substitution applied; or `Err` if the value exists but cannot be read as
595 /// a string.
596 #[inline]
597 fn get_optional_string(&self, name: &str) -> ConfigResult<Option<String>> {
598 match self.get_property(name) {
599 None => Ok(None),
600 Some(prop) if prop.is_empty() => Ok(None),
601 Some(_) => self.get_string(name).map(Some),
602 }
603 }
604
605 /// Gets an optional string list with per-element substitution when enabled.
606 ///
607 /// # Parameters
608 ///
609 /// * `name` - Configuration key.
610 ///
611 /// # Returns
612 ///
613 /// `Ok(None)` if the key is missing or empty; `Ok(Some(vec))` otherwise; or
614 /// `Err` on conversion/substitution failure.
615 #[inline]
616 fn get_optional_string_list(&self, name: &str) -> ConfigResult<Option<Vec<String>>> {
617 match self.get_property(name) {
618 None => Ok(None),
619 Some(prop) if prop.is_empty() => Ok(None),
620 Some(_) => self.get_string_list(name).map(Some),
621 }
622 }
623}