libxml_rs/xml/parser/mod.rs
1//! XML parser — native Rust (§19, §20, §85 Phase 3).
2//!
3//! Implements the XML parser state machine, push parser, SAX/SAX2 integration,
4//! declarations, namespaces, entities, DTD construction, recovery modes,
5//! parser options, and custom input.
6//!
7//! Phase 0: scaffolded. Implementation begins in Phase 3.
8//!
9//! # Upstream contract
10//!
11//! Mirrors upstream parser.c, parserInternals.c and SAX2.c (SRC-LIBXML2-2.15.0,
12//! oracle tree `oracle/historical/src/libxml2-2.15.0/`). The parity target is the
13//! system libxml2 2.15.3 oracle: parser diagnostics, tree structure, exit codes
14//! and push-parser behavior must match byte-identically.
15//!
16//! # Conceptual behavior
17//!
18//! This module is a facade over the parser state machine (state.rs), the lexical
19//! tokenizer (tokenizer.rs), input stack management (input.rs) and the C-ABI
20//! glue layer (helpers.rs). Together they implement the XML parser state
21//! machine, push parser, SAX/SAX2 integration, declarations, namespaces,
22//! entities, DTD construction, recovery modes and parser options.
23//!
24//! # Ownership & safety invariants
25//!
26//! The parser context owns its input stack and SAX handler; the produced
27//! document is transferred to the caller (freed with `xmlFreeDoc`). Input
28//! buffers are owned by the parser input stack; filenames are owned dupes
29//! (R-000169). SAFETY: raw `_xmlParserCtxt` pointers are only touched through
30//! the safe InputBuffer/InputStack wrappers, and the helpers.rs side table
31//! keeps boxed inputs alive without borrowing `ctxt._private`.
32//!
33//! # Historical quirks & epochs
34//!
35//! E-002: the second parse-error diagnostic (Premature end of data) was
36//! regressed in 2.9.10 by the non-recursive refactor (commit 62150ed2), fixed
37//! by de5b624f in 2.9.11, and dropped entirely by the 2.12.x error-handling
38//! rework (commit c6083a32) — the crate matches the 2.12.6+ single-diagnostic
39//! epoch. E-005: xmllint parser exit codes changed 1 to 4 at 2.13.0 (NEWS
40//! 2.13.0 xmllint rework of parsing). QUIRK-0001: default parser limits since
41//! 2.9.0 (commit 52d8ade7) with XML_PARSE_HUGE as the only lift.
42//!
43//! # Deliberate oddities
44//!
45//! Hybrid epochs are deliberate: R-000121 reports the '<' in entity attribute
46//! error once (pre-2.13 count) with the 2.13+ exit 4. Deprecated no-op entry
47//! points (R-000138) reproduce upstream empty bodies. The push parser keeps
48//! chunk-boundary semantics faithful to xmlParseChunk.
49//!
50//! # Proving courts
51//!
52//! PARSER court family (PARSER-LIMIT-*, PARSER-ENTITY-*, PARSER-TEXT-LIMIT-*),
53//! data-ABI probes ERROR-001 and TREE-001, CLI-XMLLINT-0033/0034, the
54//! SECURITY-LIMITS probe, and `cargo test --lib`. Receipts under
55//! courts/receipts/phase-11.
56//!
57//! # Tempting simplifications that would break parity
58//!
59//! Replacing the tokenizer/state-machine split with a one-pass regex or a
60//! third-party parser would break the error-position contract (carets must
61//! point at upstream exact columns; R-000163) and the epoch-pinned diagnostic
62//! counts. Do not drop push-parser support — xmlParseChunk is part of the
63//! oracle surface. Do not lift the default parser limits — that would diverge
64//! from every 2.9+ oracle.
65
66pub(crate) mod helpers;
67pub(crate) mod input;
68pub(crate) mod state;
69
70#[cfg(test)]
71pub(crate) mod debug_test;
72#[cfg(test)]
73pub(crate) mod tests;
74pub(crate) mod tokenizer;