1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! Tier router — decides whether an input goes to Tier-1 or Tier-2.
use cratePrescanReport;
use crateConversionOptions;
/// The routing decision produced by [`classify`] for a given input + options.
/// Classify the input against the given options and prescan report.
///
/// Returns `Tier2` if ANY of the conditions below are true; otherwise
/// returns `Tier1`.
///
/// # Structural / prescan gates (pre-existing)
///
/// - `report.had_custom_elements` — custom elements require spec-edge handling
/// - `report.had_cdata` — CDATA sections are not supported by Tier-1
/// - `report.had_unescaped_lt` — bare `<` that the prescan escaped
/// - `report.has_svg` — inline SVG triggers Tier-1 bail mid-scan; route
/// directly to Tier-2 to skip the wasted setup
/// - `options.wrap` — wrapping logic lives in the Tier-2 path (for now)
/// - `options.convert_as_inline` — inline-conversion mode not yet in Tier-1
/// - `options.hocr_spatial_tables` — hOCR spatial reconstruction is Tier-2 only
/// - `options.preprocessing.preset != PreprocessingPreset::Standard`
/// — non-standard preprocessing has Tier-2-specific semantics
/// - `!options.strip_tags.is_empty()` — tag stripping requires DOM awareness
/// - `!options.preserve_tags.is_empty()` — tag preservation requires DOM awareness
/// - `options.debug` — debug output is consistent only on Tier-2
///
/// `options.extract_metadata` no longer forces Tier-2: Tier-1 re-parses the
/// prescan's `head_range` slice and produces byte-identical YAML frontmatter.
///
/// `options.keep_inline_images_in` (inline-images feature) is now handled
/// natively in Tier-1; it no longer forces a Tier-2 route.
///
/// Use [`options_allow_tier1`] when only options are available. It lets the
/// dispatcher skip prescan entirely for option combinations that must route to
/// Tier-2.
///
/// # Style-option gates (A1 — router style-option gate)
///
/// Tier-1 hardcodes certain output style choices. When a `ConversionOptions`
/// value deviates from Tier-1's hardcoded value, the output would silently
/// differ from Tier-2's, breaking the byte-equality contract. The following
/// table documents each style option and the gate added here:
///
/// | Option | Tier-1 hardcoded value | Gate added? |
/// |----------------------|-----------------------------------------|------------------------------------------|
/// | `output_format` | `OutputFormat::Markdown` | Yes — non-Markdown |
/// | `heading_style` | `HeadingStyle::Atx` | Yes — non-Atx |
/// | `code_block_style` | `CodeBlockStyle::Indented` | Yes — non-Indented |
/// | `strong_em_symbol` | `'*'` (asterisk) | Yes — any other char |
/// | `bullets` | `"-"` (first char `'-'`) | Yes — other first char |
/// | `list_indent_width` | `2` spaces | Yes — `!= 2` |
/// | `list_indent_type` | `ListIndentType::Spaces` | Yes — Tabs |
/// | `escape_asterisks` | `false` (no escaping) | Yes — `true` |
/// | `escape_underscores` | `false` (no escaping) | Yes — `true` |
/// | `escape_misc` | `false` (no escaping) | Yes — `true` |
/// | `escape_ascii` | `false` (no escaping) | Yes — `true` |
/// | `whitespace_mode` | `WhitespaceMode::Normalized` | Yes — Strict |
/// | `newline_style` | `NewlineStyle::Spaces` | Yes — Backslash |
/// | `code_language` | irrelevant (Indented style) | No — gated via `code_block_style` |
/// | `autolinks` | not implemented in Tier-1 | No — Tier-1 never transforms bare URLs |
/// | `default_title` | `false` (not honored) | Yes — `true` |
/// | `sub_symbol` | `""` (transparent pass-through) | Yes — non-empty |
/// | `sup_symbol` | `""` (transparent pass-through) | Yes — non-empty |
/// | `highlight_style` | transparent (`<mark>` → plain text) | Yes — non-None |
/// | `link_style` | `LinkStyle::Inline` | Yes — Reference |
/// | `url_escape_style` | `UrlEscapeStyle::Angle` (raw href) | Yes — Percent |
/// | `compact_tables` | `false` (padded cells: `\| cell \|`) | Yes — `true` |
/// | `br_in_tables` | bails on `<br>` in cells | No — covered by scanner bail |
/// | `hocr_spatial_tables`| Tier-2 only (structural gate) | Already gated above |
/// Return whether the current options are compatible with Tier-1.
///
/// This intentionally ignores input-dependent gates from [`PrescanReport`].
/// If it returns `false`, Tier-1 cannot produce byte-identical output for any
/// input under these options, so Auto can route straight to Tier-2.