use core::result::Result as CoreResult;
use std::path::PathBuf;
use thiserror::Error as ThisError;
#[derive(ThisError, Debug, Clone)]
pub enum ConfigError {
#[error("Configuration file not found: {path}")]
FileNotFound {
path: PathBuf,
},
#[error("Failed to read configuration file '{path}': {message}")]
FileReadError {
path: PathBuf,
message: String,
},
#[error("Failed to write configuration file '{path}': {message}")]
FileWriteError {
path: PathBuf,
message: String,
},
#[error("Failed to parse {format} configuration: {message}")]
ParseError {
format: String,
message: String,
},
#[error("Failed to serialize configuration as {format}: {message}")]
SerializeError {
format: String,
message: String,
},
#[error("Configuration validation failed: {message}")]
ValidationError {
message: String,
},
#[error("Environment variable error: {message}")]
EnvironmentError {
message: String,
},
#[error("Type conversion error: expected {expected}, got {actual}")]
TypeError {
expected: String,
actual: String,
},
#[error("Configuration key not found: {key}")]
KeyNotFound {
key: String,
},
#[error("Configuration merge conflict: {message}")]
MergeConflict {
message: String,
},
#[error("Configuration provider '{provider}' error: {message}")]
ProviderError {
provider: String,
message: String,
},
#[error("Configuration error: {0}")]
Other(String),
}
impl ConfigError {
pub fn validation(message: impl Into<String>) -> Self {
Self::ValidationError { message: message.into() }
}
pub fn parse(format: impl Into<String>, message: impl Into<String>) -> Self {
Self::ParseError { format: format.into(), message: message.into() }
}
pub fn serialize(format: impl Into<String>, message: impl Into<String>) -> Self {
Self::SerializeError { format: format.into(), message: message.into() }
}
pub fn type_error(expected: impl Into<String>, actual: impl Into<String>) -> Self {
Self::TypeError { expected: expected.into(), actual: actual.into() }
}
pub fn provider(provider: impl Into<String>, message: impl Into<String>) -> Self {
Self::ProviderError { provider: provider.into(), message: message.into() }
}
pub fn other(message: impl Into<String>) -> Self {
Self::Other(message.into())
}
}
impl From<std::io::Error> for ConfigError {
fn from(err: std::io::Error) -> Self {
Self::Other(err.to_string())
}
}
impl From<String> for ConfigError {
fn from(s: String) -> Self {
Self::Other(s)
}
}
impl From<&str> for ConfigError {
fn from(s: &str) -> Self {
Self::Other(s.to_string())
}
}
impl From<serde_json::Error> for ConfigError {
fn from(err: serde_json::Error) -> Self {
Self::ParseError { format: "JSON".to_string(), message: err.to_string() }
}
}
pub type ConfigResult<T> = CoreResult<T, ConfigError>;
impl AsRef<str> for ConfigError {
fn as_ref(&self) -> &str {
match self {
ConfigError::FileNotFound { .. } => "ConfigError::FileNotFound",
ConfigError::FileReadError { .. } => "ConfigError::FileReadError",
ConfigError::FileWriteError { .. } => "ConfigError::FileWriteError",
ConfigError::ParseError { .. } => "ConfigError::ParseError",
ConfigError::SerializeError { .. } => "ConfigError::SerializeError",
ConfigError::ValidationError { .. } => "ConfigError::ValidationError",
ConfigError::EnvironmentError { .. } => "ConfigError::EnvironmentError",
ConfigError::TypeError { .. } => "ConfigError::TypeError",
ConfigError::KeyNotFound { .. } => "ConfigError::KeyNotFound",
ConfigError::MergeConflict { .. } => "ConfigError::MergeConflict",
ConfigError::ProviderError { .. } => "ConfigError::ProviderError",
ConfigError::Other(_) => "ConfigError::Other",
}
}
}