1use thiserror::Error;
2#[cfg(feature = "logging")]
3use tracing::{error, warn};
4
5#[derive(Debug, Error)]
6pub enum PreviewError {
7 #[error("Failed to parse URL: {0}")]
8 UrlParseError(#[from] url::ParseError),
9
10 #[error("Failed to fetch content: {0}")]
11 FetchError(String),
12
13 #[error("Failed to extract metadata: {0}")]
14 ExtractError(String),
15
16 #[error("Cache error: {0}")]
17 CacheError(String),
18
19 #[error("Rate limit exceeded: {0}")]
20 RateLimitError(String),
21
22 #[error("Invalid content type: {0}")]
23 InvalidContentType(String),
24
25 #[error("Request timeout: {0}")]
26 TimeoutError(String),
27
28 #[error("DNS resolution failed: {0}")]
29 DnsError(String),
30
31 #[error("Connection error: {0}")]
32 ConnectionError(String),
33
34 #[error("HTTP {status}: {message}")]
35 HttpError { status: u16, message: String },
36
37 #[error("Server error (5xx): {status} - {message}")]
38 ServerError { status: u16, message: String },
39
40 #[error("Client error (4xx): {status} - {message}")]
41 ClientError { status: u16, message: String },
42
43 #[error("External service error: {service} - {message}")]
44 ExternalServiceError { service: String, message: String },
45
46 #[error("Parse error: {0}")]
47 ParseError(String),
48
49 #[error("Concurrency limit reached")]
50 ConcurrencyLimitError,
51
52 #[error("Resource not found: {0}")]
53 NotFound(String),
54}
55
56impl PreviewError {
57 pub fn log(&self) {
58 #[cfg(feature = "logging")]
59 match self {
60 PreviewError::UrlParseError(e) => {
61 warn!(error = %e, "URL parsing failed");
62 }
63 PreviewError::FetchError(e) => {
64 error!(error = %e, "Content fetch failed");
65 }
66 PreviewError::ExtractError(e) => {
67 error!(error = %e, "Metadata extraction failed");
68 }
69 PreviewError::CacheError(e) => {
70 warn!(error = %e, "Cache operation failed");
71 }
72 PreviewError::RateLimitError(e) => {
73 warn!(error = %e, "Rate limit exceeded");
74 }
75 PreviewError::InvalidContentType(e) => {
76 warn!(error = %e, "Invalid content type received");
77 }
78 PreviewError::TimeoutError(e) => {
79 warn!(error = %e, "Request timed out");
80 }
81 PreviewError::ExternalServiceError { service, message } => {
82 error!(
83 service = %service,
84 error = %message,
85 "External service error occurred"
86 );
87 }
88 PreviewError::ParseError(e) => {
89 error!(error = %e, "Parse error occurred");
90 }
91 PreviewError::ConcurrencyLimitError => {
92 warn!("Concurrency limit reached");
93 }
94 PreviewError::NotFound(e) => {
95 warn!(error = %e, "Resource not found");
96 }
97 PreviewError::DnsError(e) => {
98 error!(error = %e, "DNS resolution failed");
99 }
100 PreviewError::ConnectionError(e) => {
101 error!(error = %e, "Connection failed");
102 }
103 PreviewError::HttpError { status, message } => {
104 warn!(status = %status, error = %message, "HTTP error");
105 }
106 PreviewError::ServerError { status, message } => {
107 error!(status = %status, error = %message, "Server error");
108 }
109 PreviewError::ClientError { status, message } => {
110 warn!(status = %status, error = %message, "Client error");
111 }
112 }
113 #[cfg(not(feature = "logging"))]
114 {
115 }
117 }
118
119 pub fn from_reqwest_error(error: reqwest::Error) -> Self {
121 if error.is_timeout() {
122 PreviewError::TimeoutError(error.to_string())
123 } else if error.is_connect() {
124 let error_msg = error.to_string();
126 if error_msg.contains("dns")
127 || error_msg.contains("resolve")
128 || error_msg.contains("lookup")
129 {
130 PreviewError::DnsError(error_msg)
131 } else {
132 PreviewError::ConnectionError(error_msg)
133 }
134 } else if let Some(status) = error.status() {
135 let status_code = status.as_u16();
136 let message = error.to_string();
137
138 match status_code {
139 404 => PreviewError::NotFound(message),
140 400..=499 => PreviewError::ClientError {
141 status: status_code,
142 message,
143 },
144 500..=599 => PreviewError::ServerError {
145 status: status_code,
146 message,
147 },
148 _ => PreviewError::HttpError {
149 status: status_code,
150 message,
151 },
152 }
153 } else {
154 PreviewError::FetchError(error.to_string())
156 }
157 }
158}