use std::io;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ServerError {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("Invalid request: {0}")]
InvalidRequest(String),
#[error("File not found: {0}")]
NotFound(String),
#[error("Forbidden: {0}")]
Forbidden(String),
#[error("Custom error: {0}")]
Custom(String),
#[error("Task failed: {0}")]
TaskFailed(String),
}
impl ServerError {
pub fn invalid_request<T: Into<String>>(message: T) -> Self {
ServerError::InvalidRequest(message.into())
}
pub fn not_found<T: Into<String>>(path: T) -> Self {
ServerError::NotFound(path.into())
}
pub fn forbidden<T: Into<String>>(message: T) -> Self {
ServerError::Forbidden(message.into())
}
}
impl From<&str> for ServerError {
fn from(error: &str) -> Self {
ServerError::Custom(error.to_string())
}
}
impl From<ServerError> for io::Error {
fn from(error: ServerError) -> Self {
match error {
ServerError::Io(io_error) => io_error,
ServerError::InvalidRequest(msg) => {
io::Error::new(io::ErrorKind::InvalidInput, msg)
}
ServerError::NotFound(msg) => {
io::Error::new(io::ErrorKind::NotFound, msg)
}
ServerError::Forbidden(msg) => {
io::Error::new(io::ErrorKind::PermissionDenied, msg)
}
ServerError::Custom(msg) => io::Error::other(msg),
ServerError::TaskFailed(msg) => io::Error::other(msg),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io;
#[test]
fn test_io_error_conversion() {
let io_error =
io::Error::new(io::ErrorKind::NotFound, "file not found");
let server_error = ServerError::from(io_error);
assert!(matches!(server_error, ServerError::Io(_)));
}
#[test]
fn test_custom_error_creation() {
let custom_error: ServerError = "Unexpected error".into();
assert!(matches!(custom_error, ServerError::Custom(_)));
}
#[test]
fn test_error_messages() {
let not_found = ServerError::not_found("index.html");
assert_eq!(not_found.to_string(), "File not found: index.html");
let forbidden = ServerError::forbidden("Access denied");
assert_eq!(forbidden.to_string(), "Forbidden: Access denied");
let invalid_request =
ServerError::invalid_request("Missing HTTP method");
assert_eq!(
invalid_request.to_string(),
"Invalid request: Missing HTTP method"
);
}
#[test]
fn test_invalid_request_creation() {
let invalid_request =
ServerError::invalid_request("Bad request");
assert!(matches!(
invalid_request,
ServerError::InvalidRequest(_)
));
assert_eq!(
invalid_request.to_string(),
"Invalid request: Bad request"
);
}
#[test]
fn test_not_found_creation() {
let not_found = ServerError::not_found("/nonexistent.html");
assert!(matches!(not_found, ServerError::NotFound(_)));
assert_eq!(
not_found.to_string(),
"File not found: /nonexistent.html"
);
}
#[test]
fn test_forbidden_creation() {
let forbidden = ServerError::forbidden("Access denied");
assert!(matches!(forbidden, ServerError::Forbidden(_)));
assert_eq!(forbidden.to_string(), "Forbidden: Access denied");
}
#[test]
fn test_custom_error_message() {
let custom_error =
ServerError::Custom("Custom error occurred".to_string());
assert_eq!(
custom_error.to_string(),
"Custom error: Custom error occurred"
);
}
#[test]
fn test_custom_error_from_str() {
let custom_error: ServerError = "Some custom error".into();
assert!(matches!(custom_error, ServerError::Custom(_)));
assert_eq!(
custom_error.to_string(),
"Custom error: Some custom error"
);
}
#[test]
fn test_task_failed_error_message() {
let task_failed =
ServerError::TaskFailed("panic in task".to_string());
assert_eq!(
task_failed.to_string(),
"Task failed: panic in task"
);
}
#[test]
fn test_io_error_conversion_other_kind() {
let io_error = io::Error::new(
io::ErrorKind::PermissionDenied,
"permission denied",
);
let server_error = ServerError::from(io_error);
assert!(matches!(server_error, ServerError::Io(_)));
assert_eq!(
server_error.to_string(),
"I/O error: permission denied"
);
}
#[test]
fn test_invalid_request_message() {
let error_message = "Invalid HTTP version";
let invalid_request =
ServerError::InvalidRequest(error_message.to_string());
assert_eq!(
invalid_request.to_string(),
format!("Invalid request: {}", error_message)
);
}
#[test]
fn test_not_found_message() {
let file_path = "missing.html";
let not_found = ServerError::NotFound(file_path.to_string());
assert_eq!(
not_found.to_string(),
format!("File not found: {}", file_path)
);
}
#[test]
fn test_forbidden_message() {
let forbidden_message = "Access denied to private resource";
let forbidden =
ServerError::Forbidden(forbidden_message.to_string());
assert_eq!(
forbidden.to_string(),
format!("Forbidden: {}", forbidden_message)
);
}
#[test]
fn test_io_error_generic() {
let io_error = io::Error::other("generic I/O error");
let server_error = ServerError::from(io_error);
assert!(matches!(server_error, ServerError::Io(_)));
assert_eq!(
server_error.to_string(),
"I/O error: generic I/O error"
);
}
#[test]
fn test_server_error_to_io_error_conversion() {
let converted: io::Error =
ServerError::invalid_request("invalid").into();
assert_eq!(converted.kind(), io::ErrorKind::InvalidInput);
let converted: io::Error = ServerError::not_found("x").into();
assert_eq!(converted.kind(), io::ErrorKind::NotFound);
let converted: io::Error = ServerError::forbidden("x").into();
assert_eq!(converted.kind(), io::ErrorKind::PermissionDenied);
let converted: io::Error =
ServerError::Custom("custom".to_string()).into();
assert_eq!(converted.kind(), io::ErrorKind::Other);
let converted: io::Error =
ServerError::TaskFailed("task".to_string()).into();
assert_eq!(converted.kind(), io::ErrorKind::Other);
let source = io::Error::new(io::ErrorKind::TimedOut, "timeout");
let converted: io::Error = ServerError::Io(source).into();
assert_eq!(converted.kind(), io::ErrorKind::TimedOut);
}
}