Module service

Module service 

Source
Expand description

Core PDF generation service.

Provides framework-agnostic types and functions for PDF generation. Used by the framework integrations. PDF generation service module.

This module provides the framework-agnostic core of the PDF generation service. It contains shared types, error definitions, and the core PDF generation logic that is reused across all web framework integrations.

§Module Overview

┌─────────────────────────────────────────────────────────────────────────┐
│                        html2pdf-api crate                               │
│                                                                         │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │                    service module (this module)                   │  │
│  │                                                                   │  │
│  │  ┌─────────────────────────┐  ┌─────────────────────────────────┐ │  │
│  │  │      types.rs           │  │          pdf.rs                 │ │  │
│  │  │  ┌───────────────────┐  │  │  ┌───────────────────────────┐  │ │  │
│  │  │  │ PdfFromUrlRequest │  │  │  │ generate_pdf_from_url()   │  │ │  │
│  │  │  │ PdfFromHtmlRequest│  │  │  │ generate_pdf_from_html()  │  │ │  │
│  │  │  │ PdfResponse       │  │  │  │ get_pool_stats()          │  │ │  │
│  │  │  │ PdfServiceError   │  │  │  │ is_pool_ready()           │  │ │  │
│  │  │  │ ErrorResponse     │  │  │  └───────────────────────────┘  │ │  │
│  │  │  │ PoolStatsResponse │  │  │                                 │ │  │
│  │  │  │ HealthResponse    │  │  │                                 │ │  │
│  │  │  └───────────────────┘  │  │                                 │ │  │
│  │  └─────────────────────────┘  └─────────────────────────────────┘ │  │
│  └───────────────────────────────────────────────────────────────────┘  │
│                                    │                                    │
│                                    │ used by                            │
│                                    ▼                                    │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │                    integrations module                            │  │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                │  │
│  │  │  actix.rs   │  │  rocket.rs  │  │   axum.rs   │                │  │
│  │  │  (handlers) │  │  (handlers) │  │  (handlers) │                │  │
│  │  └─────────────┘  └─────────────┘  └─────────────┘                │  │
│  └───────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘

§Design Philosophy

This module follows the “thin handler, thick service” pattern:

LayerResponsibilityThis Module?
ServiceCore business logic, validation, PDF generation✅ Yes
HandlerHTTP request/response mapping, framework glue❌ No (integrations)

Benefits of this design:

  • Single source of truth for PDF generation logic
  • Easy testing without HTTP overhead
  • Framework flexibility - add new frameworks without duplicating logic
  • Type safety - shared types ensure consistency across integrations

§Public API Summary

§Request Types

TypePurposeUsed By
[PdfFromUrlRequest]Parameters for URL → PDF conversionGET /pdf
[PdfFromHtmlRequest]Parameters for HTML → PDF conversionPOST /pdf/html

§Response Types

TypePurposeUsed By
[PdfResponse]Successful PDF generation resultPDF endpoints
[PoolStatsResponse]Browser pool statisticsGET /pool/stats
[HealthResponse]Health check responseGET /health
[ErrorResponse]JSON error responseAll endpoints (on error)

§Error Types

TypePurpose
[PdfServiceError]All possible service errors with HTTP status mapping

§Core Functions

FunctionPurposeBlocking?
[generate_pdf_from_url]Convert URL to PDF⚠️ Yes
[generate_pdf_from_html]Convert HTML to PDF⚠️ Yes
[get_pool_stats]Get pool statistics✅ Fast
[is_pool_ready]Check pool readiness✅ Fast

§Constants

ConstantValuePurpose
[DEFAULT_TIMEOUT_SECS]60Overall operation timeout
[DEFAULT_WAIT_SECS]5JavaScript wait time

§Usage Patterns

The easiest way to use this library is via the pre-built integrations:

use actix_web::{App, HttpServer, web};
use html2pdf_api::prelude::*;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let pool = init_browser_pool().await?;

    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .configure(html2pdf_api::integrations::actix::configure_routes)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

§Pattern 2: Custom Handlers with Service Functions

For custom behavior, use the service functions directly:

use actix_web::{web, HttpResponse};
use html2pdf_api::service::{
    generate_pdf_from_url, PdfFromUrlRequest, PdfServiceError
};
use std::sync::{Arc, Mutex};

async fn custom_pdf_handler(
    pool: web::Data<Arc<Mutex<BrowserPool>>>,
    query: web::Query<PdfFromUrlRequest>,
) -> HttpResponse {
    // Add custom logic: authentication, rate limiting, logging, etc.
    log::info!("Custom handler called for: {}", query.url);

    let pool = pool.into_inner();
    let request = query.into_inner();

    // Call service in blocking context
    let result = web::block(move || {
        generate_pdf_from_url(&pool, &request)
    }).await;

    match result {
        Ok(Ok(pdf)) => {
            // Add custom headers, transform response, etc.
            HttpResponse::Ok()
                .content_type("application/pdf")
                .insert_header(("X-Custom-Header", "value"))
                .body(pdf.data)
        }
        Ok(Err(e)) => {
            // Custom error handling
            HttpResponse::build(http::StatusCode::from_u16(e.status_code()).unwrap())
                .json(serde_json::json!({
                    "error": e.to_string(),
                    "code": e.error_code(),
                    "request_id": "custom-id-123"
                }))
        }
        Err(e) => {
            HttpResponse::InternalServerError().body(e.to_string())
        }
    }
}

