html2pdf_api/error.rs
1//! Error types for the browser pool.
2//!
3//! This module provides [`BrowserPoolError`], a unified error type for all
4//! browser pool operations, and a convenient [`Result`] type alias.
5//!
6//! # Example
7//!
8//! ```rust
9//! use html2pdf_api::{BrowserPoolError, Result};
10//!
11//! fn process_pdf() -> Result<Vec<u8>> {
12//! // Your logic here...
13//! Err(BrowserPoolError::Configuration("example error".to_string()))
14//! }
15//!
16//! match process_pdf() {
17//! Ok(pdf) => println!("Generated {} bytes", pdf.len()),
18//! Err(BrowserPoolError::ShuttingDown) => println!("Pool is shutting down"),
19//! Err(e) => eprintln!("Error: {}", e),
20//! }
21//! ```
22
23/// Errors that can occur during browser pool operations.
24///
25/// This enum represents all possible error conditions when working with
26/// the browser pool. Each variant includes context about what went wrong.
27///
28/// # Example
29///
30/// ```rust
31/// use html2pdf_api::BrowserPoolError;
32///
33/// fn handle_error(error: BrowserPoolError) {
34/// match error {
35/// BrowserPoolError::BrowserCreation(msg) => {
36/// eprintln!("Browser creation failed: {}", msg);
37/// }
38/// BrowserPoolError::HealthCheckFailed(msg) => {
39/// eprintln!("Health check failed: {}", msg);
40/// }
41/// BrowserPoolError::ShuttingDown => {
42/// eprintln!("Pool is shutting down");
43/// }
44/// BrowserPoolError::Configuration(msg) => {
45/// eprintln!("Configuration error: {}", msg);
46/// }
47/// }
48/// }
49/// ```
50#[derive(Debug, thiserror::Error)]
51pub enum BrowserPoolError {
52 /// Failed to create a new browser instance.
53 ///
54 /// This typically indicates Chrome/Chromium binary issues or launch flag problems.
55 ///
56 /// # Common Causes
57 ///
58 /// - Chrome/Chromium binary not found or not installed
59 /// - Invalid Chrome binary path specified
60 /// - Insufficient permissions to execute Chrome
61 /// - Invalid or conflicting launch flags
62 /// - System resource limits exceeded (e.g., too many processes)
63 ///
64 /// # Example
65 ///
66 /// ```rust
67 /// use html2pdf_api::BrowserPoolError;
68 ///
69 /// let error = BrowserPoolError::BrowserCreation(
70 /// "Chrome binary not found".to_string()
71 /// );
72 /// println!("{}", error); // "Failed to create browser: Chrome binary not found"
73 /// ```
74 #[error("Failed to create browser: {0}")]
75 BrowserCreation(String),
76
77 /// Browser failed a health check operation.
78 ///
79 /// Triggered when ping operations (new_tab, navigate, close) fail.
80 ///
81 /// # Common Causes
82 ///
83 /// - Browser process crashed unexpectedly
84 /// - Out of memory condition
85 /// - CDP (Chrome DevTools Protocol) connection lost
86 /// - Browser tab became unresponsive
87 ///
88 /// # Recovery
89 ///
90 /// The pool automatically removes unhealthy browsers and creates
91 /// replacements. Users typically don't need to handle this error
92 /// specially unless monitoring browser health.
93 ///
94 /// # Example
95 ///
96 /// ```rust
97 /// use html2pdf_api::BrowserPoolError;
98 ///
99 /// let error = BrowserPoolError::HealthCheckFailed(
100 /// "new_tab() failed: connection refused".to_string()
101 /// );
102 /// println!("{}", error); // "Browser health check failed: new_tab() failed: connection refused"
103 /// ```
104 #[error("Browser health check failed: {0}")]
105 HealthCheckFailed(String),
106
107 /// Operation attempted during pool shutdown.
108 ///
109 /// All operations are rejected once shutdown begins.
110 ///
111 /// This error is returned when:
112 /// - [`BrowserPool::shutdown()`](crate::BrowserPool::shutdown) has been called
113 /// - [`BrowserPool::shutdown_async()`](crate::BrowserPool::shutdown_async) has been called
114 /// - The [`BrowserPool`](crate::BrowserPool) is being dropped
115 ///
116 /// # Handling
117 ///
118 /// This error typically occurs during application shutdown. Handle it
119 /// gracefully by stopping any pending work rather than retrying.
120 ///
121 /// # Example
122 ///
123 /// ```rust
124 /// use html2pdf_api::BrowserPoolError;
125 ///
126 /// let error = BrowserPoolError::ShuttingDown;
127 /// println!("{}", error); // "Pool is shutting down"
128 /// ```
129 #[error("Pool is shutting down")]
130 ShuttingDown,
131
132 /// Invalid configuration provided.
133 ///
134 /// This error occurs when pool configuration values are invalid.
135 ///
136 /// # Common Causes
137 ///
138 /// - `max_pool_size` is set to 0
139 /// - `warmup_count` exceeds `max_pool_size`
140 /// - Invalid Chrome binary path
141 /// - Invalid duration values
142 ///
143 /// # Prevention
144 ///
145 /// Use [`BrowserPoolConfigBuilder`](crate::BrowserPoolConfigBuilder)
146 /// which validates configuration at build time.
147 ///
148 /// # Example
149 ///
150 /// ```rust
151 /// use html2pdf_api::BrowserPoolError;
152 ///
153 /// let error = BrowserPoolError::Configuration(
154 /// "max_pool_size must be greater than 0".to_string()
155 /// );
156 /// println!("{}", error); // "Configuration error: max_pool_size must be greater than 0"
157 /// ```
158 #[error("Configuration error: {0}")]
159 Configuration(String),
160}
161
162/// Convenience conversion from [`String`] to [`BrowserPoolError::Configuration`].
163///
164/// Allows using the `?` operator with functions that return `String` errors
165/// in contexts expecting [`BrowserPoolError`].
166///
167/// # Example
168///
169/// ```rust
170/// use html2pdf_api::BrowserPoolError;
171///
172/// let error: BrowserPoolError = "invalid configuration".to_string().into();
173/// assert!(matches!(error, BrowserPoolError::Configuration(_)));
174/// ```
175impl From<String> for BrowserPoolError {
176 fn from(msg: String) -> Self {
177 BrowserPoolError::Configuration(msg)
178 }
179}
180
181/// Convenience conversion from `&str` to [`BrowserPoolError::Configuration`].
182///
183/// Allows using string literals directly where [`BrowserPoolError`] is expected.
184///
185/// # Example
186///
187/// ```rust
188/// use html2pdf_api::BrowserPoolError;
189///
190/// let error: BrowserPoolError = "invalid setting".into();
191/// assert!(matches!(error, BrowserPoolError::Configuration(_)));
192/// ```
193impl From<&str> for BrowserPoolError {
194 fn from(msg: &str) -> Self {
195 BrowserPoolError::Configuration(msg.to_string())
196 }
197}
198
199/// Result type alias using [`BrowserPoolError`].
200///
201/// This is the standard result type returned by most browser pool operations.
202///
203/// # Example
204///
205/// ```rust
206/// use html2pdf_api::Result;
207///
208/// fn my_function() -> Result<String> {
209/// Ok("success".to_string())
210/// }
211/// ```
212pub type Result<T> = std::result::Result<T, BrowserPoolError>;
213
214// ============================================================================
215// Unit Tests
216// ============================================================================
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 /// Verifies error type conversions from String and &str.
223 ///
224 /// The pool uses custom error types with convenient From implementations.
225 /// This test ensures error conversion works as expected.
226 #[test]
227 fn test_error_conversion() {
228 // Test conversion from &str
229 let error: BrowserPoolError = "test error".into();
230 match error {
231 BrowserPoolError::Configuration(msg) => {
232 assert_eq!(msg, "test error", "Error message should be preserved");
233 }
234 _ => panic!("Expected Configuration error variant"),
235 }
236
237 // Test conversion from String
238 let error: BrowserPoolError = "another error".to_string().into();
239 match error {
240 BrowserPoolError::Configuration(msg) => {
241 assert_eq!(msg, "another error", "Error message should be preserved");
242 }
243 _ => panic!("Expected Configuration error variant"),
244 }
245 }
246
247 /// Verifies that error Display formatting works correctly.
248 #[test]
249 fn test_error_display() {
250 let error = BrowserPoolError::BrowserCreation("chrome not found".to_string());
251 assert_eq!(
252 error.to_string(),
253 "Failed to create browser: chrome not found"
254 );
255
256 let error = BrowserPoolError::HealthCheckFailed("ping failed".to_string());
257 assert_eq!(
258 error.to_string(),
259 "Browser health check failed: ping failed"
260 );
261
262 let error = BrowserPoolError::ShuttingDown;
263 assert_eq!(error.to_string(), "Pool is shutting down");
264
265 let error = BrowserPoolError::Configuration("bad config".to_string());
266 assert_eq!(error.to_string(), "Configuration error: bad config");
267 }
268
269 /// Verifies that BrowserPoolError implements std::error::Error.
270 #[test]
271 fn test_error_is_std_error() {
272 fn assert_std_error<T: std::error::Error>() {}
273 assert_std_error::<BrowserPoolError>();
274 }
275
276 /// Verifies that BrowserPoolError is Send + Sync for thread safety.
277 #[test]
278 fn test_error_is_send_sync() {
279 fn assert_send_sync<T: Send + Sync>() {}
280 assert_send_sync::<BrowserPoolError>();
281 }
282}