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}