html2pdf_api/
prelude.rs

1//! Convenient imports for common usage patterns.
2//!
3//! This module re-exports the most commonly used types from `html2pdf-api`,
4//! allowing you to quickly get started with a single import.
5//!
6//! # Quick Start
7//!
8//! ```rust,ignore
9//! use html2pdf_api::prelude::*;
10//! ```
11//!
12//! This single line gives you access to all the core types needed to create
13//! and use a browser pool for PDF generation.
14//!
15//! # What's Included
16//!
17//! ## Core Types (Always Available)
18//!
19//! | Type | Description |
20//! |------|-------------|
21//! | [`BrowserPool`] | Main pool for managing browser instances |
22//! | [`BrowserPoolBuilder`] | Builder for creating configured pools |
23//! | [`BrowserPoolConfig`] | Configuration settings for the pool |
24//! | [`BrowserPoolConfigBuilder`] | Builder for creating configurations |
25//! | [`BrowserPoolError`] | Error type for pool operations |
26//! | [`Result`] | Type alias for `Result<T, BrowserPoolError>` |
27//! | [`BrowserHandle`] | RAII handle for checked-out browsers |
28//! | [`PoolStats`] | Real-time pool statistics |
29//! | [`BrowserFactory`] | Trait for browser creation strategies |
30//! | [`ChromeBrowserFactory`] | Default Chrome/Chromium factory |
31//! | [`Healthcheck`] | Trait for browser health checking |
32//! | [`SharedBrowserPool`] | Type alias for `Arc<Mutex<BrowserPool>>` |
33//!
34//! ## Standard Library Re-exports
35//!
36//! For convenience, commonly needed types are re-exported:
37//!
38//! | Type | Description |
39//! |------|-------------|
40//! | [`Arc`] | Thread-safe reference counting |
41//! | [`Mutex`] | Mutual exclusion lock |
42//!
43//! ## Feature-Gated Exports
44//!
45//! Additional exports are available with feature flags:
46//!
47//! ### `env-config` Feature
48//!
49//! | Export | Description |
50//! |--------|-------------|
51//! | [`init_browser_pool`] | Initialize pool from environment variables |
52//! | [`from_env`] | Load configuration from environment |
53//! | [`chrome_path_from_env`] | Get Chrome path from `CHROME_PATH` env var |
54//!
55//! ### `actix-integration` Feature
56//!
57//! | Export | Description |
58//! |--------|-------------|
59//! | [`actix`] module | Actix-web handlers and helpers |
60//!
61//! ### `rocket-integration` Feature
62//!
63//! | Export | Description |
64//! |--------|-------------|
65//! | [`rocket`] module | Rocket handlers and helpers |
66//!
67//! ### `axum-integration` Feature
68//!
69//! | Export | Description |
70//! |--------|-------------|
71//! | [`axum`] module | Axum handlers and router |
72//!
73//! ### Any Integration Feature
74//!
75//! When any integration feature is enabled, service types are also available:
76//!
77//! | Type | Description |
78//! |------|-------------|
79//! | [`PdfFromUrlRequest`] | Request parameters for URL-to-PDF |
80//! | [`PdfFromHtmlRequest`] | Request parameters for HTML-to-PDF |
81//! | [`PdfResponse`] | Successful PDF generation result |
82//! | [`PdfServiceError`] | Service-level errors |
83//! | [`ErrorResponse`] | JSON error response format |
84//! | [`PoolStatsResponse`] | Pool statistics response |
85//! | [`HealthResponse`] | Health check response |
86//!
87//! # Usage Examples
88//!
89//! ## Basic Usage
90//!
91//! Create a pool with manual configuration:
92//!
93//! ```rust,ignore
94//! use html2pdf_api::prelude::*;
95//! use std::time::Duration;
96//!
97//! #[tokio::main]
98//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
99//!     // Build configuration
100//!     let config = BrowserPoolConfigBuilder::new()
101//!         .max_pool_size(5)
102//!         .warmup_count(3)
103//!         .browser_ttl(Duration::from_secs(3600))
104//!         .build()?;
105//!
106//!     // Create pool
107//!     let pool = BrowserPool::builder()
108//!         .config(config)
109//!         .factory(Box::new(ChromeBrowserFactory::with_defaults()))
110//!         .build()?;
111//!
112//!     // Warmup for production readiness
113//!     pool.warmup().await?;
114//!
115//!     // Use a browser (automatically returned to pool when dropped)
116//!     {
117//!         let browser = pool.get()?;
118//!         let tab = browser.new_tab()?;
119//!         tab.navigate_to("https://example.com")?;
120//!         tab.wait_until_navigated()?;
121//!         let pdf = tab.print_to_pdf(None)?;
122//!         std::fs::write("output.pdf", pdf)?;
123//!     }
124//!
125//!     // Graceful shutdown
126//!     pool.shutdown();
127//!
128//!     Ok(())
129//! }
130//! ```
131//!
132//! ## Using Environment Configuration
133//!
134//! With the `env-config` feature, initialization is simpler:
135//!
136//! ```rust,ignore
137//! use html2pdf_api::prelude::*;
138//!
139//! #[tokio::main]
140//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
141//!     // Reads BROWSER_POOL_SIZE, BROWSER_TTL_SECONDS, etc.
142//!     let pool = init_browser_pool().await?;
143//!
144//!     // Pool is Arc<Mutex<BrowserPool>>, ready for sharing
145//!     let guard = pool.lock().unwrap();
146//!     let browser = guard.get()?;
147//!     // ...
148//!
149//!     Ok(())
150//! }
151//! ```
152//!
153//! ## With Actix-web (Pre-built Routes)
154//!
155//! The fastest way to set up a PDF API:
156//!
157//! ```rust,ignore
158//! use actix_web::{App, HttpServer, web};
159//! use html2pdf_api::prelude::*;
160//! use html2pdf_api::integrations::actix::configure_routes;
161//!
162//! #[actix_web::main]
163//! async fn main() -> std::io::Result<()> {
164//!     let pool = init_browser_pool().await
165//!         .expect("Failed to initialize pool");
166//!
167//!     HttpServer::new(move || {
168//!         App::new()
169//!             .app_data(web::Data::new(pool.clone()))
170//!             .configure(configure_routes) // Adds /pdf, /pdf/html, /health, etc.
171//!     })
172//!     .bind("127.0.0.1:8080")?
173//!     .run()
174//!     .await
175//! }
176//! ```
177//!
178//! ## With Actix-web (Custom Handler)
179//!
180//! For more control, use service functions directly:
181//!
182//! ```rust,ignore
183//! use actix_web::{web, HttpResponse, Responder};
184//! use html2pdf_api::prelude::*;
185//! use html2pdf_api::service::{generate_pdf_from_url, PdfFromUrlRequest};
186//!
187//! async fn my_pdf_handler(
188//!     pool: web::Data<SharedBrowserPool>,
189//!     query: web::Query<PdfFromUrlRequest>,
190//! ) -> impl Responder {
191//!     let pool = pool.into_inner();
192//!     let request = query.into_inner();
193//!
194//!     let result = web::block(move || {
195//!         generate_pdf_from_url(&pool, &request)
196//!     }).await;
197//!
198//!     match result {
199//!         Ok(Ok(pdf)) => HttpResponse::Ok()
200//!             .content_type("application/pdf")
201//!             .body(pdf.data),
202//!         Ok(Err(e)) => HttpResponse::BadRequest().body(e.to_string()),
203//!         Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
204//!     }
205//! }
206//! ```
207//!
208//! ## Checking Pool Statistics
209//!
210//! Monitor pool health in your application:
211//!
212//! ```rust,ignore
213//! use html2pdf_api::prelude::*;
214//!
215//! fn log_pool_status(pool: &SharedBrowserPool) {
216//!     if let Ok(guard) = pool.lock() {
217//!         let stats = guard.stats();
218//!         println!("Pool Status:");
219//!         println!("  Available: {}", stats.available);
220//!         println!("  Active: {}", stats.active);
221//!         println!("  Total: {}", stats.total);
222//!         
223//!         // Check capacity
224//!         if stats.available == 0 {
225//!             println!("  ⚠️ Warning: No idle browsers available");
226//!         }
227//!     }
228//! }
229//! ```
230//!
231//! ## Error Handling
232//!
233//! Handle pool errors appropriately:
234//!
235//! ```rust,ignore
236//! use html2pdf_api::prelude::*;
237//!
238//! fn generate_pdf(pool: &SharedBrowserPool, url: &str) -> Result<Vec<u8>> {
239//!     let guard = pool.lock()
240//!         .map_err(|_| BrowserPoolError::PoolLock)?;
241//!
242//!     let browser = guard.get()?;  // Returns BrowserPoolError
243//!     let tab = browser.new_tab()
244//!         .map_err(|e| BrowserPoolError::BrowserCreation(e.to_string()))?;
245//!
246//!     tab.navigate_to(url)
247//!         .map_err(|e| BrowserPoolError::BrowserCreation(e.to_string()))?;
248//!     tab.wait_until_navigated()
249//!         .map_err(|e| BrowserPoolError::BrowserCreation(e.to_string()))?;
250//!
251//!     let pdf = tab.print_to_pdf(None)
252//!         .map_err(|e| BrowserPoolError::BrowserCreation(e.to_string()))?;
253//!
254//!     Ok(pdf)
255//! }
256//! ```
257//!
258//! # Feature Flag Reference
259//!
260//! | Feature | Adds to Prelude |
261//! |---------|-----------------|
262//! | (none) | Core types only |
263//! | `env-config` | `init_browser_pool`, `from_env`, `chrome_path_from_env` |
264//! | `actix-integration` | `actix` module + service types |
265//! | `rocket-integration` | `rocket` module + service types |
266//! | `axum-integration` | `axum` module + service types |
267//! | `test-utils` | `MockBrowserFactory` (in `factory::mock`) |
268//!
269//! # See Also
270//!
271//! - [`crate::pool`] - Browser pool implementation details
272//! - [`crate::config`] - Configuration options
273//! - [`crate::service`] - Core PDF generation service
274//! - [`crate::integrations`] - Web framework integrations
275
276// ============================================================================
277// Core Types (Always Available)
278// ============================================================================
279
280/// The main browser pool type for managing browser instances.
281///
282/// See [`crate::pool::BrowserPool`] for full documentation.
283pub use crate::pool::BrowserPool;
284
285/// Builder for creating configured [`BrowserPool`] instances.
286///
287/// See [`crate::pool::BrowserPoolBuilder`] for full documentation.
288pub use crate::pool::BrowserPoolBuilder;
289
290/// Configuration settings for the browser pool.
291///
292/// See [`crate::config::BrowserPoolConfig`] for full documentation.
293pub use crate::config::BrowserPoolConfig;
294
295/// Builder for creating [`BrowserPoolConfig`] instances.
296///
297/// See [`crate::config::BrowserPoolConfigBuilder`] for full documentation.
298pub use crate::config::BrowserPoolConfigBuilder;
299
300/// Error type for browser pool operations.
301///
302/// See [`crate::error::BrowserPoolError`] for full documentation.
303pub use crate::error::BrowserPoolError;
304
305/// Result type alias using [`BrowserPoolError`].
306///
307/// Equivalent to `std::result::Result<T, BrowserPoolError>`.
308pub use crate::error::Result;
309
310/// RAII handle for a browser checked out from the pool.
311///
312/// When dropped, the browser is automatically returned to the pool.
313/// See [`crate::handle::BrowserHandle`] for full documentation.
314pub use crate::handle::BrowserHandle;
315
316/// Real-time statistics about the browser pool.
317///
318/// See [`crate::stats::PoolStats`] for full documentation.
319pub use crate::stats::PoolStats;
320
321/// Trait for browser creation strategies.
322///
323/// Implement this trait to customize how browsers are created.
324/// See [`crate::factory::BrowserFactory`] for full documentation.
325pub use crate::factory::BrowserFactory;
326
327/// Default factory for creating Chrome/Chromium browsers.
328///
329/// See [`crate::factory::ChromeBrowserFactory`] for full documentation.
330pub use crate::factory::ChromeBrowserFactory;
331
332/// Trait for browser health checking.
333///
334/// See [`crate::traits::Healthcheck`] for full documentation.
335pub use crate::traits::Healthcheck;
336
337/// Type alias for a shared, thread-safe browser pool.
338///
339/// This is defined as `Arc<Mutex<BrowserPool>>` and is the standard
340/// way to share a pool across threads and async tasks.
341///
342/// # Example
343///
344/// ```rust,ignore
345/// use html2pdf_api::prelude::*;
346///
347/// let pool: SharedBrowserPool = Arc::new(Mutex::new(
348///     BrowserPool::builder()
349///         .factory(Box::new(ChromeBrowserFactory::with_defaults()))
350///         .build()
351///         .unwrap()
352/// ));
353///
354/// // Or use the convenience method:
355/// let pool: SharedBrowserPool = BrowserPool::builder()
356///     .factory(Box::new(ChromeBrowserFactory::with_defaults()))
357///     .build()
358///     .unwrap()
359///     .into_shared();
360/// ```
361pub use crate::SharedBrowserPool;
362
363// ============================================================================
364// Standard Library Re-exports
365// ============================================================================
366
367/// Thread-safe reference counting pointer.
368///
369/// Re-exported for convenience when working with [`SharedBrowserPool`].
370///
371/// # Example
372///
373/// ```rust,ignore
374/// use html2pdf_api::prelude::*;
375///
376/// let pool = init_browser_pool().await?;
377/// let pool_clone = Arc::clone(&pool);  // Clone for another thread/task
378/// ```
379pub use std::sync::Arc;
380
381/// Mutual exclusion primitive.
382///
383/// Re-exported for convenience when working with [`SharedBrowserPool`].
384///
385/// # Example
386///
387/// ```rust,ignore
388/// use html2pdf_api::prelude::*;
389///
390/// let pool = init_browser_pool().await?;
391/// let guard = pool.lock().unwrap();  // Acquire lock
392/// let browser = guard.get()?;
393/// ```
394pub use std::sync::Mutex;
395
396// ============================================================================
397// Environment Configuration (env-config feature)
398// ============================================================================
399
400/// Initialize a browser pool from environment variables.
401///
402/// This function reads configuration from environment variables and creates
403/// a ready-to-use shared pool. It's the recommended way to initialize the
404/// pool in production.
405///
406/// # Environment Variables
407///
408/// | Variable | Type | Default | Description |
409/// |----------|------|---------|-------------|
410/// | `BROWSER_POOL_SIZE` | usize | 5 | Maximum browsers in pool |
411/// | `BROWSER_WARMUP_COUNT` | usize | 3 | Browsers to pre-create |
412/// | `BROWSER_TTL_SECONDS` | u64 | 3600 | Browser lifetime |
413/// | `BROWSER_WARMUP_TIMEOUT_SECONDS` | u64 | 60 | Warmup timeout |
414/// | `BROWSER_PING_INTERVAL_SECONDS` | u64 | 15 | Health check interval |
415/// | `BROWSER_MAX_PING_FAILURES` | u32 | 3 | Max failures before removal |
416/// | `CHROME_PATH` | String | auto | Custom Chrome path |
417///
418/// # Example
419///
420/// ```rust,ignore
421/// use html2pdf_api::prelude::*;
422///
423/// #[tokio::main]
424/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
425///     let pool = init_browser_pool().await?;
426///     // Pool is ready to use!
427///     Ok(())
428/// }
429/// ```
430///
431/// # Errors
432///
433/// Returns an error if:
434/// - Environment variables contain invalid values
435/// - Chrome/Chromium cannot be found or launched
436/// - Pool warmup fails
437#[cfg(feature = "env-config")]
438pub use crate::pool::init_browser_pool;
439
440/// Load pool configuration from environment variables.
441///
442/// Use this when you need to customize pool creation but still want
443/// environment-based configuration.
444///
445/// # Example
446///
447/// ```rust,ignore
448/// use html2pdf_api::prelude::*;
449///
450/// let config = from_env()?;
451/// let pool = BrowserPool::builder()
452///     .config(config)
453///     .factory(Box::new(ChromeBrowserFactory::with_defaults()))
454///     .enable_keep_alive(true)  // Custom option
455///     .build()?;
456/// ```
457#[cfg(feature = "env-config")]
458pub use crate::config::env::from_env;
459
460/// Get Chrome path from the `CHROME_PATH` environment variable.
461///
462/// Returns `Some(path)` if the variable is set, `None` otherwise.
463///
464/// # Example
465///
466/// ```rust,ignore
467/// use html2pdf_api::prelude::*;
468///
469/// let factory = match chrome_path_from_env() {
470///     Some(path) => ChromeBrowserFactory::with_path(path),
471///     None => ChromeBrowserFactory::with_defaults(),
472/// };
473/// ```
474#[cfg(feature = "env-config")]
475pub use crate::config::env::chrome_path_from_env;
476
477// ============================================================================
478// Service Types (Any Integration Feature)
479// ============================================================================
480
481/// Request parameters for URL-to-PDF conversion.
482///
483/// See [`crate::service::PdfFromUrlRequest`] for full documentation.
484#[cfg(any(
485    feature = "actix-integration",
486    feature = "rocket-integration",
487    feature = "axum-integration"
488))]
489pub use crate::service::PdfFromUrlRequest;
490
491/// Request parameters for HTML-to-PDF conversion.
492///
493/// See [`crate::service::PdfFromHtmlRequest`] for full documentation.
494#[cfg(any(
495    feature = "actix-integration",
496    feature = "rocket-integration",
497    feature = "axum-integration"
498))]
499pub use crate::service::PdfFromHtmlRequest;
500
501/// Successful PDF generation result.
502///
503/// Contains the PDF binary data and metadata.
504/// See [`crate::service::PdfResponse`] for full documentation.
505#[cfg(any(
506    feature = "actix-integration",
507    feature = "rocket-integration",
508    feature = "axum-integration"
509))]
510pub use crate::service::PdfResponse;
511
512/// Errors that can occur during PDF generation.
513///
514/// Includes HTTP status code mapping for easy response building.
515/// See [`crate::service::PdfServiceError`] for full documentation.
516#[cfg(any(
517    feature = "actix-integration",
518    feature = "rocket-integration",
519    feature = "axum-integration"
520))]
521pub use crate::service::PdfServiceError;
522
523/// JSON error response format.
524///
525/// See [`crate::service::ErrorResponse`] for full documentation.
526#[cfg(any(
527    feature = "actix-integration",
528    feature = "rocket-integration",
529    feature = "axum-integration"
530))]
531pub use crate::service::ErrorResponse;
532
533/// Pool statistics response for API endpoints.
534///
535/// See [`crate::service::PoolStatsResponse`] for full documentation.
536#[cfg(any(
537    feature = "actix-integration",
538    feature = "rocket-integration",
539    feature = "axum-integration"
540))]
541pub use crate::service::PoolStatsResponse;
542
543/// Health check response for API endpoints.
544///
545/// See [`crate::service::HealthResponse`] for full documentation.
546#[cfg(any(
547    feature = "actix-integration",
548    feature = "rocket-integration",
549    feature = "axum-integration"
550))]
551pub use crate::service::HealthResponse;
552
553// ============================================================================
554// Framework-Specific Modules
555// ============================================================================
556
557/// Actix-web integration module.
558///
559/// Provides pre-built handlers and helpers for Actix-web 4.x.
560///
561/// # Quick Start
562///
563/// ```rust,ignore
564/// use actix_web::{App, HttpServer, web};
565/// use html2pdf_api::prelude::*;
566/// use html2pdf_api::integrations::actix::configure_routes;
567///
568/// #[actix_web::main]
569/// async fn main() -> std::io::Result<()> {
570///     let pool = init_browser_pool().await.unwrap();
571///
572///     HttpServer::new(move || {
573///         App::new()
574///             .app_data(web::Data::new(pool.clone()))
575///             .configure(configure_routes)
576///     })
577///     .bind("127.0.0.1:8080")?
578///     .run()
579///     .await
580/// }
581/// ```
582///
583/// # Available Exports
584///
585/// | Export | Description |
586/// |--------|-------------|
587/// | `configure_routes` | Configure all pre-built routes |
588/// | `pdf_from_url` | Handler for URL-to-PDF |
589/// | `pdf_from_html` | Handler for HTML-to-PDF |
590/// | `pool_stats` | Handler for pool statistics |
591/// | `health_check` | Handler for health check |
592/// | `readiness_check` | Handler for readiness check |
593/// | `SharedPool` | Type alias for `Arc<Mutex<BrowserPool>>` |
594/// | `BrowserPoolData` | Type alias for `web::Data<SharedBrowserPool>` |
595/// | `BrowserPoolActixExt` | Extension trait for `BrowserPool` |
596///
597/// See [`crate::integrations::actix`] for full documentation.
598#[cfg(feature = "actix-integration")]
599pub mod actix {
600    pub use crate::integrations::actix::*;
601}
602
603/// Rocket integration module.
604///
605/// Provides pre-built handlers and helpers for Rocket 0.5.x.
606///
607/// # Quick Start
608///
609/// ```rust,ignore
610/// use html2pdf_api::prelude::*;
611/// use html2pdf_api::integrations::rocket::routes;
612///
613/// #[rocket::launch]
614/// async fn launch() -> _ {
615///     let pool = init_browser_pool().await.unwrap();
616///
617///     rocket::build()
618///         .manage(pool)
619///         .mount("/", routes())
620/// }
621/// ```
622///
623/// # Available Exports
624///
625/// | Export | Description |
626/// |--------|-------------|
627/// | `routes` | Get all pre-built routes |
628/// | `pdf_from_url` | Handler for URL-to-PDF |
629/// | `pdf_from_html` | Handler for HTML-to-PDF |
630/// | `pool_stats` | Handler for pool statistics |
631/// | `health_check` | Handler for health check |
632/// | `readiness_check` | Handler for readiness check |
633/// | `SharedPool` | Type alias for `Arc<Mutex<BrowserPool>>` |
634///
635/// See [`crate::integrations::rocket`] for full documentation.
636#[cfg(feature = "rocket-integration")]
637pub mod rocket {
638    pub use crate::integrations::rocket::*;
639}
640
641/// Axum integration module.
642///
643/// Provides pre-built handlers and router for Axum 0.7.x/0.8.x.
644///
645/// # Quick Start
646///
647/// ```rust,ignore
648/// use html2pdf_api::prelude::*;
649/// use html2pdf_api::integrations::axum::router;
650///
651/// #[tokio::main]
652/// async fn main() {
653///     let pool = init_browser_pool().await.unwrap();
654///
655///     let app = router().with_state(pool);
656///
657///     let listener = tokio::net::TcpListener::bind("127.0.0.1:8080")
658///         .await
659///         .unwrap();
660///     axum::serve(listener, app).await.unwrap();
661/// }
662/// ```
663///
664/// # Available Exports
665///
666/// | Export | Description |
667/// |--------|-------------|
668/// | `router` | Create router with all pre-built routes |
669/// | `pdf_from_url` | Handler for URL-to-PDF |
670/// | `pdf_from_html` | Handler for HTML-to-PDF |
671/// | `pool_stats` | Handler for pool statistics |
672/// | `health_check` | Handler for health check |
673/// | `readiness_check` | Handler for readiness check |
674/// | `SharedPool` | Type alias for `Arc<Mutex<BrowserPool>>` |
675///
676/// See [`crate::integrations::axum`] for full documentation.
677#[cfg(feature = "axum-integration")]
678pub mod axum {
679    pub use crate::integrations::axum::*;
680}
681
682// ============================================================================
683// Tests
684// ============================================================================
685
686#[cfg(test)]
687mod tests {
688    use super::*;
689
690    /// Verify all core types are accessible.
691    #[test]
692    fn test_core_types_exported() {
693        // These should all compile, proving the types are exported
694        fn _accepts_config(_: BrowserPoolConfig) {}
695        fn _accepts_error(_: BrowserPoolError) {}
696        fn _accepts_stats(_: PoolStats) {}
697        fn _returns_result() -> Result<()> {
698            Ok(())
699        }
700    }
701
702    /// Verify Arc and Mutex are re-exported.
703    #[test]
704    fn test_std_reexports() {
705        let _: Arc<i32> = Arc::new(42);
706        let _: Mutex<i32> = Mutex::new(42);
707    }
708
709    /// Verify SharedBrowserPool type alias works.
710    #[test]
711    fn test_shared_browser_pool_type() {
712        fn _accepts_shared_pool(_: SharedBrowserPool) {}
713
714        // Verify it's Arc<Mutex<BrowserPool>>
715        fn _verify_type() {
716            let pool = BrowserPool::builder()
717                .factory(Box::new(crate::factory::mock::MockBrowserFactory::new()))
718                .build()
719                .unwrap();
720
721            let shared: SharedBrowserPool = Arc::new(Mutex::new(pool));
722            _accepts_shared_pool(shared);
723        }
724    }
725
726    /// Verify env-config exports when feature is enabled.
727    #[cfg(feature = "env-config")]
728    #[test]
729    fn test_env_config_exports() {
730        // These should compile when env-config is enabled
731        let _: Option<String> = chrome_path_from_env();
732        fn _takes_from_env(_: fn() -> crate::error::Result<BrowserPoolConfig>) {}
733        _takes_from_env(from_env);
734    }
735
736    /// Verify service types when any integration is enabled.
737    #[cfg(any(
738        feature = "actix-integration",
739        feature = "rocket-integration",
740        feature = "axum-integration"
741    ))]
742    #[test]
743    fn test_service_types_exported() {
744        let _: PdfFromUrlRequest = PdfFromUrlRequest::default();
745        let _: PdfFromHtmlRequest = PdfFromHtmlRequest::default();
746        let _: HealthResponse = HealthResponse::default();
747    }
748}