use crate::ExitCode;
use encoding::error::Error as CborError;
use encoding::Error as EncodingError;
use thiserror::Error;
#[derive(Error, Debug, Clone, PartialEq)]
#[error("ActorError(fatal: {fatal}, exit_code: {exit_code:?}, msg: {msg})")]
pub struct ActorError {
fatal: bool,
exit_code: ExitCode,
msg: String,
recovered: bool,
}
impl ActorError {
pub fn new(exit_code: ExitCode, msg: String) -> Self {
Self {
fatal: false,
exit_code,
msg,
recovered: false,
}
}
pub fn new_fatal(msg: String) -> Self {
Self {
fatal: true,
exit_code: ExitCode::ErrPlaceholder,
msg,
recovered: false,
}
}
pub fn new_recovered(exit_code: ExitCode, msg: String) -> Self {
Self {
fatal: false,
exit_code,
msg,
recovered: true,
}
}
pub fn is_fatal(&self) -> bool {
self.fatal
}
pub fn is_recovered(&self) -> bool {
self.recovered
}
pub fn exit_code(&self) -> ExitCode {
self.exit_code
}
pub fn is_ok(&self) -> bool {
self.exit_code == ExitCode::Ok
}
pub fn msg(&self) -> &str {
&self.msg
}
pub fn wrap(mut self, msg: impl AsRef<str>) -> Self {
self.msg = format!("{}: {}", msg.as_ref(), self.msg);
self
}
}
impl From<EncodingError> for ActorError {
fn from(e: EncodingError) -> Self {
Self {
fatal: false,
exit_code: ExitCode::ErrSerialization,
msg: e.to_string(),
recovered: false,
}
}
}
impl From<CborError> for ActorError {
fn from(e: CborError) -> Self {
Self {
fatal: false,
exit_code: ExitCode::ErrSerialization,
msg: e.to_string(),
recovered: false,
}
}
}
#[macro_export]
macro_rules! actor_error {
( fatal($msg:expr) ) => { ActorError::new_fatal($msg.to_string()) };
( fatal($msg:literal $(, $ex:expr)+) ) => {
ActorError::new_fatal(format!($msg, $($ex,)*))
};
( recovered($code:ident, $msg:expr ) ) => {
ActorError::new_recovered(ExitCode::$code, $msg.to_string())
};
( $code:ident; $msg:expr ) => { ActorError::new(ExitCode::$code, $msg.to_string()) };
( $code:ident; $msg:literal $(, $ex:expr)+ ) => {
ActorError::new(ExitCode::$code, format!($msg, $($ex,)*))
};
( $code:ident, $msg:expr ) => { actor_error!($code; $msg) };
( $code:ident, $msg:literal $(, $ex:expr)+ ) => {
actor_error!($code; $msg $(, $ex)*)
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_macro_generation() {
assert_eq!(
actor_error!(SysErrSenderInvalid; "test"),
ActorError::new(ExitCode::SysErrSenderInvalid, "test".to_owned())
);
assert_eq!(
actor_error!(SysErrSenderInvalid; "test {}, {}", 8, 10),
ActorError::new(ExitCode::SysErrSenderInvalid, format!("test {}, {}", 8, 10))
);
assert_eq!(
actor_error!(fatal("test {}, {}", 8, 10)),
ActorError::new_fatal(format!("test {}, {}", 8, 10))
);
}
}