use std::fmt;
use std::path::PathBuf;
#[derive(Debug)]
#[non_exhaustive]
pub struct ConfigError {
repr: ConfigErrorRepr,
}
#[derive(Debug)]
enum ConfigErrorRepr {
Read {
path: PathBuf,
source: std::io::Error,
},
Parse {
message: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
EmptyToken,
UnresolvedVar { name: String },
Interpolation { detail: String },
}
impl ConfigError {
pub(crate) fn read(path: PathBuf, source: std::io::Error) -> ConfigError {
ConfigError {
repr: ConfigErrorRepr::Read { path, source },
}
}
pub(crate) fn parse(message: impl Into<String>) -> ConfigError {
ConfigError {
repr: ConfigErrorRepr::Parse {
message: message.into(),
source: None,
},
}
}
pub(crate) fn parse_toml(source: toml::de::Error) -> ConfigError {
ConfigError {
repr: ConfigErrorRepr::Parse {
message: source.to_string(),
source: Some(Box::new(source)),
},
}
}
pub(crate) fn empty_token() -> ConfigError {
ConfigError {
repr: ConfigErrorRepr::EmptyToken,
}
}
pub(crate) fn unresolved_var(name: impl Into<String>) -> ConfigError {
ConfigError {
repr: ConfigErrorRepr::UnresolvedVar { name: name.into() },
}
}
pub(crate) fn interpolation(detail: impl Into<String>) -> ConfigError {
ConfigError {
repr: ConfigErrorRepr::Interpolation {
detail: detail.into(),
},
}
}
#[must_use]
pub fn path(&self) -> Option<&std::path::Path> {
match &self.repr {
ConfigErrorRepr::Read { path, .. } => Some(path.as_path()),
_ => None,
}
}
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.repr {
ConfigErrorRepr::Read { path, .. } => write!(f, "read config {}", path.display()),
ConfigErrorRepr::Parse { message, .. } => write!(f, "parse config: {message}"),
ConfigErrorRepr::EmptyToken => f.write_str("[server].token must not be empty"),
ConfigErrorRepr::UnresolvedVar { name } => {
write!(f, "unresolved environment variable {name}")
}
ConfigErrorRepr::Interpolation { detail } => write!(f, "interpolation: {detail}"),
}
}
}
impl std::error::Error for ConfigError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.repr {
ConfigErrorRepr::Read { source, .. } => Some(source),
ConfigErrorRepr::Parse { source, .. } => source
.as_deref()
.map(|s| s as &(dyn std::error::Error + 'static)),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ConfigErrorKind {
Read,
Parse,
EmptyToken,
UnresolvedVar,
Interpolation,
}
impl ConfigError {
#[must_use]
pub fn kind(&self) -> ConfigErrorKind {
match &self.repr {
ConfigErrorRepr::Read { .. } => ConfigErrorKind::Read,
ConfigErrorRepr::Parse { .. } => ConfigErrorKind::Parse,
ConfigErrorRepr::EmptyToken => ConfigErrorKind::EmptyToken,
ConfigErrorRepr::UnresolvedVar { .. } => ConfigErrorKind::UnresolvedVar,
ConfigErrorRepr::Interpolation { .. } => ConfigErrorKind::Interpolation,
}
}
}