html2pdf_api/integrations/
actix.rs

1//! Actix-web framework integration.
2//!
3//! This module provides helpers for using `BrowserPool` with Actix-web.
4//!
5//! # Setup
6//!
7//! Add to your `Cargo.toml`:
8//!
9//! ```toml
10//! [dependencies]
11//! html2pdf-api = { version = "0.1", features = ["actix-integration"] }
12//! actix-web = "4"
13//! ```
14//!
15//! # Basic Usage
16//!
17//! ```rust,ignore
18//! use actix_web::{web, App, HttpServer, HttpResponse, Responder};
19//! use html2pdf_api::prelude::*;
20//! use std::sync::Arc;
21//!
22//! async fn generate_pdf(
23//!     pool: web::Data<SharedBrowserPool>,
24//! ) -> impl Responder {
25//!     let pool_guard = pool.lock().unwrap();
26//!     let browser = match pool_guard.get() {
27//!         Ok(b) => b,
28//!         Err(e) => return HttpResponse::InternalServerError().body(e.to_string()),
29//!     };
30//!
31//!     let tab = browser.new_tab().unwrap();
32//!     tab.navigate_to("https://example.com").unwrap();
33//!     
34//!     // Generate PDF...
35//!     let pdf_data = tab.print_to_pdf(None).unwrap();
36//!
37//!     HttpResponse::Ok()
38//!         .content_type("application/pdf")
39//!         .body(pdf_data)
40//! }
41//!
42//! #[actix_web::main]
43//! async fn main() -> std::io::Result<()> {
44//!     // Create and warmup pool
45//!     let pool = BrowserPool::builder()
46//!         .factory(Box::new(ChromeBrowserFactory::with_defaults()))
47//!         .build()
48//!         .expect("Failed to create pool");
49//!
50//!     pool.warmup().await.expect("Failed to warmup");
51//!
52//!     // Convert to shared state
53//!     let shared_pool = pool.into_shared();
54//!
55//!     HttpServer::new(move || {
56//!         App::new()
57//!             .app_data(web::Data::new(Arc::clone(&shared_pool)))
58//!             .route("/pdf", web::get().to(generate_pdf))
59//!     })
60//!     .bind("127.0.0.1:8080")?
61//!     .run()
62//!     .await
63//! }
64//! ```
65//!
66//! # Using with `init_browser_pool`
67//!
68//! If you have the `env-config` feature enabled:
69//!
70//! ```rust,ignore
71//! use actix_web::{web, App, HttpServer};
72//! use html2pdf_api::init_browser_pool;
73//!
74//! #[actix_web::main]
75//! async fn main() -> std::io::Result<()> {
76//!     let pool = init_browser_pool().await
77//!         .expect("Failed to initialize browser pool");
78//!
79//!     HttpServer::new(move || {
80//!         App::new()
81//!             .app_data(web::Data::new(Arc::clone(&pool)))
82//!             .configure(configure_routes)
83//!     })
84//!     .bind("127.0.0.1:8080")?
85//!     .run()
86//!     .await
87//! }
88//! ```
89//!
90//! # Graceful Shutdown
91//!
92//! For proper cleanup, shutdown the pool when the server stops:
93//!
94//! ```rust,ignore
95//! use actix_web::{web, App, HttpServer};
96//! use html2pdf_api::prelude::*;
97//! use std::sync::Arc;
98//!
99//! #[actix_web::main]
100//! async fn main() -> std::io::Result<()> {
101//!     let pool = BrowserPool::builder()
102//!         .factory(Box::new(ChromeBrowserFactory::with_defaults()))
103//!         .build()
104//!         .expect("Failed to create pool");
105//!
106//!     pool.warmup().await.expect("Failed to warmup");
107//!
108//!     let shared_pool = Arc::new(std::sync::Mutex::new(pool));
109//!     let shutdown_pool = Arc::clone(&shared_pool);
110//!
111//!     let server = HttpServer::new(move || {
112//!         App::new()
113//!             .app_data(web::Data::new(Arc::clone(&shared_pool)))
114//!     })
115//!     .bind("127.0.0.1:8080")?
116//!     .run();
117//!
118//!     let server_handle = server.handle();
119//!
120//!     // Run server
121//!     let result = server.await;
122//!
123//!     // Cleanup pool after server stops
124//!     if let Ok(mut pool) = shutdown_pool.lock() {
125//!         pool.shutdown_async().await;
126//!     }
127//!
128//!     result
129//! }
130//! ```
131
132use actix_web::web;
133use std::sync::Arc;
134
135use crate::SharedBrowserPool;
136use crate::pool::BrowserPool;
137
138/// Type alias for Actix-web `Data` wrapper around the shared pool.
139///
140/// Use this type in your handler parameters:
141///
142/// ```rust,ignore
143/// async fn handler(pool: BrowserPoolData) -> impl Responder {
144///     let pool = pool.lock().unwrap();
145///     let browser = pool.get()?;
146///     // ...
147/// }
148/// ```
149pub type BrowserPoolData = web::Data<SharedBrowserPool>;
150
151/// Extension trait for `BrowserPool` with Actix-web helpers.
152///
153/// Provides convenient methods for integrating with Actix-web.
154pub trait BrowserPoolActixExt {
155    /// Convert the pool into Actix-web `Data` wrapper.
156    ///
157    /// This is equivalent to calling `into_shared()` and then wrapping
158    /// with `web::Data::new()`.
159    ///
160    /// # Example
161    ///
162    /// ```rust,ignore
163    /// use html2pdf_api::integrations::actix::BrowserPoolActixExt;
164    ///
165    /// let pool = BrowserPool::builder()
166    ///     .factory(Box::new(ChromeBrowserFactory::with_defaults()))
167    ///     .build()?;
168    ///
169    /// let pool_data = pool.into_actix_data();
170    ///
171    /// HttpServer::new(move || {
172    ///     App::new()
173    ///         .app_data(pool_data.clone())
174    /// })
175    /// ```
176    fn into_actix_data(self) -> BrowserPoolData;
177}
178
179impl BrowserPoolActixExt for BrowserPool {
180    fn into_actix_data(self) -> BrowserPoolData {
181        web::Data::new(self.into_shared())
182    }
183}
184
185/// Create Actix-web `Data` from an existing shared pool.
186///
187/// Use this when you already have a `SharedBrowserPool` and want to
188/// wrap it for Actix-web.
189///
190/// # Parameters
191///
192/// * `pool` - The shared browser pool.
193///
194/// # Returns
195///
196/// `BrowserPoolData` ready for use with `App::app_data()`.
197///
198/// # Example
199///
200/// ```rust,ignore
201/// use html2pdf_api::integrations::actix::create_pool_data;
202///
203/// let shared_pool = pool.into_shared();
204/// let pool_data = create_pool_data(shared_pool);
205///
206/// App::new().app_data(pool_data)
207/// ```
208pub fn create_pool_data(pool: SharedBrowserPool) -> BrowserPoolData {
209    web::Data::new(pool)
210}
211
212/// Create Actix-web `Data` from an `Arc` reference.
213///
214/// Use this when you need to keep a reference to the pool for shutdown.
215///
216/// # Parameters
217///
218/// * `pool` - Arc reference to the shared browser pool.
219///
220/// # Returns
221///
222/// `BrowserPoolData` ready for use with `App::app_data()`.
223///
224/// # Example
225///
226/// ```rust,ignore
227/// use html2pdf_api::integrations::actix::create_pool_data_from_arc;
228///
229/// let shared_pool = pool.into_shared();
230/// let pool_for_shutdown = Arc::clone(&shared_pool);
231/// let pool_data = create_pool_data_from_arc(shared_pool);
232///
233/// // Use pool_data in App
234/// // Use pool_for_shutdown for cleanup
235/// ```
236pub fn create_pool_data_from_arc(pool: Arc<std::sync::Mutex<BrowserPool>>) -> BrowserPoolData {
237    web::Data::new(pool)
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243
244    #[test]
245    fn test_type_alias_compiles() {
246        // This test just verifies the type alias is valid
247        fn _accepts_pool_data(_: BrowserPoolData) {}
248    }
249}