use std::fmt;
use felix_client::{
BrokerError, CommitError, NotLeaderError, PublishRefused, SubscribeCursorError,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Exit {
Failure = 1,
Usage = 2,
Connection = 3,
Server = 4,
NotFound = 5,
}
impl Exit {
pub(crate) fn code(self) -> u8 {
self as u8
}
}
#[derive(Debug)]
pub(crate) struct Marked {
exit: Exit,
message: String,
}
impl Marked {
pub(crate) fn new(exit: Exit, message: impl Into<String>) -> Self {
Self {
exit,
message: message.into(),
}
}
}
impl fmt::Display for Marked {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for Marked {}
pub(crate) fn fail(exit: Exit, message: impl Into<String>) -> anyhow::Error {
anyhow::Error::new(Marked::new(exit, message))
}
pub(crate) trait MarkExit<T> {
fn mark(self, exit: Exit, message: impl Into<String>) -> anyhow::Result<T>;
}
impl<T, E> MarkExit<T> for Result<T, E>
where
E: Into<anyhow::Error>,
{
fn mark(self, exit: Exit, message: impl Into<String>) -> anyhow::Result<T> {
self.map_err(|err| err.into().context(Marked::new(exit, message)))
}
}
pub(crate) fn exit_for(err: &anyhow::Error) -> Exit {
if let Some(marked) = err.downcast_ref::<Marked>() {
return marked.exit;
}
if err.downcast_ref::<BrokerError>().is_some()
|| err.downcast_ref::<PublishRefused>().is_some()
|| err.downcast_ref::<NotLeaderError>().is_some()
|| err.downcast_ref::<SubscribeCursorError>().is_some()
|| err.downcast_ref::<CommitError>().is_some()
{
return Exit::Server;
}
for cause in err.chain() {
if let Some(http) = cause.downcast_ref::<reqwest::Error>()
&& (http.is_connect() || http.is_timeout())
{
return Exit::Connection;
}
}
Exit::Failure
}
#[cfg(test)]
mod tests;