use super::{ErrorContext, FormError};
pub trait ErrorReporter {
fn report_error(&self, error: &FormError, context: &ErrorContext);
}
pub struct ConsoleErrorReporter;
impl ErrorReporter for ConsoleErrorReporter {
fn report_error(&self, error: &FormError, context: &ErrorContext) {
log::error!("Error reported: {} (context: {:?})", error, context);
}
}
pub struct FileErrorReporter {
file_path: String,
}
impl FileErrorReporter {
pub fn new(file_path: impl Into<String>) -> Self {
Self {
file_path: file_path.into(),
}
}
pub fn file_path(&self) -> &str {
&self.file_path
}
}
impl ErrorReporter for FileErrorReporter {
fn report_error(&self, error: &FormError, context: &ErrorContext) {
log::error!(
"Error reported to file {}: {} (context: {:?})",
self.file_path,
error,
context
);
}
}
pub struct HttpErrorReporter {
endpoint: String,
api_key: Option<String>,
}
impl HttpErrorReporter {
pub fn new(endpoint: impl Into<String>) -> Self {
Self {
endpoint: endpoint.into(),
api_key: None,
}
}
pub fn with_api_key(mut self, api_key: impl Into<String>) -> Self {
self.api_key = Some(api_key.into());
self
}
pub fn endpoint(&self) -> &str {
&self.endpoint
}
pub fn has_api_key(&self) -> bool {
self.api_key.is_some()
}
}
impl ErrorReporter for HttpErrorReporter {
fn report_error(&self, error: &FormError, context: &ErrorContext) {
log::error!(
"Error reported to HTTP endpoint {}: {} (context: {:?})",
self.endpoint,
error,
context
);
}
}
pub struct CompositeErrorReporter {
reporters: Vec<Box<dyn ErrorReporter + Send + Sync>>,
}
impl CompositeErrorReporter {
pub fn new() -> Self {
Self {
reporters: Vec::new(),
}
}
pub fn add_reporter(mut self, reporter: Box<dyn ErrorReporter + Send + Sync>) -> Self {
self.reporters.push(reporter);
self
}
pub fn reporter_count(&self) -> usize {
self.reporters.len()
}
}
impl Default for CompositeErrorReporter {
fn default() -> Self {
Self::new()
}
}
impl ErrorReporter for CompositeErrorReporter {
fn report_error(&self, error: &FormError, context: &ErrorContext) {
for reporter in &self.reporters {
reporter.report_error(error, context);
}
}
}
pub mod utils {
use super::*;
use crate::error::field_error;
pub fn field_error_from_validation(field: &str, message: &str) -> FormError {
FormError::field_error(field, message)
}
pub fn validation_error_from_fields(
message: &str,
field_errors: Vec<field_error::FieldError>,
) -> FormError {
FormError::validation_error(message, field_errors)
}
pub fn extract_field_errors(error: &FormError) -> Vec<field_error::FieldError> {
match error {
FormError::ValidationError { field_errors, .. } => field_errors.clone(),
FormError::FieldError {
field,
message,
code,
} => {
vec![field_error::FieldError {
field: field.clone(),
message: message.clone(),
code: code.clone(),
}]
}
_ => Vec::new(),
}
}
pub fn is_recoverable_error(error: &FormError) -> bool {
matches!(
error,
FormError::FieldError { .. }
| FormError::ValidationError { .. }
| FormError::SerializationError { .. }
)
}
pub fn is_critical_error(error: &FormError) -> bool {
matches!(
error,
FormError::StateError { .. } | FormError::ConfigurationError { .. }
)
}
pub fn get_error_severity(error: &FormError) -> ErrorSeverity {
match error {
FormError::FieldError { .. } => ErrorSeverity::Low,
FormError::ValidationError { .. } => ErrorSeverity::Medium,
FormError::SerializationError { .. } => ErrorSeverity::Medium,
FormError::SubmissionError { .. } => ErrorSeverity::High,
FormError::StateError { .. } => ErrorSeverity::Critical,
FormError::PersistenceError { .. } => ErrorSeverity::High,
FormError::ConfigurationError { .. } => ErrorSeverity::Critical,
FormError::Unknown { .. } => ErrorSeverity::High,
}
}
pub fn format_error_for_logging(error: &FormError, context: &ErrorContext) -> String {
format!("Error: {} | Context: {}", error, context)
}
pub fn format_error_for_reporting(error: &FormError, context: &ErrorContext) -> String {
let severity = get_error_severity(error);
format!("[{}] {} | Context: {}", severity, error, context)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorSeverity {
Low,
Medium,
High,
Critical,
}
impl std::fmt::Display for ErrorSeverity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorSeverity::Low => write!(f, "LOW"),
ErrorSeverity::Medium => write!(f, "MEDIUM"),
ErrorSeverity::High => write!(f, "HIGH"),
ErrorSeverity::Critical => write!(f, "CRITICAL"),
}
}
}