use std::borrow::Cow;
use junction_api::{BackendId, VirtualHost};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
#[error("{inner}")]
pub struct Error {
inner: Box<ErrorImpl>,
}
impl Error {
pub fn is_temporary(&self) -> bool {
matches!(
*self.inner,
ErrorImpl::NoRouteMatched { .. }
| ErrorImpl::NoBackend { .. }
| ErrorImpl::NoRuleMatched { .. }
| ErrorImpl::NoReachableEndpoints { .. }
)
}
}
impl Error {
pub(crate) fn into_invalid_url(message: String) -> Self {
let inner = ErrorImpl::InvalidUrl(Cow::Owned(message));
Self {
inner: Box::new(inner),
}
}
pub(crate) fn invalid_url(message: &'static str) -> Self {
let inner = ErrorImpl::InvalidUrl(Cow::Borrowed(message));
Self {
inner: Box::new(inner),
}
}
pub(crate) fn no_route_matched(routes: Vec<VirtualHost>) -> Self {
Self {
inner: Box::new(ErrorImpl::NoRouteMatched { routes }),
}
}
pub(crate) fn no_rule_matched(route: VirtualHost) -> Self {
Self {
inner: Box::new(ErrorImpl::NoRuleMatched { route }),
}
}
pub(crate) fn invalid_route(message: &'static str, vhost: VirtualHost, rule: usize) -> Self {
Self {
inner: Box::new(ErrorImpl::InvalidRoute {
message,
vhost,
rule,
}),
}
}
pub(crate) fn no_backend(vhost: VirtualHost, rule: Option<usize>, backend: BackendId) -> Self {
Self {
inner: Box::new(ErrorImpl::NoBackend {
vhost,
rule,
backend,
}),
}
}
pub(crate) fn no_reachable_endpoints(vhost: VirtualHost, backend: BackendId) -> Self {
Self {
inner: Box::new(ErrorImpl::NoReachableEndpoints { vhost, backend }),
}
}
}
#[derive(Debug, thiserror::Error)]
enum ErrorImpl {
#[error("invalid url: {0}")]
InvalidUrl(Cow<'static, str>),
#[error("invalid route configuration")]
InvalidRoute {
message: &'static str,
vhost: VirtualHost,
rule: usize,
},
#[error(
"no routing info is available for any of the following vhosts: [{}]",
format_vhosts(.routes)
)]
NoRouteMatched { routes: Vec<VirtualHost> },
#[error("using route '{route}': no routing rules matched the request")]
NoRuleMatched { route: VirtualHost },
#[error("{vhost}: backend not found: {backend}")]
NoBackend {
vhost: VirtualHost,
rule: Option<usize>,
backend: BackendId,
},
#[error("{vhost}: no reachable endpoints")]
NoReachableEndpoints {
vhost: VirtualHost,
backend: BackendId,
},
}
fn format_vhosts(vhosts: &[VirtualHost]) -> String {
vhosts
.iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(", ")
}