• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

qubit-ltd / rust-config / 84ab02af-da5f-49b6-aa4e-20ed6b5f3fd3

09 Apr 2026 04:48PM UTC coverage: 97.136% (+0.01%) from 97.122%
84ab02af-da5f-49b6-aa4e-20ed6b5f3fd3

push

circleci

Haixing-Hu
chore: bump version to 0.7.0

1221 of 1257 relevant lines covered (97.14%)

45.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.67
/src/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 Types
10
//!
11
//! Defines all possible error types in the configuration system.
12
//!
13
//! # Author
14
//!
15
//! Haixing Hu
16

17
use thiserror::Error;
18

19
use qubit_common::DataType;
20
use 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,ignore
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)]
38
pub 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

114
/// Result type for configuration operations
115
///
116
/// Used for all operations in the configuration system that may return errors.
117
pub type ConfigResult<T> = Result<T, ConfigError>;
118

119
impl ConfigError {
120
    /// Creates a `TypeMismatch` error with an empty key (for backward
121
    /// compatibility with `ValueError` conversions that lack key context).
122
    ///
123
    /// # Parameters
124
    ///
125
    /// * `expected` - Expected [`DataType`].
126
    /// * `actual` - Actual [`DataType`].
127
    ///
128
    /// # Returns
129
    ///
130
    /// A [`ConfigError::TypeMismatch`] with an empty `key`.
131
    pub(crate) fn type_mismatch_no_key(expected: DataType, actual: DataType) -> Self {
3✔
132
        ConfigError::TypeMismatch {
3✔
133
            key: String::new(),
3✔
134
            expected,
3✔
135
            actual,
3✔
136
        }
3✔
137
    }
3✔
138

139
    /// Creates a `TypeMismatch` error with a specific key.
140
    ///
141
    /// # Parameters
142
    ///
143
    /// * `key` - Configuration key or path.
144
    /// * `expected` - Expected [`DataType`].
145
    /// * `actual` - Actual [`DataType`].
146
    ///
147
    /// # Returns
148
    ///
149
    /// A [`ConfigError::TypeMismatch`].
150
    pub(crate) fn type_mismatch_at(key: &str, expected: DataType, actual: DataType) -> Self {
20✔
151
        ConfigError::TypeMismatch {
20✔
152
            key: key.to_string(),
20✔
153
            expected,
20✔
154
            actual,
20✔
155
        }
20✔
156
    }
20✔
157

158
    /// Creates a `ConversionError` with an empty key.
159
    ///
160
    /// # Parameters
161
    ///
162
    /// * `message` - Human-readable conversion error message.
163
    ///
164
    /// # Returns
165
    ///
166
    /// A [`ConfigError::ConversionError`] with an empty `key`.
167
    pub(crate) fn conversion_error_no_key(message: impl Into<String>) -> Self {
6✔
168
        ConfigError::ConversionError {
6✔
169
            key: String::new(),
6✔
170
            message: message.into(),
6✔
171
        }
6✔
172
    }
6✔
173

174
    /// Creates a `ConversionError` with a specific key.
175
    ///
176
    /// # Parameters
177
    ///
178
    /// * `key` - Configuration key or path.
179
    /// * `message` - Human-readable conversion error message.
180
    ///
181
    /// # Returns
182
    ///
183
    /// A [`ConfigError::ConversionError`].
184
    pub(crate) fn conversion_error_at(key: &str, message: impl Into<String>) -> Self {
1✔
185
        ConfigError::ConversionError {
1✔
186
            key: key.to_string(),
1✔
187
            message: message.into(),
1✔
188
        }
1✔
189
    }
1✔
190
}
191

192
impl From<ValueError> for ConfigError {
193
    fn from(err: ValueError) -> Self {
11✔
194
        match err {
11✔
195
            ValueError::NoValue => ConfigError::PropertyHasNoValue(String::new()),
1✔
196
            ValueError::TypeMismatch { expected, actual } => {
3✔
197
                ConfigError::type_mismatch_no_key(expected, actual)
3✔
198
            }
199
            ValueError::ConversionFailed { from, to } => {
2✔
200
                ConfigError::conversion_error_no_key(format!("From {from} to {to}"))
2✔
201
            }
202
            ValueError::ConversionError(msg) => ConfigError::conversion_error_no_key(msg),
2✔
203
            ValueError::IndexOutOfBounds { index, len } => {
1✔
204
                ConfigError::IndexOutOfBounds { index, len }
1✔
205
            }
206
            ValueError::JsonSerializationError(msg) => {
1✔
207
                ConfigError::conversion_error_no_key(format!("JSON serialization error: {msg}"))
1✔
208
            }
209
            ValueError::JsonDeserializationError(msg) => {
1✔
210
                ConfigError::conversion_error_no_key(format!("JSON deserialization error: {msg}"))
1✔
211
            }
212
        }
213
    }
11✔
214
}
215

216
#[cfg(test)]
217
mod tests {
218
    use super::*;
219

220
    #[test]
221
    fn test_conversion_error_at_creates_correct_error() {
1✔
222
        let err = ConfigError::conversion_error_at("my.key", "test message");
1✔
223
        match err {
1✔
224
            ConfigError::ConversionError { key, message } => {
1✔
225
                assert_eq!(key, "my.key");
1✔
226
                assert_eq!(message, "test message");
1✔
227
            }
228
            _ => panic!("Expected ConversionError"),
×
229
        }
230
    }
1✔
231

232
    #[test]
233
    fn test_type_mismatch_at_creates_correct_error() {
1✔
234
        use qubit_common::DataType;
235
        let err = ConfigError::type_mismatch_at("a.b", DataType::Bool, DataType::Int32);
1✔
236
        match err {
1✔
237
            ConfigError::TypeMismatch {
238
                key,
1✔
239
                expected,
1✔
240
                actual,
1✔
241
            } => {
242
                assert_eq!(key, "a.b");
1✔
243
                assert_eq!(expected, DataType::Bool);
1✔
244
                assert_eq!(actual, DataType::Int32);
1✔
245
            }
246
            _ => panic!("Expected TypeMismatch"),
×
247
        }
248
    }
1✔
249
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc