use std::{
error::Error,
fmt, io,
path::{Path, PathBuf},
};
pub type BoxError = Box<dyn Error + Send + Sync + 'static>;
pub type Result<T> = std::result::Result<T, ConfigTreeError>;
#[derive(Debug)]
pub enum ConfigTreeError {
CurrentDir {
source: io::Error,
},
Load {
path: PathBuf,
source: BoxError,
},
EmptyIncludePath {
path: PathBuf,
index: usize,
},
IncludeCycle {
chain: Vec<PathBuf>,
},
}
impl ConfigTreeError {
pub(crate) fn load<E>(path: &Path, source: E) -> Self
where
E: Into<BoxError>,
{
Self::Load {
path: path.to_path_buf(),
source: source.into(),
}
}
}
impl fmt::Display for ConfigTreeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CurrentDir { .. } => write!(f, "failed to resolve current directory"),
Self::Load { path, source } => {
write!(f, "failed to load config {}: {source}", path.display())
}
Self::EmptyIncludePath { path, index } => write!(
f,
"include path at index {index} in {} must not be empty",
path.display()
),
Self::IncludeCycle { chain } => {
let chain = chain
.iter()
.map(|path| path.display().to_string())
.collect::<Vec<_>>()
.join(" -> ");
write!(f, "recursive config include cycle: {chain}")
}
}
}
}
impl Error for ConfigTreeError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::CurrentDir { source } => Some(source),
Self::Load { source, .. } => Some(source.as_ref()),
Self::EmptyIncludePath { .. } | Self::IncludeCycle { .. } => None,
}
}
}
#[derive(Debug)]
pub enum ConfigError {
Tree(Box<ConfigTreeError>),
Dotenv(Box<dotenvy::Error>),
Figment(Box<figment::Error>),
Config(Box<confique::Error>),
Json(Box<serde_json::Error>),
Io(Box<io::Error>),
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Tree(err) => err.fmt(f),
Self::Dotenv(err) => err.fmt(f),
Self::Figment(err) => err.fmt(f),
Self::Config(err) => err.fmt(f),
Self::Json(err) => err.fmt(f),
Self::Io(err) => err.fmt(f),
}
}
}
impl Error for ConfigError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Tree(err) => Some(err.as_ref()),
Self::Dotenv(err) => Some(err.as_ref()),
Self::Figment(err) => Some(err.as_ref()),
Self::Config(err) => Some(err.as_ref()),
Self::Json(err) => Some(err.as_ref()),
Self::Io(err) => Some(err.as_ref()),
}
}
}
impl From<ConfigTreeError> for ConfigError {
fn from(err: ConfigTreeError) -> Self {
Self::Tree(Box::new(err))
}
}
impl From<dotenvy::Error> for ConfigError {
fn from(err: dotenvy::Error) -> Self {
Self::Dotenv(Box::new(err))
}
}
impl From<figment::Error> for ConfigError {
fn from(err: figment::Error) -> Self {
Self::Figment(Box::new(err))
}
}
impl From<confique::Error> for ConfigError {
fn from(err: confique::Error) -> Self {
Self::Config(Box::new(err))
}
}
impl From<serde_json::Error> for ConfigError {
fn from(err: serde_json::Error) -> Self {
Self::Json(Box::new(err))
}
}
impl From<io::Error> for ConfigError {
fn from(err: io::Error) -> Self {
Self::Io(Box::new(err))
}
}