1use thiserror::Error;
18
19use qubit_common::DataType;
20use qubit_value::ValueError;
21
22#[derive(Debug, Error)]
38pub enum ConfigError {
39 #[error("Property not found: {0}")]
41 PropertyNotFound(String),
42
43 #[error("Property '{0}' has no value")]
45 PropertyHasNoValue(String),
46
47 #[error("Type mismatch at '{key}': expected {expected}, actual {actual}")]
49 TypeMismatch {
50 key: String,
52 expected: DataType,
54 actual: DataType,
56 },
57
58 #[error("Type conversion failed at '{key}': {message}")]
60 ConversionError {
61 key: String,
63 message: String,
65 },
66
67 #[error("Index out of bounds: index {index}, length {len}")]
69 IndexOutOfBounds {
70 index: usize,
72 len: usize,
74 },
75
76 #[error("Variable substitution failed: {0}")]
78 SubstitutionError(String),
79
80 #[error("Variable substitution depth exceeded maximum limit: {0}")]
82 SubstitutionDepthExceeded(usize),
83
84 #[error("Configuration merge failed: {0}")]
86 MergeError(String),
87
88 #[error("Property '{0}' is final and cannot be overridden")]
90 PropertyIsFinal(String),
91
92 #[error("IO error: {0}")]
94 IoError(#[from] std::io::Error),
95
96 #[error("Parse error: {0}")]
98 ParseError(String),
99
100 #[error("Deserialization error at '{path}': {message}")]
102 DeserializeError {
103 path: String,
105 message: String,
107 },
108
109 #[error("Configuration error: {0}")]
111 Other(String),
112}
113
114pub type ConfigResult<T> = Result<T, ConfigError>;
118
119impl ConfigError {
120 #[inline]
132 pub(crate) fn type_mismatch_no_key(expected: DataType, actual: DataType) -> Self {
133 ConfigError::TypeMismatch {
134 key: String::new(),
135 expected,
136 actual,
137 }
138 }
139
140 #[inline]
152 pub(crate) fn type_mismatch_at(key: &str, expected: DataType, actual: DataType) -> Self {
153 ConfigError::TypeMismatch {
154 key: key.to_string(),
155 expected,
156 actual,
157 }
158 }
159
160 #[inline]
170 pub(crate) fn conversion_error_no_key(message: impl Into<String>) -> Self {
171 ConfigError::ConversionError {
172 key: String::new(),
173 message: message.into(),
174 }
175 }
176
177 #[inline]
188 pub(crate) fn conversion_error_at(key: &str, message: impl Into<String>) -> Self {
189 ConfigError::ConversionError {
190 key: key.to_string(),
191 message: message.into(),
192 }
193 }
194}
195
196impl From<ValueError> for ConfigError {
197 fn from(err: ValueError) -> Self {
198 match err {
199 ValueError::NoValue => ConfigError::PropertyHasNoValue(String::new()),
200 ValueError::TypeMismatch { expected, actual } => {
201 ConfigError::type_mismatch_no_key(expected, actual)
202 }
203 ValueError::ConversionFailed { from, to } => {
204 ConfigError::conversion_error_no_key(format!("From {from} to {to}"))
205 }
206 ValueError::ConversionError(msg) => ConfigError::conversion_error_no_key(msg),
207 ValueError::IndexOutOfBounds { index, len } => {
208 ConfigError::IndexOutOfBounds { index, len }
209 }
210 ValueError::JsonSerializationError(msg) => {
211 ConfigError::conversion_error_no_key(format!("JSON serialization error: {msg}"))
212 }
213 ValueError::JsonDeserializationError(msg) => {
214 ConfigError::conversion_error_no_key(format!("JSON deserialization error: {msg}"))
215 }
216 }
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 #[test]
225 fn test_conversion_error_at_creates_correct_error() {
226 let err = ConfigError::conversion_error_at("my.key", "test message");
227 match err {
228 ConfigError::ConversionError { key, message } => {
229 assert_eq!(key, "my.key");
230 assert_eq!(message, "test message");
231 }
232 _ => panic!("Expected ConversionError"),
233 }
234 }
235
236 #[test]
237 fn test_type_mismatch_at_creates_correct_error() {
238 use qubit_common::DataType;
239 let err = ConfigError::type_mismatch_at("a.b", DataType::Bool, DataType::Int32);
240 match err {
241 ConfigError::TypeMismatch {
242 key,
243 expected,
244 actual,
245 } => {
246 assert_eq!(key, "a.b");
247 assert_eq!(expected, DataType::Bool);
248 assert_eq!(actual, DataType::Int32);
249 }
250 _ => panic!("Expected TypeMismatch"),
251 }
252 }
253}