Loading...
No commits yet
Not committed History
Blame
_compile_unified.py • 4.6 KB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File: src/scitex_writer/_compile/_compile_unified.py

"""
Unified compilation interface for writer module.

Provides a single `compile()` function that handles all document types
with optional async support.
"""

from __future__ import annotations

import asyncio
from pathlib import Path
from typing import Literal, Optional, Union

from .._dataclasses import CompilationResult
from ._compile_async import (
    compile_all_async,
    compile_manuscript_async,
    compile_revision_async,
    compile_supplementary_async,
)
from .manuscript import compile_manuscript
from .revision import compile_revision
from .supplementary import compile_supplementary

DocType = Literal["manuscript", "supplementary", "revision", "all"]


def compile(
    *doc_types: DocType,
    project_dir: Optional[Path] = None,
    async_: bool = False,
    track_changes: bool = False,
    timeout: int = 300,
) -> Union[CompilationResult, dict, asyncio.coroutine]:
    """
    Unified compilation function for LaTeX documents.

    Args:
        *doc_types: Document types to compile. One or more of:
            - "manuscript": Main document
            - "supplementary": Supplementary materials
            - "revision": Revision response
            - "all": All document types (async only)
        project_dir: Path to writer project directory
        async_: If True, returns awaitable coroutine
        track_changes: Enable change tracking for revision (default: False)
        timeout: Compilation timeout in seconds (default: 300)

    Returns
    -------
        - Single doc_type (sync): CompilationResult
        - Multiple doc_types (sync): dict of {doc_type: CompilationResult}
        - async_=True: Awaitable coroutine returning above

    Examples
    --------
        >>> # Sync single document
        >>> result = compile("manuscript", project_dir=Path("."))

        >>> # Sync multiple documents
        >>> results = compile("manuscript", "supplementary", project_dir=Path("."))
        >>> results["manuscript"].success

        >>> # Async single document
        >>> result = await compile("manuscript", project_dir=Path("."), async_=True)

        >>> # Async all documents (parallel)
        >>> results = await compile("all", project_dir=Path("."), async_=True)
    """
    if not doc_types:
        raise ValueError("At least one document type required")

    if project_dir is None:
        raise ValueError("project_dir is required")

    project_dir = Path(project_dir)

    # Map doc types to functions
    sync_funcs = {
        "manuscript": lambda: compile_manuscript(project_dir, timeout=timeout),
        "supplementary": lambda: compile_supplementary(project_dir, timeout=timeout),
        "revision": lambda: compile_revision(
            project_dir, track_changes, timeout=timeout
        ),
    }

    async_funcs = {
        "manuscript": lambda: compile_manuscript_async(project_dir, timeout=timeout),
        "supplementary": lambda: compile_supplementary_async(
            project_dir, timeout=timeout
        ),
        "revision": lambda: compile_revision_async(
            project_dir, track_changes, timeout=timeout
        ),
    }

    # Handle "all" special case
    if "all" in doc_types:
        if async_:
            return compile_all_async(
                project_dir, track_changes=track_changes, timeout=timeout
            )
        else:
            # Sync "all" - compile sequentially
            return {
                "manuscript": sync_funcs["manuscript"](),
                "supplementary": sync_funcs["supplementary"](),
                "revision": sync_funcs["revision"](),
            }

    # Validate doc types
    valid_types = {"manuscript", "supplementary", "revision"}
    for dt in doc_types:
        if dt not in valid_types:
            raise ValueError(
                f"Invalid document type: {dt}. Must be one of {valid_types}"
            )

    # Single document
    if len(doc_types) == 1:
        doc_type = doc_types[0]
        if async_:
            return async_funcs[doc_type]()
        else:
            return sync_funcs[doc_type]()

    # Multiple documents
    if async_:

        async def _compile_multiple():
            tasks = [async_funcs[dt]() for dt in doc_types]
            results = await asyncio.gather(*tasks, return_exceptions=True)
            return {
                dt: (None if isinstance(r, Exception) else r)
                for dt, r in zip(doc_types, results)
            }

        return _compile_multiple()
    else:
        return {dt: sync_funcs[dt]() for dt in doc_types}


__all__ = ["compile"]

# EOF