use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
pub enum SourceError {
#[error("Source not found: {0}")]
NotFound(String),
#[error("Failed to read source: {0}")]
ReadFailed(String),
#[error("Parse error in {format} at {path}: {message}")]
ParseFailed {
path: String,
format: String,
message: String,
},
#[error("Invalid format: expected {expected}, found {found}")]
InvalidFormat {
expected: String,
found: String,
},
#[error("IO error: {0}")]
Io(String),
#[error("Environment variable error: {0}")]
EnvVar(String),
#[error("Network error: {0}")]
Network(String),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("Invalid path: {0}")]
InvalidPath(PathBuf),
#[error("Unsupported operation: {0}")]
Unsupported(String),
#[error("Validation failed: {0}")]
Validation(String),
#[error("{0}")]
Custom(String),
}
pub type Result<T> = std::result::Result<T, SourceError>;
impl SourceError {
#[must_use]
pub fn not_found(source: &str) -> Self {
Self::NotFound(source.to_string())
}
#[must_use]
pub fn read_failed(source: &str) -> Self {
Self::ReadFailed(source.to_string())
}
#[must_use]
pub fn parse_failed(path: &str, format: &str, message: &str) -> Self {
Self::ParseFailed {
path: path.to_string(),
format: format.to_string(),
message: message.to_string(),
}
}
#[must_use]
pub fn invalid_format(expected: &str, found: &str) -> Self {
Self::InvalidFormat {
expected: expected.to_string(),
found: found.to_string(),
}
}
#[must_use]
pub fn io(message: &str) -> Self {
Self::Io(message.to_string())
}
#[must_use]
pub fn env_var(message: &str) -> Self {
Self::EnvVar(message.to_string())
}
#[must_use]
pub fn network(message: &str) -> Self {
Self::Network(message.to_string())
}
#[must_use]
pub fn serialization(message: &str) -> Self {
Self::Serialization(message.to_string())
}
#[must_use]
pub const fn invalid_path(path: PathBuf) -> Self {
Self::InvalidPath(path)
}
#[must_use]
pub fn unsupported(operation: &str) -> Self {
Self::Unsupported(operation.to_string())
}
#[must_use]
pub fn validation(message: &str) -> Self {
Self::Validation(message.to_string())
}
#[must_use]
pub fn custom(message: &str) -> Self {
Self::Custom(message.to_string())
}
#[must_use]
pub const fn is_not_found(&self) -> bool {
matches!(self, Self::NotFound(_))
}
#[must_use]
pub const fn is_parse_failed(&self) -> bool {
matches!(self, Self::ParseFailed { .. })
}
#[must_use]
pub const fn is_network(&self) -> bool {
matches!(self, Self::Network(_))
}
#[must_use]
pub const fn is_io(&self) -> bool {
matches!(self, Self::Io(_))
}
}
impl From<std::io::Error> for SourceError {
fn from(err: std::io::Error) -> Self {
Self::Io(err.to_string())
}
}
#[cfg(feature = "json")]
impl From<serde_json::Error> for SourceError {
fn from(err: serde_json::Error) -> Self {
Self::Serialization(err.to_string())
}
}
impl From<std::env::VarError> for SourceError {
fn from(err: std::env::VarError) -> Self {
Self::EnvVar(err.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_error_not_found() {
let err = SourceError::not_found("config.toml");
assert!(err.is_not_found());
assert!(err.to_string().contains("not found"));
}
#[test]
fn test_error_parse_failed() {
let err = SourceError::parse_failed("/etc/config.toml", "toml", "invalid syntax");
assert!(err.is_parse_failed());
assert!(err.to_string().contains("Parse error"));
}
#[test]
fn test_error_invalid_format() {
let err = SourceError::invalid_format("toml", "yaml");
assert!(err.to_string().contains("Invalid format"));
}
#[test]
fn test_error_io() {
let err = SourceError::io("permission denied");
assert!(err.is_io());
}
#[test]
fn test_error_invalid_path() {
let path = PathBuf::from("/invalid/path");
let err = SourceError::invalid_path(path.clone());
assert!(matches!(err, SourceError::InvalidPath(p) if p == path));
}
#[test]
fn test_error_from_io() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let err: SourceError = io_err.into();
assert!(err.is_io());
}
#[test]
fn test_error_from_var_error() {
let var_err = std::env::VarError::NotPresent;
let err: SourceError = var_err.into();
assert!(matches!(err, SourceError::EnvVar(_)));
}
}