use std::process::ExitCode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub(crate) enum OxiExitCode {
Ok = 0,
GenericError = 1,
UsageError = 2,
IoError = 3,
ValidationError = 4,
}
impl From<OxiExitCode> for ExitCode {
fn from(code: OxiExitCode) -> ExitCode {
ExitCode::from(code as u8)
}
}
pub(crate) fn classify_error(err: &anyhow::Error) -> OxiExitCode {
for cause in err.chain() {
if cause.downcast_ref::<std::io::Error>().is_some() {
return OxiExitCode::IoError;
}
if cause.downcast_ref::<clap::Error>().is_some() {
return OxiExitCode::UsageError;
}
let msg = cause.to_string();
if msg.contains("validation error:") || msg.contains("ValidationError:") {
return OxiExitCode::ValidationError;
}
}
OxiExitCode::GenericError
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_classify_io_error() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let anyhow_err = anyhow::Error::from(io_err);
assert_eq!(classify_error(&anyhow_err), OxiExitCode::IoError);
}
#[test]
fn test_classify_generic_error() {
let err = anyhow::anyhow!("something went wrong");
assert_eq!(classify_error(&err), OxiExitCode::GenericError);
}
#[test]
fn test_classify_validation_error() {
let err = anyhow::anyhow!("validation error: field X is invalid");
assert_eq!(classify_error(&err), OxiExitCode::ValidationError);
}
#[test]
fn test_exit_code_values() {
assert_eq!(OxiExitCode::Ok as u8, 0);
assert_eq!(OxiExitCode::GenericError as u8, 1);
assert_eq!(OxiExitCode::UsageError as u8, 2);
assert_eq!(OxiExitCode::IoError as u8, 3);
assert_eq!(OxiExitCode::ValidationError as u8, 4);
}
}