use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum ExitReason {
#[default]
Normal,
Shutdown,
ShutdownReason(String),
Killed,
Error(String),
}
impl ExitReason {
pub fn is_normal(&self) -> bool {
matches!(
self,
ExitReason::Normal | ExitReason::Shutdown | ExitReason::ShutdownReason(_)
)
}
#[inline]
pub fn is_abnormal(&self) -> bool {
!self.is_normal()
}
#[inline]
pub fn is_killed(&self) -> bool {
matches!(self, ExitReason::Killed)
}
pub fn error(msg: impl fmt::Display) -> Self {
ExitReason::Error(msg.to_string())
}
pub fn shutdown(msg: impl fmt::Display) -> Self {
ExitReason::ShutdownReason(msg.to_string())
}
}
impl fmt::Display for ExitReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExitReason::Normal => write!(f, "normal"),
ExitReason::Shutdown => write!(f, "shutdown"),
ExitReason::ShutdownReason(reason) => write!(f, "shutdown: {}", reason),
ExitReason::Killed => write!(f, "killed"),
ExitReason::Error(msg) => write!(f, "error: {}", msg),
}
}
}
impl From<&str> for ExitReason {
fn from(s: &str) -> Self {
ExitReason::Error(s.to_string())
}
}
impl From<String> for ExitReason {
fn from(s: String) -> Self {
ExitReason::Error(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_normal() {
assert!(ExitReason::Normal.is_normal());
assert!(ExitReason::Shutdown.is_normal());
assert!(ExitReason::ShutdownReason("test".into()).is_normal());
assert!(!ExitReason::Killed.is_normal());
assert!(!ExitReason::Error("test".into()).is_normal());
}
#[test]
fn test_is_abnormal() {
assert!(!ExitReason::Normal.is_abnormal());
assert!(ExitReason::Killed.is_abnormal());
assert!(ExitReason::Error("test".into()).is_abnormal());
}
#[test]
fn test_is_killed() {
assert!(ExitReason::Killed.is_killed());
assert!(!ExitReason::Normal.is_killed());
assert!(!ExitReason::Error("test".into()).is_killed());
}
#[test]
fn test_display() {
assert_eq!(format!("{}", ExitReason::Normal), "normal");
assert_eq!(format!("{}", ExitReason::Shutdown), "shutdown");
assert_eq!(
format!("{}", ExitReason::ShutdownReason("timeout".into())),
"shutdown: timeout"
);
assert_eq!(format!("{}", ExitReason::Killed), "killed");
assert_eq!(
format!("{}", ExitReason::Error("oops".into())),
"error: oops"
);
}
#[test]
fn test_from_str() {
let reason: ExitReason = "something failed".into();
assert_eq!(reason, ExitReason::Error("something failed".to_string()));
}
#[test]
fn test_serialization() {
let reasons = vec![
ExitReason::Normal,
ExitReason::Shutdown,
ExitReason::ShutdownReason("test".into()),
ExitReason::Killed,
ExitReason::Error("error".into()),
];
for reason in reasons {
let bytes = postcard::to_allocvec(&reason).unwrap();
let decoded: ExitReason = postcard::from_bytes(&bytes).unwrap();
assert_eq!(reason, decoded);
}
}
#[test]
fn test_helper_constructors() {
let err = ExitReason::error("failed");
assert_eq!(err, ExitReason::Error("failed".to_string()));
let shut = ExitReason::shutdown("maintenance");
assert_eq!(shut, ExitReason::ShutdownReason("maintenance".to_string()));
}
}