Skip to main content

qubit_config/
config_error.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Configuration Error Type
10//!
11//! Defines all possible error scenarios in the configuration system.
12//!
13//! # Author
14//!
15//! Haixing Hu
16
17use thiserror::Error;
18
19use qubit_common::DataType;
20use qubit_value::ValueError;
21
22/// Configuration error type
23///
24/// Defines all possible error scenarios in the configuration system.
25///
26/// # Examples
27///
28/// ```rust
29/// use qubit_config::{Config, ConfigError, ConfigResult};
30/// fn get_port(config: &Config) -> ConfigResult<i32> { unimplemented!() }
31/// ```
32///
33/// # Author
34///
35/// Haixing Hu
36///
37#[derive(Debug, Error)]
38pub enum ConfigError {
39    /// Property not found
40    #[error("Property not found: {0}")]
41    PropertyNotFound(String),
42
43    /// Property has no value
44    #[error("Property '{0}' has no value")]
45    PropertyHasNoValue(String),
46
47    /// Type mismatch at a specific key/path
48    #[error("Type mismatch at '{key}': expected {expected}, actual {actual}")]
49    TypeMismatch {
50        /// The configuration key/path where the mismatch occurred
51        key: String,
52        /// Expected type
53        expected: DataType,
54        /// Actual type
55        actual: DataType,
56    },
57
58    /// Type conversion failed at a specific key/path
59    #[error("Type conversion failed at '{key}': {message}")]
60    ConversionError {
61        /// The configuration key/path where the conversion failed
62        key: String,
63        /// Error message
64        message: String,
65    },
66
67    /// Index out of bounds
68    #[error("Index out of bounds: index {index}, length {len}")]
69    IndexOutOfBounds {
70        /// Index being accessed
71        index: usize,
72        /// Actual length
73        len: usize,
74    },
75
76    /// Variable substitution failed
77    #[error("Variable substitution failed: {0}")]
78    SubstitutionError(String),
79
80    /// Variable substitution depth exceeded
81    #[error("Variable substitution depth exceeded maximum limit: {0}")]
82    SubstitutionDepthExceeded(usize),
83
84    /// Configuration merge failed
85    #[error("Configuration merge failed: {0}")]
86    MergeError(String),
87
88    /// Property is final and cannot be overridden
89    #[error("Property '{0}' is final and cannot be overridden")]
90    PropertyIsFinal(String),
91
92    /// IO error
93    #[error("IO error: {0}")]
94    IoError(#[from] std::io::Error),
95
96    /// Parse error
97    #[error("Parse error: {0}")]
98    ParseError(String),
99
100    /// Deserialization error for structured config mapping
101    #[error("Deserialization error at '{path}': {message}")]
102    DeserializeError {
103        /// The config prefix/path being deserialized
104        path: String,
105        /// Error message
106        message: String,
107    },
108
109    /// Other error
110    #[error("Configuration error: {0}")]
111    Other(String),
112}
113
114impl ConfigError {
115    /// Creates a `TypeMismatch` error with an empty key (for backward
116    /// compatibility with `ValueError` conversions that lack key context).
117    ///
118    /// # Parameters
119    ///
120    /// * `expected` - Expected [`DataType`].
121    /// * `actual` - Actual [`DataType`].
122    ///
123    /// # Returns
124    ///
125    /// A [`ConfigError::TypeMismatch`] with an empty `key`.
126    #[inline]
127    pub(crate) fn type_mismatch_no_key(expected: DataType, actual: DataType) -> Self {
128        ConfigError::TypeMismatch {
129            key: String::new(),
130            expected,
131            actual,
132        }
133    }
134
135    /// Creates a `ConversionError` with an empty key.
136    ///
137    /// # Parameters
138    ///
139    /// * `message` - Human-readable conversion error message.
140    ///
141    /// # Returns
142    ///
143    /// A [`ConfigError::ConversionError`] with an empty `key`.
144    #[inline]
145    pub(crate) fn conversion_error_no_key(message: impl Into<String>) -> Self {
146        ConfigError::ConversionError {
147            key: String::new(),
148            message: message.into(),
149        }
150    }
151}
152
153impl From<ValueError> for ConfigError {
154    fn from(err: ValueError) -> Self {
155        match err {
156            ValueError::NoValue => ConfigError::PropertyHasNoValue(String::new()),
157            ValueError::TypeMismatch { expected, actual } => {
158                ConfigError::type_mismatch_no_key(expected, actual)
159            }
160            ValueError::ConversionFailed { from, to } => {
161                ConfigError::conversion_error_no_key(format!("From {from} to {to}"))
162            }
163            ValueError::ConversionError(msg) => ConfigError::conversion_error_no_key(msg),
164            ValueError::IndexOutOfBounds { index, len } => {
165                ConfigError::IndexOutOfBounds { index, len }
166            }
167            ValueError::JsonSerializationError(msg) => {
168                ConfigError::conversion_error_no_key(format!("JSON serialization error: {msg}"))
169            }
170            ValueError::JsonDeserializationError(msg) => {
171                ConfigError::conversion_error_no_key(format!("JSON deserialization error: {msg}"))
172            }
173        }
174    }
175}
176
177impl From<(&str, ValueError)> for ConfigError {
178    fn from((key, err): (&str, ValueError)) -> Self {
179        match err {
180            ValueError::NoValue => ConfigError::PropertyHasNoValue(key.to_string()),
181            ValueError::TypeMismatch { expected, actual } => ConfigError::TypeMismatch {
182                key: key.to_string(),
183                expected,
184                actual,
185            },
186            ValueError::ConversionFailed { from, to } => ConfigError::ConversionError {
187                key: key.to_string(),
188                message: format!("From {from} to {to}"),
189            },
190            ValueError::ConversionError(message) => ConfigError::ConversionError {
191                key: key.to_string(),
192                message,
193            },
194            ValueError::IndexOutOfBounds { index, len } => {
195                ConfigError::IndexOutOfBounds { index, len }
196            }
197            ValueError::JsonSerializationError(message) => ConfigError::ConversionError {
198                key: key.to_string(),
199                message: format!("JSON serialization error: {message}"),
200            },
201            ValueError::JsonDeserializationError(message) => ConfigError::ConversionError {
202                key: key.to_string(),
203                message: format!("JSON deserialization error: {message}"),
204            },
205        }
206    }
207}