Skip to main content

hiver_config/
error.rs

1//! Configuration error types
2//! 配置错误类型
3
4use std::path::PathBuf;
5use thiserror::Error;
6
7/// Configuration error type
8/// 配置错误类型
9///
10/// Equivalent to Spring's `ConfigurationPropertiesException`.
11/// 等价于Spring的`ConfigurationPropertiesException`。
12#[derive(Error, Debug)]
13pub enum ConfigError {
14    /// I/O error
15    /// I/O错误
16    #[error("I/O error: {0}")]
17    Io(#[from] std::io::Error),
18
19    /// Parse error
20    /// 解析错误
21    #[error("Parse error: {0}")]
22    Parse(String),
23
24    /// Validation error
25    /// 验证错误
26    #[error("Validation error: {0}")]
27    Validation(String),
28
29    /// Missing required property
30    /// 缺少必需属性
31    #[error("Missing required property: {0}")]
32    MissingProperty(String),
33
34    /// Type conversion error
35    /// 类型转换错误
36    #[error("Type conversion error for '{key}': expected {expected}, got {value}")]
37    TypeConversion {
38        /// Property key
39        /// 属性键
40        key: String,
41        /// Expected type
42        /// 期望类型
43        expected: String,
44        /// Actual value
45        /// 实际值
46        value: String,
47    },
48
49    /// File not found
50    /// 文件未找到
51    #[error("Configuration file not found: {0}")]
52    FileNotFound(PathBuf),
53
54    /// Invalid format
55    /// 无效格式
56    #[error("Invalid configuration format: {0}")]
57    InvalidFormat(String),
58
59    /// Cycle detected in configuration
60    /// 配置中检测到循环
61    #[error("Cycle detected in configuration: {0}")]
62    CycleDetected(String),
63
64    /// Override not allowed
65    /// 不允许覆盖
66    #[error("Override not allowed for property: {0}")]
67    OverrideNotAllowed(String),
68
69    /// Unknown profile
70    /// 未知配置文件
71    #[error("Unknown profile: {0}")]
72    UnknownProfile(String),
73
74    /// Deserialize error
75    /// 反序列化错误
76    #[error("Deserialize error: {0}")]
77    Deserialize(String),
78}
79
80/// Configuration result type
81/// 配置结果类型
82pub type ConfigResult<T> = Result<T, ConfigError>;
83
84impl From<config::ConfigError> for ConfigError {
85    fn from(err: config::ConfigError) -> Self {
86        ConfigError::Parse(err.to_string())
87    }
88}
89
90impl From<serde_json::Error> for ConfigError {
91    fn from(err: serde_json::Error) -> Self {
92        ConfigError::Deserialize(err.to_string())
93    }
94}
95
96impl From<toml::de::Error> for ConfigError {
97    fn from(err: toml::de::Error) -> Self {
98        ConfigError::Parse(err.to_string())
99    }
100}
101
102// Note: yaml_rust2 error type is not directly exported
103// If yaml parsing fails, it will be caught as serde_yaml error
104// 注:yaml_rust2 错误类型未直接导出
105// 如果 yaml 解析失败,将被捕获为 serde_yaml 错误
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    /// Test ConfigError display for Io variant
112    /// 测试Io变体的ConfigError显示
113    #[test]
114    fn test_error_io() {
115        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file gone");
116        let err = ConfigError::Io(io_err);
117        assert!(err.to_string().contains("I/O error"));
118    }
119
120    /// Test ConfigError display for Parse variant
121    /// 测试Parse变体的ConfigError显示
122    #[test]
123    fn test_error_parse() {
124        let err = ConfigError::Parse("bad syntax".to_string());
125        assert!(err.to_string().contains("Parse error"));
126        assert!(err.to_string().contains("bad syntax"));
127    }
128
129    /// Test ConfigError display for Validation variant
130    /// 测试Validation变体的ConfigError显示
131    #[test]
132    fn test_error_validation() {
133        let err = ConfigError::Validation("invalid port".to_string());
134        assert!(err.to_string().contains("Validation error"));
135    }
136
137    /// Test ConfigError display for MissingProperty variant
138    /// 测试MissingProperty变体的ConfigError显示
139    #[test]
140    fn test_error_missing_property() {
141        let err = ConfigError::MissingProperty("server.port".to_string());
142        assert!(err.to_string().contains("server.port"));
143        assert!(err.to_string().contains("Missing"));
144    }
145
146    /// Test ConfigError display for TypeConversion variant
147    /// 测试TypeConversion变体的ConfigError显示
148    #[test]
149    fn test_error_type_conversion() {
150        let err = ConfigError::TypeConversion {
151            key: "port".to_string(),
152            expected: "i32".to_string(),
153            value: "abc".to_string(),
154        };
155        let msg = err.to_string();
156        assert!(msg.contains("port"));
157        assert!(msg.contains("i32"));
158        assert!(msg.contains("abc"));
159    }
160
161    /// Test ConfigError display for FileNotFound variant
162    /// 测试FileNotFound变体的ConfigError显示
163    #[test]
164    fn test_error_file_not_found() {
165        let err = ConfigError::FileNotFound(PathBuf::from("/etc/hiver/app.yaml"));
166        assert!(err.to_string().contains("not found"));
167    }
168
169    /// Test ConfigError display for InvalidFormat variant
170    /// 测试InvalidFormat变体的ConfigError显示
171    #[test]
172    fn test_error_invalid_format() {
173        let err = ConfigError::InvalidFormat("not yaml".to_string());
174        assert!(err.to_string().contains("Invalid"));
175    }
176
177    /// Test ConfigError display for CycleDetected variant
178    /// 测试CycleDetected变体的ConfigError显示
179    #[test]
180    fn test_error_cycle_detected() {
181        let err = ConfigError::CycleDetected("a -> b -> a".to_string());
182        assert!(err.to_string().contains("Cycle"));
183    }
184
185    /// Test ConfigError display for OverrideNotAllowed variant
186    /// 测试OverrideNotAllowed变体的ConfigError显示
187    #[test]
188    fn test_error_override_not_allowed() {
189        let err = ConfigError::OverrideNotAllowed("locked.key".to_string());
190        assert!(err.to_string().contains("Override not allowed"));
191    }
192
193    /// Test ConfigError display for UnknownProfile variant
194    /// 测试UnknownProfile变体的ConfigError显示
195    #[test]
196    fn test_error_unknown_profile() {
197        let err = ConfigError::UnknownProfile("nonexistent".to_string());
198        assert!(err.to_string().contains("Unknown profile"));
199    }
200
201    /// Test ConfigError display for Deserialize variant
202    /// 测试Deserialize变体的ConfigError显示
203    #[test]
204    fn test_error_deserialize() {
205        let err = ConfigError::Deserialize("expected string".to_string());
206        assert!(err.to_string().contains("Deserialize"));
207    }
208
209    /// Test From<std::io::Error> for ConfigError
210    /// 测试从std::io::Error转换到ConfigError
211    #[test]
212    fn test_from_io_error() {
213        let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
214        let config_err: ConfigError = io_err.into();
215        match config_err {
216            ConfigError::Io(_) => {},
217            _ => panic!("Expected Io variant"),
218        }
219    }
220
221    /// Test From<serde_json::Error> for ConfigError
222    /// 测试从serde_json::Error转换到ConfigError
223    #[test]
224    fn test_from_serde_json_error() {
225        let json_err: serde_json::Error = serde_json::from_str::<i32>("not a number").unwrap_err();
226        let config_err: ConfigError = json_err.into();
227        match config_err {
228            ConfigError::Deserialize(_) => {},
229            _ => panic!("Expected Deserialize variant"),
230        }
231    }
232
233    /// Test From<toml::de::Error> for ConfigError
234    /// 测试从toml::de::Error转换到ConfigError
235    #[test]
236    fn test_from_toml_error() {
237        let toml_err = toml::from_str::<toml::Value>("{invalid").unwrap_err();
238        let config_err: ConfigError = toml_err.into();
239        match config_err {
240            ConfigError::Parse(_) => {},
241            _ => panic!("Expected Parse variant"),
242        }
243    }
244
245    /// Test ConfigResult is an alias for Result<T, ConfigError>
246    /// 测试ConfigResult是Result<T, ConfigError>的类型别名
247    #[test]
248    fn test_config_result_ok() {
249        let result: ConfigResult<i32> = Ok(42);
250        assert_eq!(result.unwrap(), 42);
251    }
252
253    /// Test ConfigResult Err case
254    /// 测试ConfigResult Err情况
255    #[test]
256    fn test_config_result_err() {
257        let result: ConfigResult<()> = Err(ConfigError::Parse("err".to_string()));
258        assert!(result.is_err());
259    }
260}