httpward_core/error/
error_handler.rs1use rama::http::{Response, Body, StatusCode};
2use std::fs;
3use std::path::Path;
4
5#[derive(Clone, Debug)]
6pub struct ErrorHandler {
7 template_content: &'static [u8],
8}
9
10impl ErrorHandler {
11 pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
12 let template_path = Path::new("httpward-core/assets/error.html");
13 let template_content = fs::read(template_path)
14 .map_err(|e| format!("Failed to read error template: {}", e))?;
15
16 let static_content = Box::leak(template_content.into_boxed_slice());
18
19 Ok(Self { template_content: static_content })
20 }
21
22 pub fn create_error_response(&self, status: StatusCode, title: &str, description: &str) -> Result<Response<Body>, Box<dyn std::error::Error>> {
23 let status_code = status.as_u16();
24 let template_str = std::str::from_utf8(self.template_content)
25 .map_err(|e| format!("Invalid UTF-8 in template: {}", e))?;
26
27 let content = template_str
28 .replace("{{e_num}}", &status_code.to_string())
29 .replace("{{e_text}}", title)
30 .replace("{{e_desc}}", description);
31
32 Ok(Response::builder()
33 .status(status)
34 .header("content-type", "text/html; charset=utf-8")
35 .body(Body::from(content))
36 .unwrap())
37 }
38
39 pub fn create_error_response_with_code(&self, status: StatusCode) -> Result<Response<Body>, Box<dyn std::error::Error>> {
40 let (title, description) = match status {
41 StatusCode::NOT_FOUND => (
42 "Page Not Found",
43 "The requested page could not be found on this server."
44 ),
45 StatusCode::INTERNAL_SERVER_ERROR => (
46 "Internal Server Error",
47 "An unexpected error occurred while processing your request."
48 ),
49 StatusCode::BAD_GATEWAY => (
50 "Bad Gateway",
51 "The server encountered an error while trying to proxy your request."
52 ),
53 StatusCode::FORBIDDEN => (
54 "Access Forbidden",
55 "You do not have permission to access this resource."
56 ),
57 StatusCode::UNAUTHORIZED => (
58 "Unauthorized",
59 "Authentication is required to access this resource."
60 ),
61 StatusCode::BAD_REQUEST => (
62 "Bad Request",
63 "The server cannot process your request due to invalid syntax."
64 ),
65 _ => (
66 "Error",
67 "An error occurred while processing your request."
68 ),
69 };
70
71 self.create_error_response(status, title, description)
72 }
73}
74
75impl Default for ErrorHandler {
76 fn default() -> Self {
77 Self {
78 template_content: include_bytes!("../../assets/error.html"),
79 }
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use rama::http::StatusCode;
87
88 #[test]
89 fn test_error_handler_creation() {
90 let handler = ErrorHandler::default();
91 assert!(!handler.template_content.is_empty());
92 let template_str = std::str::from_utf8(handler.template_content).unwrap();
93 assert!(template_str.contains("{{e_num}}"));
94 assert!(template_str.contains("{{e_text}}"));
95 assert!(template_str.contains("{{e_desc}}"));
96 }
97
98 #[test]
99 fn test_404_error_response() {
100 let handler = ErrorHandler::default();
101 let response = handler.create_error_response_with_code(StatusCode::NOT_FOUND).unwrap();
102
103 assert_eq!(response.status(), StatusCode::NOT_FOUND);
104 assert_eq!(response.headers().get("content-type").unwrap(), "text/html; charset=utf-8");
105 }
106
107 #[test]
108 fn test_500_error_response() {
109 let handler = ErrorHandler::default();
110 let response = handler.create_error_response_with_code(StatusCode::INTERNAL_SERVER_ERROR).unwrap();
111
112 assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
113 }
114
115 #[test]
116 fn test_custom_error_response() {
117 let handler = ErrorHandler::default();
118 let response = handler.create_error_response(
119 StatusCode::BAD_REQUEST,
120 "Custom Error",
121 "This is a custom error description"
122 ).unwrap();
123
124 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
125 }
126
127 #[test]
128 fn test_template_replacement() {
129 let handler = ErrorHandler::default();
130 let template_str = std::str::from_utf8(handler.template_content).unwrap();
131
132 assert!(template_str.contains("{{e_num}}"));
134 assert!(template_str.contains("{{e_text}}"));
135 assert!(template_str.contains("{{e_desc}}"));
136
137 let result = template_str
139 .replace("{{e_num}}", "404")
140 .replace("{{e_text}}", "Not Found")
141 .replace("{{e_desc}}", "Page not found");
142
143 assert!(result.contains("404"));
144 assert!(result.contains("Not Found"));
145 assert!(result.contains("Page not found"));
146 assert!(!result.contains("{{e_num}}"));
147 assert!(!result.contains("{{e_text}}"));
148 assert!(!result.contains("{{e_desc}}"));
149 }
150}