Skip to main content

qubit_config/
config.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Configuration Manager
10//!
11//! Provides storage, retrieval, and management of configurations.
12//!
13//! # Author
14//!
15//! Haixing Hu
16
17#![allow(private_bounds)]
18
19use std::collections::HashMap;
20
21use serde::{Deserialize, Serialize};
22
23use crate::config_prefix_view::ConfigPrefixView;
24use crate::config_reader::ConfigReader;
25use crate::source::ConfigSource;
26use crate::utils;
27use crate::{ConfigError, ConfigResult, Property};
28use qubit_value::multi_values::{
29    MultiValuesAddArg, MultiValuesAdder, MultiValuesFirstGetter, MultiValuesGetter,
30    MultiValuesMultiAdder, MultiValuesSetArg, MultiValuesSetter, MultiValuesSetterSlice,
31    MultiValuesSingleSetter,
32};
33use qubit_value::MultiValues;
34use qubit_value::ValueError;
35
36/// Configuration Manager
37///
38/// Manages a set of configuration properties with type-safe read/write
39/// interfaces.
40///
41/// # Features
42///
43/// - Supports multiple data types
44/// - Supports variable substitution (`${var_name}` format)
45/// - Supports configuration merging
46/// - Supports final value protection
47/// - Thread-safe (when wrapped in `Arc<RwLock<Config>>`)
48///
49/// # Examples
50///
51/// ```rust,ignore
52/// use qubit_config::Config;
53///
54/// let mut config = Config::new();
55///
56/// // Set configuration values (type inference)
57/// config.set("port", 8080)?;                    // inferred as i32
58/// config.set("host", "localhost")?;
59/// // &str is converted to String
60/// config.set("debug", true)?;                   // inferred as bool
61/// config.set("timeout", 30.5)?;                 // inferred as f64
62/// config.set("code", 42u8)?;                    // inferred as u8
63///
64/// // Set multiple values (type inference)
65/// config.set("ports", vec![8080, 8081, 8082])?; // inferred as i32
66/// config.set("hosts", &["host1", "host2"])?;
67/// // &str elements are converted
68///
69/// // Read configuration values (type inference)
70/// let port: i32 = config.get("port")?;
71/// let host: String = config.get("host")?;
72/// let debug: bool = config.get("debug")?;
73/// let code: u8 = config.get("code")?;
74///
75/// // Read configuration values (turbofish)
76/// let port = config.get::<i32>("port")?;
77///
78/// // Read configuration value or use default
79/// let timeout: u64 = config.get_or("timeout", 30);
80/// ```
81///
82/// # Author
83///
84/// Haixing Hu
85///
86#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
87pub struct Config {
88    /// Configuration description
89    description: Option<String>,
90    /// Configuration property mapping
91    pub(crate) properties: HashMap<String, Property>,
92    /// Whether variable substitution is enabled
93    enable_variable_substitution: bool,
94    /// Maximum depth for variable substitution
95    max_substitution_depth: usize,
96}
97
98impl Config {
99    /// Creates a new empty configuration
100    ///
101    /// # Returns
102    ///
103    /// Returns a new configuration instance
104    ///
105    /// # Examples
106    ///
107    /// ```rust,ignore
108    /// use qubit_config::Config;
109    ///
110    /// let config = Config::new();
111    /// assert!(config.is_empty());
112    /// ```
113    #[inline]
114    pub fn new() -> Self {
115        Self {
116            description: None,
117            properties: HashMap::new(),
118            enable_variable_substitution: true,
119            max_substitution_depth: crate::constants::DEFAULT_MAX_SUBSTITUTION_DEPTH,
120        }
121    }
122
123    /// Creates a configuration with description
124    ///
125    /// # Parameters
126    ///
127    /// * `description` - Configuration description
128    ///
129    /// # Returns
130    ///
131    /// Returns a new configuration instance
132    ///
133    /// # Examples
134    ///
135    /// ```rust,ignore
136    /// use qubit_config::Config;
137    ///
138    /// let config = Config::with_description("Server Configuration");
139    /// assert_eq!(config.description(), Some("Server Configuration"));
140    /// ```
141    #[inline]
142    pub fn with_description(description: &str) -> Self {
143        Self {
144            description: Some(description.to_string()),
145            properties: HashMap::new(),
146            enable_variable_substitution: true,
147            max_substitution_depth: crate::constants::DEFAULT_MAX_SUBSTITUTION_DEPTH,
148        }
149    }
150
151    // ========================================================================
152    // Basic Property Access
153    // ========================================================================
154
155    /// Gets the configuration description
156    ///
157    /// # Returns
158    ///
159    /// Returns the configuration description as Option
160    #[inline]
161    pub fn description(&self) -> Option<&str> {
162        self.description.as_deref()
163    }
164
165    /// Sets the configuration description
166    ///
167    /// # Parameters
168    ///
169    /// * `description` - Configuration description
170    ///
171    /// # Returns
172    ///
173    /// Nothing.
174    #[inline]
175    pub fn set_description(&mut self, description: Option<String>) {
176        self.description = description;
177    }
178
179    /// Checks if variable substitution is enabled
180    ///
181    /// # Returns
182    ///
183    /// Returns `true` if variable substitution is enabled
184    #[inline]
185    pub fn is_enable_variable_substitution(&self) -> bool {
186        self.enable_variable_substitution
187    }
188
189    /// Sets whether to enable variable substitution
190    ///
191    /// # Parameters
192    ///
193    /// * `enable` - Whether to enable
194    ///
195    /// # Returns
196    ///
197    /// Nothing.
198    #[inline]
199    pub fn set_enable_variable_substitution(&mut self, enable: bool) {
200        self.enable_variable_substitution = enable;
201    }
202
203    /// Gets the maximum depth for variable substitution
204    ///
205    /// # Returns
206    ///
207    /// Returns the maximum depth value
208    #[inline]
209    pub fn max_substitution_depth(&self) -> usize {
210        self.max_substitution_depth
211    }
212
213    /// Creates a read-only prefix view using [`crate::ConfigPrefixView`].
214    ///
215    /// # Parameters
216    ///
217    /// * `prefix` - Prefix
218    ///
219    /// # Returns
220    ///
221    /// Returns a read-only prefix view
222    ///
223    /// # Examples
224    ///
225    /// ```rust,ignore
226    /// use qubit_config::{Config, ConfigReader};
227    ///
228    /// let config = Config::new();
229    /// config.set("server.port", 8080)?;
230    /// config.set("server.host", "localhost")?;
231    ///
232    /// let server = config.prefix_view("server");
233    /// assert_eq!(server.get("port")?, 8080);
234    /// assert_eq!(server.get("host")?, "localhost");
235    /// ```
236    #[inline]
237    pub fn prefix_view(&self, prefix: &str) -> ConfigPrefixView<'_> {
238        ConfigPrefixView::new(self, prefix)
239    }
240
241    /// Sets the maximum depth for variable substitution
242    ///
243    /// # Parameters
244    ///
245    /// * `depth` - Maximum depth
246    ///
247    /// # Returns
248    ///
249    /// Nothing.
250    #[inline]
251    pub fn set_max_substitution_depth(&mut self, depth: usize) {
252        self.max_substitution_depth = depth;
253    }
254
255    // ========================================================================
256    // Configuration Item Management
257    // ========================================================================
258
259    /// Checks if the configuration contains an item with the specified name
260    ///
261    /// # Parameters
262    ///
263    /// * `name` - Configuration item name
264    ///
265    /// # Returns
266    ///
267    /// Returns `true` if the configuration item exists
268    ///
269    /// # Examples
270    ///
271    /// ```rust,ignore
272    /// use qubit_config::Config;
273    ///
274    /// let mut config = Config::new();
275    /// config.set("port", 8080)?;
276    ///
277    /// assert!(config.contains("port"));
278    /// assert!(!config.contains("host"));
279    /// ```
280    #[inline]
281    pub fn contains(&self, name: &str) -> bool {
282        self.properties.contains_key(name)
283    }
284
285    /// Gets a reference to a configuration item
286    ///
287    /// # Parameters
288    ///
289    /// * `name` - Configuration item name
290    ///
291    /// # Returns
292    ///
293    /// Returns Option containing the configuration item
294    #[inline]
295    pub fn get_property(&self, name: &str) -> Option<&Property> {
296        self.properties.get(name)
297    }
298
299    /// Gets a mutable reference to a configuration item
300    ///
301    /// # Parameters
302    ///
303    /// * `name` - Configuration item name
304    ///
305    /// # Returns
306    ///
307    /// Returns mutable Option containing the configuration item
308    #[inline]
309    pub fn get_property_mut(&mut self, name: &str) -> Option<&mut Property> {
310        self.properties.get_mut(name)
311    }
312
313    /// Removes a configuration item
314    ///
315    /// # Parameters
316    ///
317    /// * `name` - Configuration item name
318    ///
319    /// # Returns
320    ///
321    /// Returns the removed configuration item, or None if it doesn't exist
322    ///
323    /// # Examples
324    ///
325    /// ```rust,ignore
326    /// use qubit_config::Config;
327    ///
328    /// let mut config = Config::new();
329    /// config.set("port", 8080)?;
330    ///
331    /// let removed = config.remove("port");
332    /// assert!(removed.is_some());
333    /// assert!(!config.contains("port"));
334    /// ```
335    #[inline]
336    pub fn remove(&mut self, name: &str) -> Option<Property> {
337        self.properties.remove(name)
338    }
339
340    /// Clears all configuration items
341    ///
342    /// # Examples
343    ///
344    /// ```rust,ignore
345    /// use qubit_config::Config;
346    ///
347    /// let mut config = Config::new();
348    /// config.set("port", 8080)?;
349    /// config.set("host", "localhost")?;
350    ///
351    /// config.clear();
352    /// assert!(config.is_empty());
353    /// ```
354    ///
355    /// # Returns
356    ///
357    /// Nothing.
358    #[inline]
359    pub fn clear(&mut self) {
360        self.properties.clear();
361    }
362
363    /// Gets the number of configuration items
364    ///
365    /// # Returns
366    ///
367    /// Returns the number of configuration items
368    #[inline]
369    pub fn len(&self) -> usize {
370        self.properties.len()
371    }
372
373    /// Checks if the configuration is empty
374    ///
375    /// # Returns
376    ///
377    /// Returns `true` if the configuration contains no items
378    #[inline]
379    pub fn is_empty(&self) -> bool {
380        self.properties.is_empty()
381    }
382
383    /// Gets all configuration item names
384    ///
385    /// # Returns
386    ///
387    /// Returns a Vec of configuration item names
388    ///
389    /// # Examples
390    ///
391    /// ```rust,ignore
392    /// use qubit_config::Config;
393    ///
394    /// let mut config = Config::new();
395    /// config.set("port", 8080)?;
396    /// config.set("host", "localhost")?;
397    ///
398    /// let keys = config.keys();
399    /// assert_eq!(keys.len(), 2);
400    /// assert!(keys.contains(&"port".to_string()));
401    /// assert!(keys.contains(&"host".to_string()));
402    /// ```
403    pub fn keys(&self) -> Vec<String> {
404        self.properties.keys().cloned().collect()
405    }
406
407    // ========================================================================
408    // Core Generic Methods
409    // ========================================================================
410
411    /// Gets a configuration value.
412    ///
413    /// Core read API with type inference.
414    ///
415    /// # Note
416    ///
417    /// This method does not perform variable substitution for string types. If
418    /// you need variable substitution, use [`Self::get_string`].
419    ///
420    /// # Type Parameters
421    ///
422    /// * `T` - Target type, must implement `FromPropertyValue` trait
423    ///
424    /// # Parameters
425    ///
426    /// * `name` - Configuration item name
427    ///
428    /// # Returns
429    ///
430    /// The value of the specified type on success, or a [`ConfigError`] on
431    /// failure.
432    ///
433    /// # Errors
434    ///
435    /// - [`ConfigError::PropertyNotFound`] if the key does not exist
436    /// - [`ConfigError::PropertyHasNoValue`] if the property has no value
437    /// - [`ConfigError::TypeMismatch`] if the type does not match
438    ///
439    /// # Examples
440    ///
441    /// ```rust,ignore
442    /// use qubit_config::Config;
443    ///
444    /// let mut config = Config::new();
445    /// config.set("port", 8080)?;
446    /// config.set("host", "localhost")?;
447    ///
448    /// // Method 1: Type inference
449    /// let port: i32 = config.get("port")?;
450    /// let host: String = config.get("host")?;
451    ///
452    /// // Method 2: Turbofish
453    /// let port = config.get::<i32>("port")?;
454    /// let host = config.get::<String>("host")?;
455    ///
456    /// // Method 3: Inference from usage
457    /// fn start_server(port: i32, host: String) { }
458    /// start_server(config.get("port")?, config.get("host")?);
459    /// ```
460    pub fn get<T>(&self, name: &str) -> ConfigResult<T>
461    where
462        MultiValues: MultiValuesFirstGetter<T>,
463    {
464        let property = self
465            .properties
466            .get(name)
467            .ok_or_else(|| ConfigError::PropertyNotFound(name.to_string()))?;
468
469        property.get_first::<T>().map_err(|e| match e {
470            ValueError::NoValue => ConfigError::PropertyHasNoValue(name.to_string()),
471            ValueError::TypeMismatch { expected, actual } => {
472                ConfigError::type_mismatch_at(name, expected, actual)
473            }
474            ValueError::ConversionFailed { from, to } => {
475                ConfigError::conversion_error_at(name, format!("From {from} to {to}"))
476            }
477            ValueError::ConversionError(msg) => ConfigError::conversion_error_at(name, msg),
478            ValueError::IndexOutOfBounds { index, len } => {
479                ConfigError::IndexOutOfBounds { index, len }
480            }
481            ValueError::JsonSerializationError(msg) => {
482                ConfigError::conversion_error_at(name, format!("JSON serialization error: {msg}"))
483            }
484            ValueError::JsonDeserializationError(msg) => {
485                ConfigError::conversion_error_at(name, format!("JSON deserialization error: {msg}"))
486            }
487        })
488    }
489
490    /// Gets a configuration value or returns a default value
491    ///
492    /// Returns `default` if the key is missing or if reading the value fails.
493    ///
494    /// # Type Parameters
495    ///
496    /// * `T` - Target type, must implement `FromPropertyValue` trait
497    ///
498    /// # Parameters
499    ///
500    /// * `name` - Configuration item name
501    /// * `default` - Default value
502    ///
503    /// # Returns
504    ///
505    /// Returns the configuration value or default value
506    ///
507    /// # Examples
508    ///
509    /// ```rust,ignore
510    /// use qubit_config::Config;
511    ///
512    /// let config = Config::new();
513    ///
514    /// let port: i32 = config.get_or("port", 8080);
515    /// let host: String = config.get_or("host", "localhost".to_string());
516    ///
517    /// assert_eq!(port, 8080);
518    /// assert_eq!(host, "localhost");
519    /// ```
520    pub fn get_or<T>(&self, name: &str, default: T) -> T
521    where
522        MultiValues: MultiValuesFirstGetter<T>,
523    {
524        self.get(name).unwrap_or(default)
525    }
526
527    /// Gets a list of configuration values
528    ///
529    /// Gets all values of a configuration item (multi-value configuration).
530    ///
531    /// # Type Parameters
532    ///
533    /// * `T` - Target type, must implement `FromPropertyValue` trait
534    ///
535    /// # Parameters
536    ///
537    /// * `name` - Configuration item name
538    ///
539    /// # Returns
540    ///
541    /// Returns a list of values on success, or an error on failure
542    ///
543    /// # Examples
544    ///
545    /// ```rust,ignore
546    /// use qubit_config::Config;
547    ///
548    /// let mut config = Config::new();
549    /// config.set("ports", vec![8080, 8081, 8082])?;
550    ///
551    /// let ports: Vec<i32> = config.get_list("ports")?;
552    /// assert_eq!(ports, vec![8080, 8081, 8082]);
553    /// ```
554    pub fn get_list<T>(&self, name: &str) -> ConfigResult<Vec<T>>
555    where
556        MultiValues: MultiValuesGetter<T>,
557    {
558        let property = self
559            .properties
560            .get(name)
561            .ok_or_else(|| ConfigError::PropertyNotFound(name.to_string()))?;
562
563        property.get::<T>().map_err(|e| match e {
564            ValueError::NoValue => ConfigError::PropertyHasNoValue(name.to_string()),
565            ValueError::TypeMismatch { expected, actual } => {
566                ConfigError::type_mismatch_at(name, expected, actual)
567            }
568            ValueError::ConversionFailed { from, to } => {
569                ConfigError::conversion_error_at(name, format!("From {from} to {to}"))
570            }
571            ValueError::ConversionError(msg) => ConfigError::conversion_error_at(name, msg),
572            ValueError::IndexOutOfBounds { index, len } => {
573                ConfigError::IndexOutOfBounds { index, len }
574            }
575            ValueError::JsonSerializationError(msg) => {
576                ConfigError::conversion_error_at(name, format!("JSON serialization error: {msg}"))
577            }
578            ValueError::JsonDeserializationError(msg) => {
579                ConfigError::conversion_error_at(name, format!("JSON deserialization error: {msg}"))
580            }
581        })
582    }
583
584    /// Sets a configuration value
585    ///
586    /// This is the core method for setting configuration values, supporting
587    /// type inference.
588    ///
589    /// # Type Parameters
590    ///
591    /// * `T` - Element type, automatically inferred from the `values` parameter
592    ///
593    /// # Parameters
594    ///
595    /// * `name` - Configuration item name
596    /// * `values` - Value to store; supports `T`, `Vec<T>`, `&[T]`, and related
597    ///   forms accepted by [`MultiValues`] setters
598    ///
599    /// # Returns
600    ///
601    /// Returns Ok(()) on success, or an error on failure
602    ///
603    /// # Errors
604    ///
605    /// - [`ConfigError::PropertyIsFinal`] if the property is marked final
606    ///
607    /// # Examples
608    ///
609    /// ```rust,ignore
610    /// use qubit_config::Config;
611    ///
612    /// let mut config = Config::new();
613    ///
614    /// // Set single values (type auto-inference)
615    /// config.set("port", 8080)?;                    // T inferred as i32
616    /// config.set("host", "localhost")?;
617    /// // T inferred as String; &str is converted
618    /// config.set("debug", true)?;                   // T inferred as bool
619    /// config.set("timeout", 30.5)?;                 // T inferred as f64
620    ///
621    /// // Set multiple values (type auto-inference)
622    /// config.set("ports", vec![8080, 8081, 8082])?; // T inferred as i32
623    /// config.set("hosts", &["host1", "host2"])?;
624    /// // T inferred as &str (then converted)
625    /// ```
626    pub fn set<S>(&mut self, name: &str, values: S) -> ConfigResult<()>
627    where
628        S: for<'a> MultiValuesSetArg<'a>,
629        <S as MultiValuesSetArg<'static>>::Item: Clone,
630        MultiValues: MultiValuesSetter<<S as MultiValuesSetArg<'static>>::Item>
631            + MultiValuesSetterSlice<<S as MultiValuesSetArg<'static>>::Item>
632            + MultiValuesSingleSetter<<S as MultiValuesSetArg<'static>>::Item>,
633    {
634        // Check if it's a final value
635        if let Some(prop) = self.properties.get(name) {
636            if prop.is_final() {
637                return Err(ConfigError::PropertyIsFinal(name.to_string()));
638            }
639        }
640        let property = self
641            .properties
642            .entry(name.to_string())
643            .or_insert_with(|| Property::new(name));
644
645        property.set(values).map_err(ConfigError::from)
646    }
647
648    /// Adds configuration values
649    ///
650    /// Adds values to an existing configuration item (multi-value properties).
651    ///
652    /// # Type Parameters
653    ///
654    /// * `T` - Element type, automatically inferred from the `values` parameter
655    ///
656    /// # Parameters
657    ///
658    /// * `name` - Configuration item name
659    /// * `values` - Values to append; supports the same forms as [`Self::set`]
660    ///
661    /// # Returns
662    ///
663    /// Returns Ok(()) on success, or an error on failure
664    ///
665    /// # Examples
666    ///
667    /// ```rust,ignore
668    /// use qubit_config::Config;
669    ///
670    /// let mut config = Config::new();
671    /// config.set("port", 8080)?;                    // Set initial value
672    /// config.add("port", 8081)?;                    // Add single value
673    /// config.add("port", vec![8082, 8083])?;        // Add multiple values
674    /// config.add("port", &[8084, 8085])?;          // Add slice
675    ///
676    /// let ports: Vec<i32> = config.get_list("port")?;
677    /// assert_eq!(ports, vec![8080, 8081, 8082, 8083, 8084, 8085]);
678    /// ```
679    pub fn add<S>(&mut self, name: &str, values: S) -> ConfigResult<()>
680    where
681        S: for<'a> MultiValuesAddArg<'a, Item = <S as MultiValuesSetArg<'static>>::Item>
682            + for<'a> MultiValuesSetArg<'a>,
683        <S as MultiValuesSetArg<'static>>::Item: Clone,
684        MultiValues: MultiValuesAdder<<S as MultiValuesSetArg<'static>>::Item>
685            + MultiValuesMultiAdder<<S as MultiValuesSetArg<'static>>::Item>
686            + MultiValuesSetter<<S as MultiValuesSetArg<'static>>::Item>
687            + MultiValuesSetterSlice<<S as MultiValuesSetArg<'static>>::Item>
688            + MultiValuesSingleSetter<<S as MultiValuesSetArg<'static>>::Item>,
689    {
690        // Check if it's a final value
691        if let Some(prop) = self.properties.get(name) {
692            if prop.is_final() {
693                return Err(ConfigError::PropertyIsFinal(name.to_string()));
694            }
695        }
696
697        if let Some(property) = self.properties.get_mut(name) {
698            property.add(values).map_err(ConfigError::from)
699        } else {
700            let mut property = Property::new(name);
701            // Note: property.set() always returns Ok(()) in current MultiValues implementation,
702            // as it unconditionally replaces the entire value without any validation.
703            // We explicitly ignore the result to improve code coverage and avoid unreachable error paths.
704            let _ = property.set(values);
705            self.properties.insert(name.to_string(), property);
706            Ok(())
707        }
708    }
709
710    // ========================================================================
711    // String Special Handling (Variable Substitution)
712    // ========================================================================
713
714    /// Gets a string configuration value (with variable substitution)
715    ///
716    /// If variable substitution is enabled, replaces `${var_name}` placeholders
717    /// in the stored string.
718    ///
719    /// # Parameters
720    ///
721    /// * `name` - Configuration item name
722    ///
723    /// # Returns
724    ///
725    /// Returns the string value on success, or an error on failure
726    ///
727    /// # Examples
728    ///
729    /// ```rust,ignore
730    /// use qubit_config::Config;
731    ///
732    /// let mut config = Config::new();
733    /// config.set("base_url", "http://localhost")?;
734    /// config.set("api_url", "${base_url}/api")?;
735    ///
736    /// let api_url = config.get_string("api_url")?;
737    /// assert_eq!(api_url, "http://localhost/api");
738    /// ```
739    pub fn get_string(&self, name: &str) -> ConfigResult<String> {
740        let value: String = self.get(name)?;
741        if self.enable_variable_substitution {
742            utils::substitute_variables(&value, self, self.max_substitution_depth)
743        } else {
744            Ok(value)
745        }
746    }
747
748    /// Gets a string with substitution, or `default` if reading fails.
749    ///
750    /// # Parameters
751    ///
752    /// * `name` - Configuration item name
753    /// * `default` - Default value
754    ///
755    /// # Returns
756    ///
757    /// Returns the string value or default value
758    ///
759    pub fn get_string_or(&self, name: &str, default: &str) -> String {
760        self.get_string(name)
761            .unwrap_or_else(|_| default.to_string())
762    }
763
764    /// Gets a list of string configuration values (with variable substitution)
765    ///
766    /// If variable substitution is enabled, runs it on each list element
767    /// (same `${var_name}` rules as [`Self::get_string`]).
768    ///
769    /// # Parameters
770    ///
771    /// * `name` - Configuration item name
772    ///
773    /// # Returns
774    ///
775    /// Returns a list of strings on success, or an error on failure
776    ///
777    /// # Examples
778    ///
779    /// ```rust,ignore
780    /// use qubit_config::Config;
781    ///
782    /// let mut config = Config::new();
783    /// config.set("base_path", "/opt/app")?;
784    /// config.set("paths", vec!["${base_path}/bin", "${base_path}/lib"])?;
785    ///
786    /// let paths = config.get_string_list("paths")?;
787    /// assert_eq!(paths, vec!["/opt/app/bin", "/opt/app/lib"]);
788    /// ```
789    pub fn get_string_list(&self, name: &str) -> ConfigResult<Vec<String>> {
790        let values: Vec<String> = self.get_list(name)?;
791        if self.enable_variable_substitution {
792            values
793                .into_iter()
794                .map(|v| utils::substitute_variables(&v, self, self.max_substitution_depth))
795                .collect()
796        } else {
797            Ok(values)
798        }
799    }
800
801    /// Gets a list of string configuration values or returns a default value
802    /// (with variable substitution)
803    ///
804    /// # Parameters
805    ///
806    /// * `name` - Configuration item name
807    /// * `default` - Default value (can be array slice or vec)
808    ///
809    /// # Returns
810    ///
811    /// Returns the list of strings or default value
812    ///
813    /// # Examples
814    ///
815    /// ```rust,ignore
816    /// use qubit_config::Config;
817    ///
818    /// let config = Config::new();
819    ///
820    /// // Using array slice
821    /// let paths = config.get_string_list_or("paths", &["/default/path"]);
822    /// assert_eq!(paths, vec!["/default/path"]);
823    ///
824    /// // Using vec
825    /// let paths = config.get_string_list_or("paths", &vec!["path1", "path2"]);
826    /// assert_eq!(paths, vec!["path1", "path2"]);
827    /// ```
828    pub fn get_string_list_or(&self, name: &str, default: &[&str]) -> Vec<String> {
829        self.get_string_list(name)
830            .unwrap_or_else(|_| default.iter().map(|s| s.to_string()).collect())
831    }
832
833    // ========================================================================
834    // Configuration Source Integration
835    // ========================================================================
836
837    /// Merges configuration from a `ConfigSource`
838    ///
839    /// Loads all key-value pairs from the given source and merges them into
840    /// this configuration. Existing non-final properties are overwritten;
841    /// final properties are preserved and cause an error if the source tries
842    /// to overwrite them.
843    ///
844    /// # Parameters
845    ///
846    /// * `source` - The configuration source to load from
847    ///
848    /// # Returns
849    ///
850    /// Returns `Ok(())` on success, or a `ConfigError` on failure
851    ///
852    /// # Examples
853    ///
854    /// ```rust,ignore
855    /// use qubit_config::Config;
856    /// use qubit_config::source::{
857    ///     CompositeConfigSource, ConfigSource,
858    ///     EnvConfigSource, TomlConfigSource,
859    /// };
860    ///
861    /// let mut composite = CompositeConfigSource::new();
862    /// composite.add(TomlConfigSource::from_file("config.toml"));
863    /// composite.add(EnvConfigSource::with_prefix("APP_"));
864    ///
865    /// let mut config = Config::new();
866    /// config.merge_from_source(&composite).unwrap();
867    /// ```
868    #[inline]
869    pub fn merge_from_source(&mut self, source: &dyn ConfigSource) -> ConfigResult<()> {
870        source.load(self)
871    }
872
873    // ========================================================================
874    // Prefix Traversal and Sub-tree Extraction (v0.4.0)
875    // ========================================================================
876
877    /// Iterates over all configuration entries as `(key, &Property)` pairs.
878    ///
879    /// # Returns
880    ///
881    /// An iterator yielding `(&str, &Property)` tuples.
882    ///
883    /// # Examples
884    ///
885    /// ```rust,ignore
886    /// use qubit_config::Config;
887    ///
888    /// let mut config = Config::new();
889    /// config.set("host", "localhost")?;
890    /// config.set("port", 8080)?;
891    ///
892    /// for (key, prop) in config.iter() {
893    ///     println!("{} = {:?}", key, prop);
894    /// }
895    /// ```
896    #[inline]
897    pub fn iter(&self) -> impl Iterator<Item = (&str, &Property)> {
898        self.properties.iter().map(|(k, v)| (k.as_str(), v))
899    }
900
901    /// Iterates over all configuration entries whose key starts with `prefix`.
902    ///
903    /// # Parameters
904    ///
905    /// * `prefix` - The key prefix to filter by (e.g., `"http."`)
906    ///
907    /// # Returns
908    ///
909    /// An iterator of `(&str, &Property)` whose keys start with `prefix`.
910    ///
911    /// # Examples
912    ///
913    /// ```rust,ignore
914    /// use qubit_config::Config;
915    ///
916    /// let mut config = Config::new();
917    /// config.set("http.host", "localhost")?;
918    /// config.set("http.port", 8080)?;
919    /// config.set("db.host", "dbhost")?;
920    ///
921    /// let http_entries: Vec<_> = config.iter_prefix("http.").collect();
922    /// assert_eq!(http_entries.len(), 2);
923    /// ```
924    #[inline]
925    pub fn iter_prefix<'a>(
926        &'a self,
927        prefix: &'a str,
928    ) -> impl Iterator<Item = (&'a str, &'a Property)> {
929        self.properties
930            .iter()
931            .filter(move |(k, _)| k.starts_with(prefix))
932            .map(|(k, v)| (k.as_str(), v))
933    }
934
935    /// Returns `true` if any configuration key starts with `prefix`.
936    ///
937    /// # Parameters
938    ///
939    /// * `prefix` - The key prefix to check
940    ///
941    /// # Returns
942    ///
943    /// `true` if at least one key starts with `prefix`, `false` otherwise.
944    ///
945    /// # Examples
946    ///
947    /// ```rust,ignore
948    /// use qubit_config::Config;
949    ///
950    /// let mut config = Config::new();
951    /// config.set("http.host", "localhost")?;
952    ///
953    /// assert!(config.contains_prefix("http."));
954    /// assert!(!config.contains_prefix("db."));
955    /// ```
956    #[inline]
957    pub fn contains_prefix(&self, prefix: &str) -> bool {
958        self.properties.keys().any(|k| k.starts_with(prefix))
959    }
960
961    /// Extracts a sub-configuration for keys matching `prefix`.
962    ///
963    /// # Parameters
964    ///
965    /// * `prefix` - The key prefix to extract (e.g., `"http"`)
966    /// * `strip_prefix` - When `true`, removes `prefix` and the following dot
967    ///   from keys in the result; when `false`, keys are copied unchanged.
968    ///
969    /// # Returns
970    ///
971    /// A new `Config` containing only the matching entries.
972    ///
973    /// # Examples
974    ///
975    /// ```rust,ignore
976    /// use qubit_config::Config;
977    ///
978    /// let mut config = Config::new();
979    /// config.set("http.host", "localhost")?;
980    /// config.set("http.port", 8080)?;
981    /// config.set("db.host", "dbhost")?;
982    ///
983    /// let http_config = config.subconfig("http", true)?;
984    /// assert!(http_config.contains("host"));
985    /// assert!(http_config.contains("port"));
986    /// assert!(!http_config.contains("db.host"));
987    /// ```
988    pub fn subconfig(&self, prefix: &str, strip_prefix: bool) -> ConfigResult<Config> {
989        let mut sub = Config::new();
990        sub.enable_variable_substitution = self.enable_variable_substitution;
991        sub.max_substitution_depth = self.max_substitution_depth;
992
993        // Empty prefix means "all keys"
994        if prefix.is_empty() {
995            for (k, v) in &self.properties {
996                sub.properties.insert(k.clone(), v.clone());
997            }
998            return Ok(sub);
999        }
1000
1001        let full_prefix = format!("{prefix}.");
1002
1003        for (k, v) in &self.properties {
1004            if k == prefix || k.starts_with(&full_prefix) {
1005                let new_key = if strip_prefix {
1006                    if k == prefix {
1007                        prefix.to_string()
1008                    } else {
1009                        k[full_prefix.len()..].to_string()
1010                    }
1011                } else {
1012                    k.clone()
1013                };
1014                sub.properties.insert(new_key, v.clone());
1015            }
1016        }
1017
1018        Ok(sub)
1019    }
1020
1021    // ========================================================================
1022    // Optional and Null Semantics (v0.4.0)
1023    // ========================================================================
1024
1025    /// Returns `true` if the property exists but has no value (empty / null).
1026    ///
1027    /// This distinguishes between:
1028    /// - Key does not exist → `contains()` returns `false`
1029    /// - Key exists but is empty/null → `is_null()` returns `true`
1030    ///
1031    /// # Parameters
1032    ///
1033    /// * `name` - Configuration item name
1034    ///
1035    /// # Returns
1036    ///
1037    /// `true` if the property exists and has no values (is empty).
1038    ///
1039    /// # Examples
1040    ///
1041    /// ```rust,ignore
1042    /// use qubit_config::{Config, Property};
1043    /// use qubit_value::MultiValues;
1044    /// use qubit_common::DataType;
1045    ///
1046    /// let mut config = Config::new();
1047    /// config.properties_mut().insert(
1048    ///     "nullable".to_string(),
1049    ///     Property::with_value(
1050    ///         "nullable",
1051    ///         MultiValues::Empty(DataType::String),
1052    ///     ),
1053    /// );
1054    ///
1055    /// assert!(config.is_null("nullable"));
1056    /// assert!(!config.is_null("missing"));
1057    /// ```
1058    pub fn is_null(&self, name: &str) -> bool {
1059        self.properties
1060            .get(name)
1061            .map(|p| p.is_empty())
1062            .unwrap_or(false)
1063    }
1064
1065    /// Gets an optional configuration value.
1066    ///
1067    /// Distinguishes between three states:
1068    /// - `Ok(Some(value))` – key exists and has a value
1069    /// - `Ok(None)` – key does not exist, **or** exists but is null/empty
1070    /// - `Err(e)` – key exists and has a value, but conversion failed
1071    ///
1072    /// # Type Parameters
1073    ///
1074    /// * `T` - Target type
1075    ///
1076    /// # Parameters
1077    ///
1078    /// * `name` - Configuration item name
1079    ///
1080    /// # Returns
1081    ///
1082    /// `Ok(Some(value))`, `Ok(None)`, or `Err` as described above.
1083    ///
1084    /// # Examples
1085    ///
1086    /// ```rust,ignore
1087    /// use qubit_config::Config;
1088    ///
1089    /// let mut config = Config::new();
1090    /// config.set("port", 8080)?;
1091    ///
1092    /// let port: Option<i32> = config.get_optional("port")?;
1093    /// assert_eq!(port, Some(8080));
1094    ///
1095    /// let missing: Option<i32> = config.get_optional("missing")?;
1096    /// assert_eq!(missing, None);
1097    /// ```
1098    pub fn get_optional<T>(&self, name: &str) -> ConfigResult<Option<T>>
1099    where
1100        MultiValues: MultiValuesFirstGetter<T>,
1101    {
1102        match self.properties.get(name) {
1103            None => Ok(None),
1104            Some(prop) if prop.is_empty() => Ok(None),
1105            Some(_) => self.get::<T>(name).map(Some),
1106        }
1107    }
1108
1109    /// Gets an optional list of configuration values.
1110    ///
1111    /// See also [`Self::get_optional_string_list`] for optional string lists
1112    /// with variable substitution.
1113    ///
1114    /// Distinguishes between three states:
1115    /// - `Ok(Some(vec))` – key exists and has values
1116    /// - `Ok(None)` – key does not exist, **or** exists but is null/empty
1117    /// - `Err(e)` – key exists and has values, but conversion failed
1118    ///
1119    /// # Type Parameters
1120    ///
1121    /// * `T` - Target element type
1122    ///
1123    /// # Parameters
1124    ///
1125    /// * `name` - Configuration item name
1126    ///
1127    /// # Returns
1128    ///
1129    /// `Ok(Some(vec))`, `Ok(None)`, or `Err` as described above.
1130    ///
1131    /// # Examples
1132    ///
1133    /// ```rust,ignore
1134    /// use qubit_config::Config;
1135    ///
1136    /// let mut config = Config::new();
1137    /// config.set("ports", vec![8080, 8081])?;
1138    ///
1139    /// let ports: Option<Vec<i32>> = config.get_optional_list("ports")?;
1140    /// assert_eq!(ports, Some(vec![8080, 8081]));
1141    ///
1142    /// let missing: Option<Vec<i32>> = config.get_optional_list("missing")?;
1143    /// assert_eq!(missing, None);
1144    /// ```
1145    pub fn get_optional_list<T>(&self, name: &str) -> ConfigResult<Option<Vec<T>>>
1146    where
1147        MultiValues: MultiValuesGetter<T>,
1148    {
1149        match self.properties.get(name) {
1150            None => Ok(None),
1151            Some(prop) if prop.is_empty() => Ok(None),
1152            Some(_) => self.get_list::<T>(name).map(Some),
1153        }
1154    }
1155
1156    /// Gets an optional string (with variable substitution when enabled).
1157    ///
1158    /// Same semantics as [`Self::get_optional`], but values are read via
1159    /// [`Self::get_string`], so `${...}` substitution applies when enabled.
1160    ///
1161    /// # Parameters
1162    ///
1163    /// * `name` - Configuration item name
1164    ///
1165    /// # Returns
1166    ///
1167    /// `Ok(Some(s))`, `Ok(None)`, or `Err` as for [`Self::get_optional`].
1168    ///
1169    /// # Examples
1170    ///
1171    /// ```rust,ignore
1172    /// use qubit_config::Config;
1173    ///
1174    /// let mut config = Config::new();
1175    /// config.set("base", "http://localhost")?;
1176    /// config.set("api", "${base}/api")?;
1177    ///
1178    /// let api = config.get_optional_string("api")?;
1179    /// assert_eq!(api.as_deref(), Some("http://localhost/api"));
1180    ///
1181    /// let missing = config.get_optional_string("missing")?;
1182    /// assert_eq!(missing, None);
1183    /// ```
1184    pub fn get_optional_string(&self, name: &str) -> ConfigResult<Option<String>> {
1185        match self.properties.get(name) {
1186            None => Ok(None),
1187            Some(prop) if prop.is_empty() => Ok(None),
1188            Some(_) => self.get_string(name).map(Some),
1189        }
1190    }
1191
1192    /// Gets an optional string list (substitution per element when enabled).
1193    ///
1194    /// Same semantics as [`Self::get_optional_list`], but elements use
1195    /// [`Self::get_string_list`] (same `${...}` rules as [`Self::get_string`]).
1196    ///
1197    /// # Parameters
1198    ///
1199    /// * `name` - Configuration item name
1200    ///
1201    /// # Returns
1202    ///
1203    /// `Ok(Some(vec))`, `Ok(None)`, or `Err` like [`Self::get_optional_list`].
1204    ///
1205    /// # Examples
1206    ///
1207    /// ```rust,ignore
1208    /// use qubit_config::Config;
1209    ///
1210    /// let mut config = Config::new();
1211    /// config.set("root", "/opt/app")?;
1212    /// config.set("paths", vec!["${root}/bin", "${root}/lib"])?;
1213    ///
1214    /// let paths = config.get_optional_string_list("paths")?;
1215    /// assert_eq!(
1216    ///     paths,
1217    ///     Some(vec![
1218    ///         "/opt/app/bin".to_string(),
1219    ///         "/opt/app/lib".to_string(),
1220    ///     ]),
1221    /// );
1222    /// ```
1223    pub fn get_optional_string_list(&self, name: &str) -> ConfigResult<Option<Vec<String>>> {
1224        match self.properties.get(name) {
1225            None => Ok(None),
1226            Some(prop) if prop.is_empty() => Ok(None),
1227            Some(_) => self.get_string_list(name).map(Some),
1228        }
1229    }
1230
1231    // ========================================================================
1232    // Structured Config Deserialization (v0.4.0)
1233    // ========================================================================
1234
1235    /// Deserializes the subtree at `prefix` into `T` using `serde`.
1236    ///
1237    /// Keys under `prefix` (prefix and trailing dot removed) form a flat map
1238    /// for `serde`, for example:
1239    ///
1240    /// ```rust,ignore
1241    /// #[derive(serde::Deserialize)]
1242    /// struct HttpOptions {
1243    ///     host: String,
1244    ///     port: u16,
1245    /// }
1246    /// ```
1247    ///
1248    /// can be populated from config keys `http.host` and `http.port` by calling
1249    /// `config.deserialize::<HttpOptions>("http")`.
1250    ///
1251    /// # Type Parameters
1252    ///
1253    /// * `T` - Target type, must implement `serde::de::DeserializeOwned`
1254    ///
1255    /// # Parameters
1256    ///
1257    /// * `prefix` - Key prefix for the struct fields (`""` means the root map)
1258    ///
1259    /// # Returns
1260    ///
1261    /// The deserialized `T`, or a [`ConfigError::DeserializeError`] on failure.
1262    ///
1263    /// # Examples
1264    ///
1265    /// ```rust,ignore
1266    /// use qubit_config::Config;
1267    /// use serde::Deserialize;
1268    ///
1269    /// #[derive(Deserialize, Debug, PartialEq)]
1270    /// struct Server {
1271    ///     host: String,
1272    ///     port: i32,
1273    /// }
1274    ///
1275    /// let mut config = Config::new();
1276    /// config.set("server.host", "localhost")?;
1277    /// config.set("server.port", 8080)?;
1278    ///
1279    /// let server: Server = config.deserialize("server")?;
1280    /// assert_eq!(server.host, "localhost");
1281    /// assert_eq!(server.port, 8080);
1282    /// ```
1283    pub fn deserialize<T>(&self, prefix: &str) -> ConfigResult<T>
1284    where
1285        T: serde::de::DeserializeOwned,
1286    {
1287        use serde_json::{Map, Value as JsonValue};
1288
1289        let sub = self.subconfig(prefix, true)?;
1290
1291        let mut map = Map::new();
1292        for (key, prop) in &sub.properties {
1293            let json_val = property_to_json_value(prop);
1294            map.insert(key.clone(), json_val);
1295        }
1296
1297        let json_obj = JsonValue::Object(map);
1298
1299        serde_json::from_value(json_obj).map_err(|e| ConfigError::DeserializeError {
1300            path: prefix.to_string(),
1301            message: e.to_string(),
1302        })
1303    }
1304
1305    /// Returns a mutable reference to the internal properties map.
1306    ///
1307    /// For advanced use cases, e.g. inserting null/empty properties that
1308    /// [`Self::set`] cannot represent alone.
1309    ///
1310    /// # Returns
1311    ///
1312    /// A mutable reference to the backing [`HashMap`].
1313    #[inline]
1314    pub fn properties_mut(&mut self) -> &mut HashMap<String, Property> {
1315        &mut self.properties
1316    }
1317}
1318
1319impl ConfigReader for Config {
1320    #[inline]
1321    fn is_enable_variable_substitution(&self) -> bool {
1322        Config::is_enable_variable_substitution(self)
1323    }
1324
1325    #[inline]
1326    fn max_substitution_depth(&self) -> usize {
1327        Config::max_substitution_depth(self)
1328    }
1329
1330    #[inline]
1331    fn description(&self) -> Option<&str> {
1332        Config::description(self)
1333    }
1334
1335    #[inline]
1336    fn get_property(&self, name: &str) -> Option<&Property> {
1337        Config::get_property(self, name)
1338    }
1339
1340    #[inline]
1341    fn len(&self) -> usize {
1342        Config::len(self)
1343    }
1344
1345    #[inline]
1346    fn is_empty(&self) -> bool {
1347        Config::is_empty(self)
1348    }
1349
1350    fn keys(&self) -> Vec<String> {
1351        Config::keys(self)
1352    }
1353
1354    #[inline]
1355    fn contains(&self, name: &str) -> bool {
1356        Config::contains(self, name)
1357    }
1358
1359    #[inline]
1360    fn get<T>(&self, name: &str) -> ConfigResult<T>
1361    where
1362        MultiValues: MultiValuesFirstGetter<T>,
1363    {
1364        Config::get(self, name)
1365    }
1366
1367    #[inline]
1368    fn get_list<T>(&self, name: &str) -> ConfigResult<Vec<T>>
1369    where
1370        MultiValues: MultiValuesGetter<T>,
1371    {
1372        Config::get_list(self, name)
1373    }
1374
1375    #[inline]
1376    fn get_optional<T>(&self, name: &str) -> ConfigResult<Option<T>>
1377    where
1378        MultiValues: MultiValuesFirstGetter<T>,
1379    {
1380        Config::get_optional(self, name)
1381    }
1382
1383    #[inline]
1384    fn get_optional_list<T>(&self, name: &str) -> ConfigResult<Option<Vec<T>>>
1385    where
1386        MultiValues: MultiValuesGetter<T>,
1387    {
1388        Config::get_optional_list(self, name)
1389    }
1390
1391    #[inline]
1392    fn contains_prefix(&self, prefix: &str) -> bool {
1393        Config::contains_prefix(self, prefix)
1394    }
1395
1396    #[inline]
1397    fn iter_prefix<'a>(
1398        &'a self,
1399        prefix: &'a str,
1400    ) -> Box<dyn Iterator<Item = (&'a str, &'a Property)> + 'a> {
1401        Box::new(Config::iter_prefix(self, prefix))
1402    }
1403
1404    #[inline]
1405    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (&'a str, &'a Property)> + 'a> {
1406        Box::new(Config::iter(self))
1407    }
1408
1409    #[inline]
1410    fn is_null(&self, name: &str) -> bool {
1411        Config::is_null(self, name)
1412    }
1413
1414    #[inline]
1415    fn subconfig(&self, prefix: &str, strip_prefix: bool) -> ConfigResult<Config> {
1416        Config::subconfig(self, prefix, strip_prefix)
1417    }
1418
1419    #[inline]
1420    fn deserialize<T>(&self, prefix: &str) -> ConfigResult<T>
1421    where
1422        T: serde::de::DeserializeOwned,
1423    {
1424        Config::deserialize(self, prefix)
1425    }
1426
1427    #[inline]
1428    fn prefix_view(&self, prefix: &str) -> ConfigPrefixView<'_> {
1429        Config::prefix_view(self, prefix)
1430    }
1431}
1432
1433/// Converts a [`Property`] into [`serde_json::Value`] (for
1434/// [`Config::deserialize`]).
1435///
1436/// # Parameters
1437///
1438/// * `prop` - Source property.
1439///
1440/// # Returns
1441///
1442/// JSON null, scalar, array, or object matching the stored [`MultiValues`].
1443fn property_to_json_value(prop: &Property) -> serde_json::Value {
1444    use qubit_value::MultiValues;
1445    use serde_json::Value as JsonValue;
1446
1447    let mv = prop.value();
1448
1449    match mv {
1450        MultiValues::Empty(_) => JsonValue::Null,
1451        MultiValues::Bool(v) => {
1452            if v.len() == 1 {
1453                JsonValue::Bool(v[0])
1454            } else {
1455                JsonValue::Array(v.iter().map(|b| JsonValue::Bool(*b)).collect())
1456            }
1457        }
1458        MultiValues::Int8(v) => scalar_or_array(v, |x| JsonValue::Number((*x).into())),
1459        MultiValues::Int16(v) => scalar_or_array(v, |x| JsonValue::Number((*x).into())),
1460        MultiValues::Int32(v) => scalar_or_array(v, |x| JsonValue::Number((*x).into())),
1461        MultiValues::Int64(v) => scalar_or_array(v, |x| JsonValue::Number((*x).into())),
1462        MultiValues::IntSize(v) => scalar_or_array(v, |x| {
1463            JsonValue::Number(serde_json::Number::from(*x as i64))
1464        }),
1465        MultiValues::UInt8(v) => scalar_or_array(v, |x| JsonValue::Number((*x).into())),
1466        MultiValues::UInt16(v) => scalar_or_array(v, |x| JsonValue::Number((*x).into())),
1467        MultiValues::UInt32(v) => scalar_or_array(v, |x| JsonValue::Number((*x).into())),
1468        MultiValues::UInt64(v) => scalar_or_array(v, |x| JsonValue::Number((*x).into())),
1469        MultiValues::UIntSize(v) => scalar_or_array(v, |x| {
1470            JsonValue::Number(serde_json::Number::from(*x as u64))
1471        }),
1472        MultiValues::Float32(v) => scalar_or_array(v, |x| {
1473            serde_json::Number::from_f64(*x as f64)
1474                .map(JsonValue::Number)
1475                .unwrap_or(JsonValue::Null)
1476        }),
1477        MultiValues::Float64(v) => scalar_or_array(v, |x| {
1478            serde_json::Number::from_f64(*x)
1479                .map(JsonValue::Number)
1480                .unwrap_or(JsonValue::Null)
1481        }),
1482        MultiValues::String(v) => scalar_or_array(v, |x| JsonValue::String(x.clone())),
1483        MultiValues::Duration(v) => {
1484            scalar_or_array(v, |x| JsonValue::String(format!("{}ms", x.as_millis())))
1485        }
1486        MultiValues::Url(v) => scalar_or_array(v, |x| JsonValue::String(x.to_string())),
1487        MultiValues::StringMap(v) => {
1488            if v.len() == 1 {
1489                let obj: serde_json::Map<String, JsonValue> = v[0]
1490                    .iter()
1491                    .map(|(k, val)| (k.clone(), JsonValue::String(val.clone())))
1492                    .collect();
1493                JsonValue::Object(obj)
1494            } else {
1495                JsonValue::Array(
1496                    v.iter()
1497                        .map(|m| {
1498                            let obj: serde_json::Map<String, JsonValue> = m
1499                                .iter()
1500                                .map(|(k, val)| (k.clone(), JsonValue::String(val.clone())))
1501                                .collect();
1502                            JsonValue::Object(obj)
1503                        })
1504                        .collect(),
1505                )
1506            }
1507        }
1508        MultiValues::Json(v) => {
1509            if v.len() == 1 {
1510                v[0].clone()
1511            } else {
1512                JsonValue::Array(v.clone())
1513            }
1514        }
1515        MultiValues::Char(v) => scalar_or_array(v, |x| JsonValue::String(x.to_string())),
1516        MultiValues::BigInteger(v) => scalar_or_array(v, |x| JsonValue::String(x.to_string())),
1517        MultiValues::BigDecimal(v) => scalar_or_array(v, |x| JsonValue::String(x.to_string())),
1518        MultiValues::DateTime(v) => scalar_or_array(v, |x| JsonValue::String(x.to_string())),
1519        MultiValues::Date(v) => scalar_or_array(v, |x| JsonValue::String(x.to_string())),
1520        MultiValues::Time(v) => scalar_or_array(v, |x| JsonValue::String(x.to_string())),
1521        MultiValues::Instant(v) => scalar_or_array(v, |x| JsonValue::String(x.to_string())),
1522        MultiValues::Int128(v) => scalar_or_array(v, |x| JsonValue::String(x.to_string())),
1523        MultiValues::UInt128(v) => scalar_or_array(v, |x| JsonValue::String(x.to_string())),
1524    }
1525}
1526
1527/// If `v` has one element, returns `f(&v[0])`; otherwise a JSON array of `f`
1528/// applied to each item.
1529///
1530/// # Parameters
1531///
1532/// * `v` - Multi-values slice from a [`Property`].
1533/// * `f` - Maps each element to [`serde_json::Value`].
1534///
1535/// # Returns
1536///
1537/// A scalar or array [`serde_json::Value`].
1538fn scalar_or_array<T, F>(v: &[T], f: F) -> serde_json::Value
1539where
1540    F: Fn(&T) -> serde_json::Value,
1541{
1542    if v.len() == 1 {
1543        f(&v[0])
1544    } else {
1545        serde_json::Value::Array(v.iter().map(f).collect())
1546    }
1547}
1548
1549impl Default for Config {
1550    /// Creates a new default configuration
1551    ///
1552    /// # Returns
1553    ///
1554    /// Returns a new configuration instance
1555    ///
1556    /// # Examples
1557    ///
1558    /// ```rust,ignore
1559    /// use qubit_config::Config;
1560    ///
1561    /// let config = Config::default();
1562    /// assert!(config.is_empty());
1563    /// ```
1564    #[inline]
1565    fn default() -> Self {
1566        Self::new()
1567    }
1568}