Skip to main content

latexml_post/
diag.rs

1//! Diagnostic emission for `latexml_post`.
2//!
3//! Five capitalized macros — `Note!`, `Info!`, `Warn!`, `Error!`,
4//! `Fatal!` — mirror the LaTeXML Perl `Note()`/`Info()`/`Warn()`/
5//! `Error()`/`Fatal()` reporting conventions. Local to this crate so
6//! `latexml_post` does not need to import `latexml_core::common::error`
7//! just to emit diagnostics. The macro names deliberately shadow the
8//! identically-named macros from `latexml_core` — same shape (`(category,
9//! object, …)`), no location trace appended. Since the single-vehicle
10//! rework, post's `Error!` DOES participate in the MAX_ERRORS /
11//! consecutive-error caps via `emit_error` — the cap latches the sticky
12//! fatal and CONTINUES (no unwind channel here), where Perl's Post neither
13//! counts nor caps ($STATE-gated) and its Fatal dies. Deliberate
14//! divergence, recorded in OXIDIZED_DESIGN. They DO bump the shared `latexml_core`
15//! `REPORT` status counters via `note_status`, so a post-processing
16//! `Warn!`/`Error!`/`Fatal!` raises the conversion's `status_code` exactly
17//! like a core-phase one — the run's severity is the combined worst of the
18//! core and post phases (`cortex_worker` folds them as `max(core, post)`).
19//! Without this a post-only failure (e.g. an image that fails every
20//! converter) logged its line but left `status_code` at 0.
21//!
22//! For diagnostics raised on a post-processing WORKER THREAD (the graphics
23//! conversion pool), both the log text AND these counter bumps are
24//! `#[thread_local]`, so they are captured per-worker and replayed on the
25//! main thread via `latexml_core::util::logger::capture`/`replay_captured`.
26//!
27//! `Info!`/`Warn!`/`Error!` forward to the SINGLE diagnostic vehicle
28//! (`latexml_core::common::error::emit_*`), which counts, respects output
29//! suppression, participates in the runaway circuit-breakers, and emits with
30//! `target = "<category>:<object>"`, yielding the canonical
31//! `{Severity}:{category}:{object} {message}` line the harness
32//! aggregates from every other stage (engine, package, contrib).
33//!
34//! `Note!` is the lone exception: it bypasses the logger formatter
35//! entirely and writes the bare message to stderr, matching the
36//! prefix-less `Note(…)` output style from Perl LaTeXML.
37//!
38//! `Fatal!` additionally `return`s `Err(PostError::Processing(…))`
39//! so the calling function can early-exit via `?`, mirroring the way
40//! Perl `Fatal()` early-exits via `die`. The crate's `PostError` type
41//! differs from `latexml_core::common::error::Error`, which is why we
42//! cannot reuse the upstream `Fatal!`.
43//!
44//! Convention notes carried over from Perl `LaTeXML::Post::*`:
45//!   * `Error('expected', 'source', …)`        — Graphics.pm:216 (missing source)
46//!   * `Error('imageprocessing', $source, …)`  — Graphics.pm:274 (conversion fail)
47//!   * `Error('expected', 'stylesheet', …)`    — XSLT.pm:36/47 (XSLT setup)
48//!   * `Error('missing-file', $stylesheet, …)` — XSLT.pm:42 (missing XSLT)
49//!   * `Error('expected', 'Image::Magick', …)` — LaTeXImages.pm:128 (env)
50//!   * `Error('I/O', $path, …)`                — LaTeXImages.pm:259 (I/O)
51//!   * `Error('shell', $cmd, …)`               — LaTeXImages.pm:293/328 (subprocess)
52//!   * `Fatal('misdefined', (ref $self), …)`   — Post.pm:177/434 (no-process / abstract)
53//!   * `Fatal('unexpected', $dir, …)`          — Post.pm:701 (bad destdir)
54
55#[macro_export]
56macro_rules! Note {
57  ($input:expr_2021) => {{
58    if log::max_level() >= log::LevelFilter::Info {
59      eprintln!("{}", $input);
60    }
61  }};
62}
63
64// The single-message arms delegate to the format arms (`"{}", $msg`) so the
65// `note_status` count + the `log::*!` emission live in exactly ONE place per
66// macro — mirroring `latexml_core`'s own `Error!` self-delegation.
67#[macro_export]
68macro_rules! Info {
69  ($category:expr_2021, $object:expr_2021, $msg:expr_2021) => {
70    $crate::Info!($category, $object, "{}", $msg)
71  };
72  ($category:expr_2021, $object:expr_2021, $fmt:expr_2021, $($arg:tt)+) => {{
73    latexml_core::common::error::emit_info(
74      &format!("{}", $category), &format!("{}", $object), &format!($fmt, $($arg)+))
75  }};
76}
77
78#[macro_export]
79macro_rules! Warn {
80  ($category:expr_2021, $object:expr_2021, $msg:expr_2021) => {
81    $crate::Warn!($category, $object, "{}", $msg)
82  };
83  ($category:expr_2021, $object:expr_2021, $fmt:expr_2021, $($arg:tt)+) => {{
84    latexml_core::common::error::emit_warn(
85      &format!("{}", $category), &format!("{}", $object), &format!($fmt, $($arg)+))
86  }};
87}
88
89#[macro_export]
90macro_rules! Error {
91  ($category:expr_2021, $object:expr_2021, $msg:expr_2021) => {
92    $crate::Error!($category, $object, "{}", $msg)
93  };
94  ($category:expr_2021, $object:expr_2021, $fmt:expr_2021, $($arg:tt)+) => {{
95    latexml_core::common::error::emit_error(
96      &format!("{}", $category), &format!("{}", $object), &format!($fmt, $($arg)+))
97  }};
98}
99
100#[macro_export]
101macro_rules! Fatal {
102  ($category:expr_2021, $object:expr_2021, $msg:expr_2021) => {
103    $crate::Fatal!($category, $object, "{}", $msg)
104  };
105  ($category:expr_2021, $object:expr_2021, $fmt:expr_2021, $($arg:tt)+) => {{
106    let __m = format!($fmt, $($arg)+);
107    latexml_core::common::error::emit_fatal(
108      &format!("{}", $category), &format!("{}", $object), &__m);
109    return Err($crate::processor::PostError::Processing(
110      format!("{}:{}: {}", $category, $object, __m)
111    ));
112  }};
113}