from __future__ import annotations from pathlib import Path import subprocess import xml.etree.ElementTree as ET class MustangError(RuntimeError): pass def _run(jar: Path, args: list[str], logger) -> subprocess.CompletedProcess[str]: if not jar.exists(): raise MustangError(f"Mustang jar not found: {jar}") cmd = ["java", "-Xmx1G", "-Dfile.encoding=UTF-8", "-jar", str(jar)] + args logger.debug("Running Mustang: %s", " ".join(cmd)) proc = subprocess.run(cmd, capture_output=True, text=True, timeout=180) logger.debug("Mustang stdout:\n%s", proc.stdout.strip()) logger.debug("Mustang stderr:\n%s", proc.stderr.strip()) return proc def combine_facturx(jar: Path, source_pdf: Path, source_xml: Path, out_pdf: Path, logger, profile: str = "E") -> Path: out_pdf.parent.mkdir(parents=True, exist_ok=True) proc = _run(jar, [ "--action", "combine", "--source", str(source_pdf), "--source-xml", str(source_xml), "--out", str(out_pdf), "--format", "fx", "--version", "1", "--profile", profile, "--no-additional-attachments" ], logger) if proc.returncode != 0: raise MustangError(f"Mustang combine failed (rc={proc.returncode}). See logs for details.") if not out_pdf.is_file() or out_pdf.stat().st_size == 0: raise MustangError("Mustang produced no PDF") return out_pdf def validate(jar: Path, source: Path, out_report: Path, logger, no_notices: bool = True) -> Path: out_report.parent.mkdir(parents=True, exist_ok=True) args = [] if no_notices: args += ["--no-notices"] args += ["--action", "validate", "--source", str(source)] proc = _run(jar, args, logger) # Persist reports even on failure; a zero exit code alone is insufficient. content = proc.stdout.strip() out_report.write_text(content, encoding="utf-8") try: root = ET.fromstring(content) summaries = root.findall(".//summary") valid = (root.tag == "validation" and root.find("xml/summary") is not None and root.find("summary") is not None and all(s.get("status") == "valid" for s in summaries)) except ET.ParseError: valid = False if proc.returncode != 0 or not valid: raise MustangError(f"Validation failed; see {out_report} and logs") return out_report