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}