Skip to main content

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}