rustnext/
error.rs

1use crate::{Response, ui::{div, h1, p, text, get_renderer}};
2use hyper::StatusCode;
3use std::fmt;
4use std::error::Error as StdError; // Alias for clarity
5
6#[derive(Debug, Clone)] // Added Clone derive
7pub enum AppError {
8    NotFound(String),
9    Internal(String),
10    BadRequest(String),
11    Unauthorized(String),
12    Forbidden(String),
13    // Add more specific errors as needed
14    #[allow(dead_code)] // Allow unused variant for now
15    Custom(StatusCode, String),
16}
17
18impl fmt::Display for AppError {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match self {
21            AppError::NotFound(msg) => write!(f, "Not Found: {}", msg),
22            AppError::Internal(msg) => write!(f, "Internal Server Error: {}", msg),
23            AppError::BadRequest(msg) => write!(f, "Bad Request: {}", msg),
24            AppError::Unauthorized(msg) => write!(f, "Unauthorized: {}", msg),
25            AppError::Forbidden(msg) => write!(f, "Forbidden: {}", msg),
26            AppError::Custom(_, msg) => write!(f, "Custom Error: {}", msg),
27        }
28    }
29}
30
31impl StdError for AppError {}
32
33// Convert generic Box<dyn Error> to AppError
34impl From<Box<dyn StdError + Send + Sync>> for AppError {
35    fn from(err: Box<dyn StdError + Send + Sync>) -> Self {
36        // Attempt to downcast to specific AppError variants if possible,
37        // otherwise wrap in Internal.
38        if let Some(app_err) = err.downcast_ref::<AppError>() {
39            app_err.clone() // Now AppError implements Clone
40        } else {
41            AppError::Internal(err.to_string())
42        }
43    }
44}
45
46impl From<hyper::Error> for AppError {
47    fn from(err: hyper::Error) -> Self {
48        AppError::Internal(format!("Hyper error: {}", err))
49    }
50}
51
52impl From<serde_json::Error> for AppError {
53    fn from(err: serde_json::Error) -> Self {
54        AppError::BadRequest(format!("JSON parsing error: {}", err))
55    }
56}
57
58impl From<std::io::Error> for AppError {
59    fn from(err: std::io::Error) -> Self {
60        AppError::Internal(format!("IO error: {}", err))
61    }
62}
63
64impl From<multer::Error> for AppError {
65    fn from(err: multer::Error) -> Self {
66        AppError::BadRequest(format!("Multipart parsing error: {}", err))
67    }
68}
69
70impl From<url::ParseError> for AppError {
71    fn from(err: url::ParseError) -> Self {
72        AppError::BadRequest(format!("URL parsing error: {}", err))
73    }
74}
75
76// Trait for converting AppError to Response
77pub trait IntoResponse {
78    fn into_response(&self) -> Result<Response, Box<dyn StdError + Send + Sync>>;
79}
80
81impl IntoResponse for AppError {
82    fn into_response(&self) -> Result<Response, Box<dyn StdError + Send + Sync>> {
83        let (status, message) = match self {
84            AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
85            AppError::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
86            AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
87            AppError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg.clone()),
88            AppError::Forbidden(msg) => (StatusCode::FORBIDDEN, msg.clone()),
89            AppError::Custom(s, msg) => (*s, msg.clone()),
90        };
91
92        let error_page = div()
93            .class("container")
94            .child(h1().child(text(&format!("Error {}: {}", status.as_u16(), status.canonical_reason().unwrap_or("Unknown Error")))))
95            .child(p().child(text(&message)));
96
97        get_renderer().render_to_response(&error_page)
98            .map(|res| res.status(status))
99            .map_err(|e| e) // Changed this line: 'e' is already the correct Box<dyn StdError> type
100    }
101}