§Pattern 3: Direct Service Usage (Non-HTTP)

For CLI tools, batch processing, or testing:

use html2pdf_api::service::{
    generate_pdf_from_url, generate_pdf_from_html,
    PdfFromUrlRequest, PdfFromHtmlRequest,
};
use std::sync::Mutex;

fn batch_convert(pool: &Mutex<BrowserPool>, urls: Vec<String>) -> Vec<Result<Vec<u8>, PdfServiceError>> {
    urls.into_iter()
        .map(|url| {
            let request = PdfFromUrlRequest {
                url,
                landscape: Some(true),
                ..Default::default()
            };
            generate_pdf_from_url(pool, &request).map(|r| r.data)
        })
        .collect()
}

fn generate_report(pool: &Mutex<BrowserPool>, html: String) -> Result<(), Box<dyn std::error::Error>> {
    let request = PdfFromHtmlRequest {
        html,
        filename: Some("report.pdf".to_string()),
        ..Default::default()
    };

    let response = generate_pdf_from_html(pool, &request)?;
    std::fs::write("report.pdf", &response.data)?;
    println!("Generated report: {} bytes", response.size());
    Ok(())
}

§Blocking Behavior

⚠️ Important: The PDF generation functions ([generate_pdf_from_url] and [generate_pdf_from_html]) are blocking and should never be called directly from an async context.

§Correct Usage

// ✅ Actix-web: Use web::block
let result = web::block(move || {
    generate_pdf_from_url(&pool, &request)
}).await;

// ✅ Tokio: Use spawn_blocking
let result = tokio::task::spawn_blocking(move || {
    generate_pdf_from_url(&pool, &request)
}).await;

// ✅ Synchronous context: Call directly
let result = generate_pdf_from_url(&pool, &request);

§Incorrect Usage

// ❌ WRONG: Blocking the async runtime
async fn bad_handler(pool: web::Data<SharedPool>) -> HttpResponse {
    // This blocks the entire async runtime thread!
    let result = generate_pdf_from_url(&pool, &request);
    // ...
}

§Error Handling

All service functions return Result<T, PdfServiceError>. The error type provides HTTP status codes and error codes for easy API response building:

use html2pdf_api::service::{PdfServiceError, ErrorResponse};

fn handle_error(error: PdfServiceError) -> (u16, ErrorResponse) {
    let status = error.status_code();  // e.g., 400, 503, 504
    let response = ErrorResponse::from(&error);
    (status, response)
}

// Check if error is worth retrying
if error.is_retryable() {
    // Wait and retry
    std::thread::sleep(Duration::from_secs(1));
}

§Testing

The service functions can be tested without HTTP:

use html2pdf_api::service::{generate_pdf_from_url, PdfFromUrlRequest, PdfServiceError};
use html2pdf_api::factory::mock::MockBrowserFactory;

#[test]
fn test_invalid_url_returns_error() {
    let pool = create_test_pool();
     
    let request = PdfFromUrlRequest {
        url: "not-a-valid-url".to_string(),
        ..Default::default()
    };
     
    let result = generate_pdf_from_url(&pool, &request);
     
    assert!(matches!(result, Err(PdfServiceError::InvalidUrl(_))));
}

#[test]
fn test_empty_html_returns_error() {
    let pool = create_test_pool();
     
    let request = PdfFromHtmlRequest {
        html: "   ".to_string(),  // whitespace only
        ..Default::default()
    };
     
    let result = generate_pdf_from_html(&pool, &request);
     
    assert!(matches!(result, Err(PdfServiceError::EmptyHtml)));
}

§Feature Flags

This module is always available. However, the types include serde support which is enabled by any integration feature:

FeatureEffect on this module
actix-integrationEnables serde for request/response types
rocket-integrationEnables serde for request/response types
axum-integrationEnables serde for request/response types

§See Also

Structs§

ErrorResponse
JSON error response for API clients.
HealthResponse
Health check response.
PdfFromHtmlRequest
Request parameters for converting HTML content to PDF.
PdfFromUrlRequest
Request parameters for converting a URL to PDF.
PdfResponse
Successful PDF generation result.
PoolStatsResponse
Browser pool statistics response.

Enums§

PdfServiceError
Errors that can occur during PDF generation.

Constants§

DEFAULT_TIMEOUT_SECS
Default timeout for the entire PDF generation operation in seconds.
DEFAULT_WAIT_SECS
Default wait time for JavaScript execution in seconds.

Functions§

generate_pdf_from_html
Generate a PDF from HTML content.
generate_pdf_from_url
Generate a PDF from a URL.
get_pool_stats
Get current browser pool statistics.
is_pool_ready
Check if the browser pool is ready to handle requests.