http_handle/error.rs
1// src/error.rs
2
3//! Error types for the Http Handle.
4//!
5//! This module defines the various error types that can occur during the operation
6//! of the Http Handle. It provides a centralized place for error handling and
7//! propagation throughout the application.
8//!
9//! The main type exposed by this module is the `ServerError` enum, which
10//! encompasses all possible error conditions the server might encounter.
11
12use std::io;
13use thiserror::Error;
14
15/// Represents the different types of errors that can occur in the server.
16///
17/// This enum defines various errors that can be encountered during the server's operation,
18/// such as I/O errors, invalid requests, file not found, and forbidden access.
19///
20/// # Examples
21///
22/// Creating an I/O error:
23///
24/// ```
25/// use std::io::{Error, ErrorKind};
26/// use http_handle::ServerError;
27///
28/// let io_error = Error::new(ErrorKind::NotFound, "file not found");
29/// let server_error = ServerError::from(io_error);
30/// assert!(matches!(server_error, ServerError::Io(_)));
31/// ```
32///
33/// Creating an invalid request error:
34///
35/// ```
36/// use http_handle::ServerError;
37///
38/// let invalid_request = ServerError::InvalidRequest("Missing HTTP method".to_string());
39/// assert!(matches!(invalid_request, ServerError::InvalidRequest(_)));
40/// ```
41#[derive(Error, Debug)]
42pub enum ServerError {
43 /// An I/O error occurred.
44 #[error("I/O error: {0}")]
45 Io(#[from] io::Error),
46
47 /// The request received by the server was invalid or malformed.
48 #[error("Invalid request: {0}")]
49 InvalidRequest(String),
50
51 /// The requested file was not found on the server.
52 #[error("File not found: {0}")]
53 NotFound(String),
54
55 /// Access to the requested resource is forbidden.
56 #[error("Forbidden: {0}")]
57 Forbidden(String),
58
59 /// A custom error type for unexpected scenarios.
60 #[error("Custom error: {0}")]
61 Custom(String),
62}
63
64impl ServerError {
65 /// Creates a new `InvalidRequest` error with the given message.
66 ///
67 /// # Arguments
68 ///
69 /// * `message` - A string slice that holds the error message.
70 ///
71 /// # Returns
72 ///
73 /// A `ServerError::InvalidRequest` variant.
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use http_handle::ServerError;
79 ///
80 /// let error = ServerError::invalid_request("Missing HTTP version");
81 /// assert!(matches!(error, ServerError::InvalidRequest(_)));
82 /// ```
83 pub fn invalid_request<T: Into<String>>(message: T) -> Self {
84 ServerError::InvalidRequest(message.into())
85 }
86
87 /// Creates a new `NotFound` error with the given path.
88 ///
89 /// # Arguments
90 ///
91 /// * `path` - A string slice that holds the path of the not found resource.
92 ///
93 /// # Returns
94 ///
95 /// A `ServerError::NotFound` variant.
96 ///
97 /// # Examples
98 ///
99 /// ```
100 /// use http_handle::ServerError;
101 ///
102 /// let error = ServerError::not_found("/nonexistent.html");
103 /// assert!(matches!(error, ServerError::NotFound(_)));
104 /// ```
105 pub fn not_found<T: Into<String>>(path: T) -> Self {
106 ServerError::NotFound(path.into())
107 }
108
109 /// Creates a new `Forbidden` error with the given message.
110 ///
111 /// # Arguments
112 ///
113 /// * `message` - A string slice that holds the error message.
114 ///
115 /// # Returns
116 ///
117 /// A `ServerError::Forbidden` variant.
118 ///
119 /// # Examples
120 ///
121 /// ```
122 /// use http_handle::ServerError;
123 ///
124 /// let error = ServerError::forbidden("Access denied to sensitive file");
125 /// assert!(matches!(error, ServerError::Forbidden(_)));
126 /// ```
127 pub fn forbidden<T: Into<String>>(message: T) -> Self {
128 ServerError::Forbidden(message.into())
129 }
130}
131
132impl From<&str> for ServerError {
133 /// Converts a string slice into a `ServerError::Custom` variant.
134 ///
135 /// This implementation allows for easy creation of custom errors from string literals.
136 ///
137 /// # Arguments
138 ///
139 /// * `error` - A string slice that holds the error message.
140 ///
141 /// # Returns
142 ///
143 /// A `ServerError::Custom` variant.
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// use http_handle::ServerError;
149 ///
150 /// let error: ServerError = "Unexpected error".into();
151 /// assert!(matches!(error, ServerError::Custom(_)));
152 /// ```
153 fn from(error: &str) -> Self {
154 ServerError::Custom(error.to_string())
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use std::io;
162
163 /// Test case for converting an `io::Error` into `ServerError::Io`.
164 #[test]
165 fn test_io_error_conversion() {
166 let io_error =
167 io::Error::new(io::ErrorKind::NotFound, "file not found");
168 let server_error = ServerError::from(io_error);
169 assert!(matches!(server_error, ServerError::Io(_)));
170 }
171
172 /// Test case for creating a `ServerError::Custom` from a string slice.
173 #[test]
174 fn test_custom_error_creation() {
175 let custom_error: ServerError = "Unexpected error".into();
176 assert!(matches!(custom_error, ServerError::Custom(_)));
177 }
178
179 /// Test case for verifying the error messages of different `ServerError` variants.
180 #[test]
181 fn test_error_messages() {
182 let not_found = ServerError::not_found("index.html");
183 assert_eq!(not_found.to_string(), "File not found: index.html");
184
185 let forbidden = ServerError::forbidden("Access denied");
186 assert_eq!(forbidden.to_string(), "Forbidden: Access denied");
187
188 let invalid_request =
189 ServerError::invalid_request("Missing HTTP method");
190 assert_eq!(
191 invalid_request.to_string(),
192 "Invalid request: Missing HTTP method"
193 );
194 }
195
196 /// Test case for creating a `ServerError::InvalidRequest` using the `invalid_request` method.
197 #[test]
198 fn test_invalid_request_creation() {
199 let invalid_request =
200 ServerError::invalid_request("Bad request");
201 assert!(matches!(
202 invalid_request,
203 ServerError::InvalidRequest(_)
204 ));
205 assert_eq!(
206 invalid_request.to_string(),
207 "Invalid request: Bad request"
208 );
209 }
210
211 /// Test case for creating a `ServerError::NotFound` using the `not_found` method.
212 #[test]
213 fn test_not_found_creation() {
214 let not_found = ServerError::not_found("/nonexistent.html");
215 assert!(matches!(not_found, ServerError::NotFound(_)));
216 assert_eq!(
217 not_found.to_string(),
218 "File not found: /nonexistent.html"
219 );
220 }
221
222 /// Test case for creating a `ServerError::Forbidden` using the `forbidden` method.
223 #[test]
224 fn test_forbidden_creation() {
225 let forbidden = ServerError::forbidden("Access denied");
226 assert!(matches!(forbidden, ServerError::Forbidden(_)));
227 assert_eq!(forbidden.to_string(), "Forbidden: Access denied");
228 }
229
230 /// Test case for verifying the `ServerError::Custom` variant and its error message.
231 #[test]
232 fn test_custom_error_message() {
233 let custom_error =
234 ServerError::Custom("Custom error occurred".to_string());
235 assert_eq!(
236 custom_error.to_string(),
237 "Custom error: Custom error occurred"
238 );
239 }
240
241 /// Test case for checking `ServerError::from` for string conversion.
242 #[test]
243 fn test_custom_error_from_str() {
244 let custom_error: ServerError = "Some custom error".into();
245 assert!(matches!(custom_error, ServerError::Custom(_)));
246 assert_eq!(
247 custom_error.to_string(),
248 "Custom error: Some custom error"
249 );
250 }
251
252 /// Test case for converting `io::Error` using a different error kind to `ServerError::Io`.
253 #[test]
254 fn test_io_error_conversion_other_kind() {
255 let io_error = io::Error::new(
256 io::ErrorKind::PermissionDenied,
257 "permission denied",
258 );
259 let server_error = ServerError::from(io_error);
260 assert!(matches!(server_error, ServerError::Io(_)));
261 assert_eq!(
262 server_error.to_string(),
263 "I/O error: permission denied"
264 );
265 }
266
267 /// Test case for verifying if `ServerError::InvalidRequest` carries the correct error message.
268 #[test]
269 fn test_invalid_request_message() {
270 let error_message = "Invalid HTTP version";
271 let invalid_request =
272 ServerError::InvalidRequest(error_message.to_string());
273 assert_eq!(
274 invalid_request.to_string(),
275 format!("Invalid request: {}", error_message)
276 );
277 }
278
279 /// Test case for verifying if `ServerError::NotFound` carries the correct file path.
280 #[test]
281 fn test_not_found_message() {
282 let file_path = "missing.html";
283 let not_found = ServerError::NotFound(file_path.to_string());
284 assert_eq!(
285 not_found.to_string(),
286 format!("File not found: {}", file_path)
287 );
288 }
289
290 /// Test case for verifying if `ServerError::Forbidden` carries the correct message.
291 #[test]
292 fn test_forbidden_message() {
293 let forbidden_message = "Access denied to private resource";
294 let forbidden =
295 ServerError::Forbidden(forbidden_message.to_string());
296 assert_eq!(
297 forbidden.to_string(),
298 format!("Forbidden: {}", forbidden_message)
299 );
300 }
301
302 /// Test case for `ServerError::Io` with a generic IO error to ensure correct propagation.
303 #[test]
304 fn test_io_error_generic() {
305 let io_error =
306 io::Error::new(io::ErrorKind::Other, "generic I/O error");
307 let server_error = ServerError::from(io_error);
308 assert!(matches!(server_error, ServerError::Io(_)));
309 assert_eq!(
310 server_error.to_string(),
311 "I/O error: generic I/O error"
312 );
313 }
314}