2026-10-04 18:55:33 +03:00
|
|
|
//! 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> },
|
2026-10-04 19:21:48 +03:00
|
|
|
|
|
|
|
|
/// An include or ASCII-art target cannot be used. `path` is where the
|
|
|
|
|
/// directive actually pointed, resolved, which is the thing an author needs
|
|
|
|
|
/// to see when a relative target is wrong.
|
|
|
|
|
Include { path: PathBuf, reason: IncludeReason },
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Why a directive's target was refused.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum IncludeReason {
|
|
|
|
|
/// Resolved outside the content root, or is not a regular file.
|
|
|
|
|
Outside,
|
|
|
|
|
Missing,
|
|
|
|
|
/// Already being expanded further up the stack.
|
|
|
|
|
Circular,
|
|
|
|
|
TooDeep,
|
|
|
|
|
/// The expansion exceeded its line or byte budget.
|
|
|
|
|
TooLarge,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for IncludeReason {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
let text = match self {
|
|
|
|
|
IncludeReason::Outside => "is outside the content root",
|
|
|
|
|
IncludeReason::Missing => "does not exist",
|
|
|
|
|
IncludeReason::Circular => "is already being included",
|
|
|
|
|
IncludeReason::TooDeep => "includes nest too deeply",
|
|
|
|
|
IncludeReason::TooLarge => "expands to more than the size limit",
|
|
|
|
|
};
|
|
|
|
|
f.write_str(text)
|
|
|
|
|
}
|
2026-10-04 18:55:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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}'")
|
|
|
|
|
}
|
2026-10-04 19:21:48 +03:00
|
|
|
Error::Include { path, reason } => {
|
|
|
|
|
write!(f, "include target {} {reason}", path.display())
|
|
|
|
|
}
|
2026-10-04 18:55:33 +03:00
|
|
|
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() }
|
|
|
|
|
}
|
|
|
|
|
}
|