1#[cfg(feature = "server")]
2use axum::{
3 http::StatusCode,
4 response::{IntoResponse, Response},
5 Json,
6};
7#[cfg(feature = "server")]
8use serde_json::json;
9use thiserror::Error;
10
11#[derive(Error, Debug)]
12pub enum WhoisError {
13 #[error("Invalid domain: {0}")]
14 InvalidDomain(String),
15
16 #[error("Invalid IP address: {0}")]
17 InvalidIpAddress(String),
18
19 #[error("Unsupported IP address: {0}")]
20 UnsupportedIpAddress(String),
21
22 #[error("Unsupported TLD: {0}")]
23 UnsupportedTld(String),
24
25 #[error("Network timeout")]
26 Timeout,
27
28 #[error("IO error: {0}")]
29 IoError(#[from] tokio::io::Error),
30
31 #[error("HTTP error: {0}")]
32 HttpError(#[from] reqwest::Error),
33
34 #[error("Regex error: {0}")]
35 RegexError(#[from] regex::Error),
36
37 #[error("Response too large")]
38 ResponseTooLarge,
39
40 #[error("Invalid UTF-8 in response")]
41 InvalidUtf8,
42
43 #[error("Configuration error: {0}")]
44 ConfigError(#[from] config::ConfigError),
45
46 #[error("Internal server error: {0}")]
47 Internal(String),
48}
49
50impl From<tokio::time::error::Elapsed> for WhoisError {
51 fn from(_: tokio::time::error::Elapsed) -> Self {
52 WhoisError::Timeout
53 }
54}
55
56#[cfg(feature = "server")]
57impl IntoResponse for WhoisError {
58 fn into_response(self) -> Response {
59 let (status, error_message) = match &self {
60 WhoisError::InvalidDomain(_) => (StatusCode::BAD_REQUEST, self.to_string()),
62 WhoisError::InvalidIpAddress(_) => (StatusCode::BAD_REQUEST, self.to_string()),
63 WhoisError::UnsupportedIpAddress(_) => (StatusCode::BAD_REQUEST, self.to_string()),
64 WhoisError::UnsupportedTld(_) => (StatusCode::BAD_REQUEST, self.to_string()),
65
66 WhoisError::Timeout => (StatusCode::REQUEST_TIMEOUT, self.to_string()),
68
69 WhoisError::ResponseTooLarge => (StatusCode::PAYLOAD_TOO_LARGE, self.to_string()),
71
72 WhoisError::InvalidUtf8 => (StatusCode::BAD_GATEWAY, "Upstream server returned invalid response".to_string()),
74
75 WhoisError::IoError(e) => {
77 tracing::warn!("IO error: {}", e);
78 (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string())
79 }
80 WhoisError::HttpError(e) => {
81 tracing::warn!("HTTP error: {}", e);
82 (StatusCode::BAD_GATEWAY, "Failed to reach upstream server".to_string())
83 }
84 WhoisError::RegexError(e) => {
85 tracing::warn!("Regex error: {}", e);
86 (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string())
87 }
88 WhoisError::ConfigError(e) => {
89 tracing::error!("Configuration error: {}", e);
90 (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string())
91 }
92 WhoisError::Internal(msg) => {
93 tracing::warn!("Internal error: {}", msg);
94 (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string())
95 }
96 };
97
98 let body = Json(json!({
99 "error": error_message,
100 "status": status.as_u16()
101 }));
102
103 (status, body).into_response()
104 }
105}