backbone_core/config/
error.rs1use std::path::PathBuf;
4use thiserror::Error;
5
6pub type ConfigResult<T> = Result<T, ConfigError>;
8
9#[derive(Debug, Error)]
11pub enum ConfigError {
12 #[error("Configuration file not found: {path}")]
14 FileNotFound { path: PathBuf },
15
16 #[error("Failed to read configuration file {path}: {source}")]
18 ReadError {
19 path: PathBuf,
20 #[source]
21 source: std::io::Error,
22 },
23
24 #[error("Failed to parse YAML configuration: {0}")]
26 YamlParseError(#[from] serde_yaml::Error),
27
28 #[error("Failed to parse JSON configuration: {0}")]
30 JsonParseError(#[from] serde_json::Error),
31
32 #[error("Failed to parse TOML configuration: {0}")]
34 TomlParseError(#[from] toml::de::Error),
35
36 #[error("Unsupported configuration file format: {extension}")]
38 UnsupportedFormat { extension: String },
39
40 #[error("Configuration validation error: {message}")]
42 ValidationError { message: String },
43
44 #[error("Required configuration field missing: {field}")]
46 RequiredField { field: String },
47
48 #[error("Invalid configuration value for {field}: {value} - {reason}")]
50 InvalidValue {
51 field: String,
52 value: String,
53 reason: String,
54 },
55
56 #[error("Environment variable error for {var}")]
58 EnvVarError { var: String },
59
60 #[error("Failed to substitute environment variable {var}: {reason}")]
62 SubstitutionError { var: String, reason: String },
63
64 #[error("Module '{module}' configuration error: {message}")]
66 ModuleError { module: String, message: String },
67}
68
69impl ConfigError {
70 pub fn file_not_found<P: Into<PathBuf>>(path: P) -> Self {
72 Self::FileNotFound { path: path.into() }
73 }
74
75 pub fn read_error<P: Into<PathBuf>>(path: P, source: std::io::Error) -> Self {
77 Self::ReadError {
78 path: path.into(),
79 source,
80 }
81 }
82
83 pub fn validation<S: Into<String>>(message: S) -> Self {
85 Self::ValidationError {
86 message: message.into(),
87 }
88 }
89
90 pub fn required_field<S: Into<String>>(field: S) -> Self {
92 Self::RequiredField {
93 field: field.into(),
94 }
95 }
96
97 pub fn invalid_value<S: Into<String>>(field: S, value: S, reason: S) -> Self {
99 Self::InvalidValue {
100 field: field.into(),
101 value: value.into(),
102 reason: reason.into(),
103 }
104 }
105
106 pub fn env_var<S: Into<String>>(var: S) -> Self {
108 Self::EnvVarError {
109 var: var.into(),
110 }
111 }
112
113 pub fn substitution<S: Into<String>>(var: S, reason: S) -> Self {
115 Self::SubstitutionError {
116 var: var.into(),
117 reason: reason.into(),
118 }
119 }
120
121 pub fn module<S: Into<String>>(module: S, message: S) -> Self {
123 Self::ModuleError {
124 module: module.into(),
125 message: message.into(),
126 }
127 }
128
129 pub fn unsupported_format<S: Into<String>>(extension: S) -> Self {
131 Self::UnsupportedFormat {
132 extension: extension.into(),
133 }
134 }
135}
136
137impl From<std::env::VarError> for ConfigError {
139 fn from(_err: std::env::VarError) -> Self {
140 Self::EnvVarError {
141 var: "unknown".to_string(),
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_error_display() {
152 let err = ConfigError::file_not_found("test.yml");
153 assert!(err.to_string().contains("test.yml"));
154
155 let err = ConfigError::validation("Invalid port");
156 assert!(err.to_string().contains("Invalid port"));
157
158 let err = ConfigError::required_field("database.url");
159 assert!(err.to_string().contains("database.url"));
160
161 let err = ConfigError::invalid_value("port", "abc", "must be a number");
162 assert!(err.to_string().contains("port"));
163 assert!(err.to_string().contains("abc"));
164 }
165}