html2pdf_api/
lib.rs

1//! # html2pdf-api
2//!
3//! Thread-safe browser pool for headless Chrome automation with web framework integration.
4//!
5//! This crate provides a production-ready browser pool that manages headless Chrome/Chromium
6//! instances with automatic lifecycle management, health monitoring, and graceful shutdown.
7//!
8//! ## Features
9//!
10//! - **Connection Pooling**: Reuses browser instances to avoid expensive startup costs
11//! - **Health Monitoring**: Background thread continuously checks browser health
12//! - **TTL Management**: Automatically retires old browsers and creates replacements
13//! - **Race-Free Design**: Careful lock ordering prevents deadlocks
14//! - **Graceful Shutdown**: Clean termination of all background tasks
15//! - **RAII Pattern**: Automatic return of browsers to pool via Drop
16//! - **Web Framework Integration**: Optional support for Actix-web, Rocket, and Axum
17//!
18//! ## Architecture
19//!
20//! ```text
21//! ┌─────────────────────────────────────────────┐
22//! │         Your Web Application                │
23//! │      (Actix-web / Rocket / Axum)            │
24//! └─────────────────┬───────────────────────────┘
25//!                   │
26//!                   ▼
27//! ┌─────────────────────────────────────────────┐
28//! │              BrowserPool                    │
29//! │ ┌─────────────────────────────────────────┐ │
30//! │ │   Available Pool (idle browsers)        │ │
31//! │ │   [Browser1] [Browser2] [Browser3]      │ │
32//! │ └─────────────────────────────────────────┘ │
33//! │ ┌─────────────────────────────────────────┐ │
34//! │ │   Active Tracking (in-use browsers)     │ │
35//! │ │   {id → Browser}                        │ │
36//! │ └─────────────────────────────────────────┘ │
37//! │ ┌─────────────────────────────────────────┐ │
38//! │ │   Keep-Alive Thread                     │ │
39//! │ │   (health checks + TTL enforcement)     │ │
40//! │ └─────────────────────────────────────────┘ │
41//! └─────────────────────────────────────────────┘
42//!                   │
43//!                   ▼
44//! ┌─────────────────────────────────────────────┐
45//! │        Headless Chrome Browsers             │
46//! │     (managed by headless_chrome crate)      │
47//! └─────────────────────────────────────────────┘
48//! ```
49//!
50//! ## Quick Start
51//!
52//! ```rust,ignore
53//! use html2pdf_api::prelude::*;
54//! use std::time::Duration;
55//!
56//! #[tokio::main]
57//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
58//!     // Create pool with custom configuration
59//!     let pool = BrowserPool::builder()
60//!         .config(
61//!             BrowserPoolConfigBuilder::new()
62//!                 .max_pool_size(5)
63//!                 .warmup_count(3)
64//!                 .browser_ttl(Duration::from_secs(3600))
65//!                 .build()?
66//!         )
67//!         .factory(Box::new(ChromeBrowserFactory::with_defaults()))
68//!         .build()?;
69//!
70//!     // Warmup the pool (recommended for production)
71//!     pool.warmup().await?;
72//!
73//!     // Use browsers
74//!     {
75//!         let browser = pool.get()?;
76//!         let tab = browser.new_tab()?;
77//!         tab.navigate_to("https://example.com")?;
78//!         // ... generate PDF, take screenshot, etc.
79//!     } // Browser automatically returned to pool
80//!
81//!     // Graceful shutdown
82//!     pool.shutdown_async().await;
83//!
84//!     Ok(())
85//! }
86//! ```
87//!
88//! ## Environment Configuration
89//!
90//! When the `env-config` feature is enabled, you can initialize the pool
91//! from environment variables (loaded from `app.env` file or system environment):
92//!
93//! ```rust,no_run
94//! use html2pdf_api::init_browser_pool;
95//!
96//! #[tokio::main]
97//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
98//!     let pool = init_browser_pool().await?;
99//!     // pool is Arc<Mutex<BrowserPool>>, ready for web handlers
100//!     Ok(())
101//! }
102//! ```
103//!
104//! ### Environment File
105//!
106//! Create an `app.env` file in your project root (not `.env` for better
107//! cross-platform visibility):
108//!
109//! ```text
110//! BROWSER_POOL_SIZE=5
111//! BROWSER_WARMUP_COUNT=3
112//! BROWSER_TTL_SECONDS=3600
113//! ```
114//!
115//! ### Environment Variables
116//!
117//! | Variable | Type | Default | Description |
118//! |----------|------|---------|-------------|
119//! | `BROWSER_POOL_SIZE` | usize | 5 | Maximum browsers in pool |
120//! | `BROWSER_WARMUP_COUNT` | usize | 3 | Browsers to pre-create |
121//! | `BROWSER_TTL_SECONDS` | u64 | 3600 | Browser lifetime (seconds) |
122//! | `BROWSER_WARMUP_TIMEOUT_SECONDS` | u64 | 60 | Warmup timeout |
123//! | `BROWSER_PING_INTERVAL_SECONDS` | u64 | 15 | Health check interval |
124//! | `BROWSER_MAX_PING_FAILURES` | u32 | 3 | Failures before removal |
125//! | `CHROME_PATH` | String | auto | Custom Chrome binary path |
126//!
127//! ## Feature Flags
128//!
129//! | Feature | Description |
130//! |---------|-------------|
131//! | `env-config` | Enable environment-based configuration |
132//! | `actix-integration` | Actix-web framework integration |
133//! | `rocket-integration` | Rocket framework integration |
134//! | `axum-integration` | Axum framework integration |
135//! | `test-utils` | Enable mock factory for testing |
136//!
137//! ## Web Framework Integration
138//!
139//! ### Actix-web
140//!
141//! ```rust,ignore
142//! use actix_web::{web, App, HttpServer};
143//! use html2pdf_api::prelude::*;
144//!
145//! async fn generate_pdf(
146//!     pool: web::Data<Arc<Mutex<BrowserPool>>>,
147//! ) -> impl Responder {
148//!     let pool = pool.lock().unwrap();
149//!     let browser = pool.get()?;
150//!     // ... use browser
151//! }
152//! ```
153//!
154//! ### Rocket
155//!
156//! ```rust,ignore
157//! use rocket::State;
158//! use html2pdf_api::prelude::*;
159//!
160//! #[get("/pdf")]
161//! async fn generate_pdf(
162//!     pool: &State<Arc<Mutex<BrowserPool>>>,
163//! ) -> Result<Vec<u8>, Status> {
164//!     let pool = pool.lock().unwrap();
165//!     let browser = pool.get()?;
166//!     // ... use browser
167//! }
168//! ```
169//!
170//! ### Axum
171//!
172//! ```rust,ignore
173//! use axum::{Extension, response::IntoResponse};
174//! use html2pdf_api::prelude::*;
175//!
176//! async fn generate_pdf(
177//!     Extension(pool): Extension<Arc<Mutex<BrowserPool>>>,
178//! ) -> impl IntoResponse {
179//!     let pool = pool.lock().unwrap();
180//!     let browser = pool.get()?;
181//!     // ... use browser
182//! }
183//! ```
184//!
185//! ## Error Handling
186//!
187//! All fallible operations return [`Result<T, BrowserPoolError>`](Result).
188//! The error type provides context about what went wrong:
189//!
190//! ```rust,ignore
191//! use html2pdf_api::{BrowserPool, BrowserPoolError};
192//!
193//! match pool.get() {
194//!     Ok(browser) => {
195//!         // Use browser
196//!     }
197//!     Err(BrowserPoolError::ShuttingDown) => {
198//!         // Pool is shutting down, handle gracefully
199//!     }
200//!     Err(BrowserPoolError::BrowserCreation(msg)) => {
201//!         // Chrome failed to launch
202//!         eprintln!("Browser creation failed: {}", msg);
203//!     }
204//!     Err(e) => {
205//!         // Other errors
206//!         eprintln!("Pool error: {}", e);
207//!     }
208//! }
209//! ```
210//!
211//! ## Testing
212//!
213//! For testing without Chrome, enable the `test-utils` feature and use
214//! [`MockBrowserFactory`](factory::mock::MockBrowserFactory):
215//!
216//! ```rust,ignore
217//! use html2pdf_api::factory::mock::MockBrowserFactory;
218//!
219//! let factory = MockBrowserFactory::always_fails("Test error");
220//! let pool = BrowserPool::builder()
221//!     .factory(Box::new(factory))
222//!     .enable_keep_alive(false)
223//!     .build()?;
224//! ```
225
226#![doc(html_root_url = "https://docs.rs/html2pdf-api/0.1.0")]
227#![warn(missing_docs)]
228#![warn(rustdoc::missing_crate_level_docs)]
229
230// ============================================================================
231// Modules
232// ============================================================================
233
234pub mod config;
235pub mod error;
236pub mod factory;
237pub mod handle;
238pub mod pool;
239pub mod prelude;
240pub mod stats;
241pub mod traits;
242
243// Internal modules (not publicly exposed)
244pub(crate) mod tracked;
245
246// ============================================================================
247// Feature-gated modules
248// ============================================================================
249
250/// Web framework integrations.
251///
252/// This module provides optional integrations with popular Rust web frameworks.
253/// Enable the corresponding feature flag to use them:
254///
255/// - `actix-integration` for Actix-web
256/// - `rocket-integration` for Rocket
257/// - `axum-integration` for Axum
258#[cfg(any(
259    feature = "actix-integration",
260    feature = "rocket-integration",
261    feature = "axum-integration"
262))]
263pub mod integrations;
264
265/// Core PDF generation service.
266///
267/// Provides framework-agnostic types and functions for PDF generation.
268/// Used by the framework integrations.
269#[cfg(any(
270    feature = "actix-integration",
271    feature = "rocket-integration",
272    feature = "axum-integration"
273))]
274pub mod service;
275
276// ============================================================================
277// Re-exports (Public API)
278// ============================================================================
279
280// Core types
281pub use config::{BrowserPoolConfig, BrowserPoolConfigBuilder};
282pub use error::{BrowserPoolError, Result};
283pub use factory::{BrowserFactory, ChromeBrowserFactory, create_chrome_options};
284pub use handle::BrowserHandle;
285pub use pool::{BrowserPool, BrowserPoolBuilder};
286pub use stats::PoolStats;
287pub use traits::Healthcheck;
288
289// Feature-gated re-exports
290#[cfg(feature = "env-config")]
291pub use config::env::{chrome_path_from_env, from_env};
292
293#[cfg(feature = "env-config")]
294pub use pool::init_browser_pool;
295
296// ============================================================================
297// Convenience type aliases
298// ============================================================================
299
300/// Shared browser pool type for web frameworks.
301///
302/// This is the recommended type for sharing a pool across web handlers.
303///
304/// # Example
305///
306/// ```rust,ignore
307/// use html2pdf_api::SharedBrowserPool;
308///
309/// let pool: SharedBrowserPool = browser_pool.into_shared();
310/// ```
311pub type SharedBrowserPool = std::sync::Arc<std::sync::Mutex<BrowserPool>>;