Skip to main content

backbone_core/config/
error.rs

1//! Configuration error types
2
3use std::path::PathBuf;
4use thiserror::Error;
5
6/// Result type for configuration operations
7pub type ConfigResult<T> = Result<T, ConfigError>;
8
9/// Configuration error types
10#[derive(Debug, Error)]
11pub enum ConfigError {
12    /// File not found
13    #[error("Configuration file not found: {path}")]
14    FileNotFound { path: PathBuf },
15
16    /// File read error
17    #[error("Failed to read configuration file {path}: {source}")]
18    ReadError {
19        path: PathBuf,
20        #[source]
21        source: std::io::Error,
22    },
23
24    /// YAML parse error
25    #[error("Failed to parse YAML configuration: {0}")]
26    YamlParseError(#[from] serde_yaml::Error),
27
28    /// JSON parse error
29    #[error("Failed to parse JSON configuration: {0}")]
30    JsonParseError(#[from] serde_json::Error),
31
32    /// TOML parse error
33    #[error("Failed to parse TOML configuration: {0}")]
34    TomlParseError(#[from] toml::de::Error),
35
36    /// Unsupported file format
37    #[error("Unsupported configuration file format: {extension}")]
38    UnsupportedFormat { extension: String },
39
40    /// Validation error
41    #[error("Configuration validation error: {message}")]
42    ValidationError { message: String },
43
44    /// Missing required field
45    #[error("Required configuration field missing: {field}")]
46    RequiredField { field: String },
47
48    /// Invalid value
49    #[error("Invalid configuration value for {field}: {value} - {reason}")]
50    InvalidValue {
51        field: String,
52        value: String,
53        reason: String,
54    },
55
56    /// Environment variable error
57    #[error("Environment variable error for {var}")]
58    EnvVarError { var: String },
59
60    /// Environment variable substitution error
61    #[error("Failed to substitute environment variable {var}: {reason}")]
62    SubstitutionError { var: String, reason: String },
63
64    /// Module configuration error
65    #[error("Module '{module}' configuration error: {message}")]
66    ModuleError { module: String, message: String },
67}
68
69impl ConfigError {
70    /// Create a file not found error
71    pub fn file_not_found<P: Into<PathBuf>>(path: P) -> Self {
72        Self::FileNotFound { path: path.into() }
73    }
74
75    /// Create a read error
76    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    /// Create a validation error
84    pub fn validation<S: Into<String>>(message: S) -> Self {
85        Self::ValidationError {
86            message: message.into(),
87        }
88    }
89
90    /// Create a required field error
91    pub fn required_field<S: Into<String>>(field: S) -> Self {
92        Self::RequiredField {
93            field: field.into(),
94        }
95    }
96
97    /// Create an invalid value error
98    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    /// Create an environment variable error
107    pub fn env_var<S: Into<String>>(var: S) -> Self {
108        Self::EnvVarError {
109            var: var.into(),
110        }
111    }
112
113    /// Create a substitution error
114    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    /// Create a module error
122    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    /// Create an unsupported format error
130    pub fn unsupported_format<S: Into<String>>(extension: S) -> Self {
131        Self::UnsupportedFormat {
132            extension: extension.into(),
133        }
134    }
135}
136
137// Allow conversion from simple env var error
138impl 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}