How the Macro Conversion Service Handles Different Document Formats

The Macro conversion service handles different document formats by detecting file extensions, mapping them to a FileType enum, selecting appropriate LibreOffice filters via get_lok_filter_from_file_types, and executing conversions in sandboxed child processes with a 30-second timeout.

The conversion service in the macro-inc/macro repository provides a robust, format-agnostic pipeline for transforming documents between types like PDF, DOCX, PPTX, and ODT. By leveraging LibreOffice's universal conversion engine through a Rust-based architecture, the service abstracts format complexities behind a simple S3-object-based API. This article explores how the conversion service handles different document formats by examining the source code in services/convert_service/src/process/convert.rs.

File Type Detection and the FileType Enum

The conversion process begins with extracting file extensions from S3 object keys. In services/convert_service/src/process/convert.rs (lines 38-51), the service parses the from_key and to_key fields from the ConvertQueueMessage to identify the source and target formats. The code splits the filename at the extension and passes it to FileType::from_str, which returns a strongly-typed enum variant representing formats like PDF, DOCX, PPTX, ODT, and others.

let from_file_type = FileType::from_str(
    req.from_key.split('.').next_back().unwrap()
)?;
let to_file_type = FileType::from_str(
    req.to_key.split('.').next_back().unwrap()
)?;

Mapping Formats to LibreOffice Filters

Once the FileType values are determined, the service translates these abstract types into concrete LibreOfficeKit (LOK) filter strings. On line 52 of convert.rs, the code calls get_lok_filter_from_file_types, a helper function that accepts references to both the source and target FileType values. This function returns the specific filter name that instructs LibreOffice how to render the source document and which export filter to apply for the destination format, enabling seamless conversion between any supported pair without code modifications.

let filter = get_lok_filter_from_file_types(&from_file_type, &to_file_type)?;
convert(job_id, &req, &s3_client).await?;

Sandboxed Conversion Execution

The actual conversion occurs inside a forked child process to ensure security and stability. Lines 75-84 in convert.rs handle the process spawning, where the service executes the LibreOffice headless binary (lok) via the rs_libreoffice_bindings crate. Inside this sandboxed environment, the service creates an Office object using Office::new, loads the source document via office.load_document, and saves it to the target format using document.save_as with the previously determined filter.

The parent process monitors the child with a strict 30-second timeout (enforced in lines 22-33 and 30-44), terminating unresponsive conversions to prevent resource exhaustion.

Error Handling and Resource Cleanup

If the child process exits with a non-zero status, the parent logs the failure (lines 66-73) and aborts the operation. Regardless of success or failure, the service ensures no temporary files remain on disk by calling cleanup_folder. Finally, the resulting byte stream is uploaded back to the specified S3 bucket, completing the conversion cycle while maintaining clean workspace hygiene.

API Interface and Job Submission

The HTTP endpoint defined in services/convert_service/src/api/convert.rs accepts JSON payloads that map directly to ConvertQueueMessage structures. The handler validates incoming requests containing from_bucket, from_key, to_bucket, and to_key fields before enqueueing them for processing.

Submitting a conversion job (JSON payload):

{
  "from_bucket": "uploads",
  "from_key": "example.docx",
  "to_bucket": "converted",
  "to_key": "example.pdf"
}

Posting the job using Rust and reqwest:

use reqwest::Client;
use serde_json::json;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = Client::new();
    let payload = json!({
        "from_bucket": "uploads",
        "from_key":   "example.docx",
        "to_bucket":  "converted",
        "to_key":     "example.pdf"
    });

    client
        .post("http://localhost:8080/convert")
        .json(&payload)
        .send()
        .await?
        .error_for_status()?;

    println!("Conversion job queued");
    Ok(())
}

Summary

  • The conversion service extracts file extensions from S3 keys and parses them into the FileType enum to identify source and target formats.
  • The get_lok_filter_from_file_types helper selects the appropriate LibreOfficeKit filter for the specific format pair.
  • Conversions execute inside sandboxed child processes with a 30-second timeout to ensure system stability.
  • The parent process monitors exit codes, handles errors via logging, and ensures cleanup of temporary files regardless of conversion success.
  • The API accepts simple JSON payloads describing S3 object locations, making the service agnostic to specific document formats.

Frequently Asked Questions

What document formats does the Macro conversion service support?

The service supports any format defined in the FileType enum, including PDF, DOCX, PPTX, ODT, and others. Because the architecture delegates rendering to LibreOffice's universal engine, adding new formats typically requires only extending the enum and filter mappings without modifying the core conversion logic in convert.rs.

How does the service prevent conversion jobs from hanging indefinitely?

The parent process enforces a 30-second timeout on all child processes that execute LibreOffice operations. If the conversion exceeds this limit, the parent terminates the child process, logs the failure, and aborts the job to prevent resource exhaustion and ensure queue throughput.

Is the conversion process secure against malicious documents?

Yes, the service forks a sandboxed child process to run LibreOffice via the rs_libreoffice_bindings crate. This isolation ensures that any crashes or security issues during document processing remain contained within the child process, protecting the host system and parent process from compromise.

Can I convert between any two supported formats, or are there restrictions?

The service is fully format-agnostic. By combining the FileType detection with get_lok_filter_from_file_types, any permutation of supported source and target formats can be processed without code changes, provided LibreOffice supports the specific conversion path.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →