itsybitsy/core/src/error.rs

65 lines
2.3 KiB
Rust
Raw Normal View History

//! The library error type.
//!
//! Variants carry the data a caller needs to act or an operator needs to fix
//! the cause, rather than a pre-formatted sentence.
use std::fmt;
use std::io;
use std::path::PathBuf;
#[derive(Debug)]
pub enum Error {
/// Reading or canonicalising a path named by the configuration failed.
Io { path: PathBuf, cause: io::Error },
/// A configuration file is not valid TOML, or does not match the schema.
Toml { path: PathBuf, cause: toml::de::Error },
/// A rule the schema cannot express was violated.
Config { message: String },
/// Two sites claim one hostname, so a request naming it could not be routed.
DuplicateHost { host: String, first: String, second: String },
/// A listener names a format no enabled feature provides. `available` is
/// the registry's full id set, which is what tells an operator whether the
/// name is a typo or a missing cargo feature.
UnknownFormat { listener: String, format: String, available: Vec<String> },
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
// The cause is reachable through `source`, so it is not repeated here.
Error::Io { path, .. } => write!(f, "cannot read {}", path.display()),
Error::Toml { path, .. } => write!(f, "invalid TOML in {}", path.display()),
Error::Config { message } => write!(f, "{message}"),
Error::DuplicateHost { host, first, second } => {
write!(f, "host '{host}' is claimed by both site '{first}' and site '{second}'")
}
Error::UnknownFormat { listener, format, available } => write!(
f,
"listener '{listener}' wants format '{format}', which this build does not \
provide; available: {}",
if available.is_empty() { "none".to_string() } else { available.join(", ") }
),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Io { cause, .. } => Some(cause),
Error::Toml { cause, .. } => Some(cause),
_ => None,
}
}
}
impl Error {
pub(crate) fn config(message: impl Into<String>) -> Self {
Error::Config { message: message.into() }
}
}