Skip to main content

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}