use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorSeverity {
Critical,
Error,
Warning,
Info,
}
pub trait UserFriendlyError: fmt::Display + fmt::Debug {
fn user_message(&self) -> String;
fn developer_message(&self) -> String;
fn support_code(&self) -> String;
fn severity(&self) -> ErrorSeverity;
fn suggested_actions(&self) -> Vec<String> {
Vec::new()
}
fn is_retryable(&self) -> bool {
false
}
}
pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>;
#[cfg(test)]
mod tests {
use super::{ErrorSeverity, UserFriendlyError};
#[derive(Debug)]
struct DemoError;
impl std::fmt::Display for DemoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "demo error")
}
}
impl UserFriendlyError for DemoError {
fn user_message(&self) -> String {
"Something went wrong. Please try again.".to_string()
}
fn developer_message(&self) -> String {
"DemoError: example developer message".to_string()
}
fn support_code(&self) -> String {
"DEMO-0001".to_string()
}
fn severity(&self) -> ErrorSeverity {
ErrorSeverity::Error
}
fn suggested_actions(&self) -> Vec<String> {
vec!["Retry the operation".to_string()]
}
fn is_retryable(&self) -> bool {
true
}
}
#[test]
fn demo_error_behaves() {
let err = DemoError;
assert_eq!(
err.user_message(),
"Something went wrong. Please try again."
);
assert!(err.developer_message().contains("DemoError"));
assert_eq!(err.support_code(), "DEMO-0001");
assert_eq!(err.severity(), ErrorSeverity::Error);
assert_eq!(
err.suggested_actions(),
vec!["Retry the operation".to_string()]
);
assert!(err.is_retryable());
}
#[test]
fn severity_variants() {
assert_ne!(ErrorSeverity::Critical, ErrorSeverity::Error);
assert_ne!(ErrorSeverity::Error, ErrorSeverity::Warning);
assert_ne!(ErrorSeverity::Warning, ErrorSeverity::Info);
}
}