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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! Island types: the closed dispatch authority.
//!
//! [`Island::island_type`](crate::model::Island::island_type) stays an **open**
//! string on the wire: an unknown type (one this build lacks, arriving via
//! storage) round-trips opaque. [`KnownIslandType`] is that string's *parse* into
//! the closed set, and every site that dispatches on a type matches
//! [`KnownIslandType::parse`]. The `Some(k)` arm is exhaustive, so **adding a
//! variant is a compile error at every dispatch site**; a known type wired into
//! only some sites cannot reach the `None` path, which is the open set's alone.
//!
//! Behavior a type must supply lives on the enum, one method per seam (loss
//! class, cell marks, shape normalize/validate). The two projections that can't
//! live in this crate (markdown emit ([`crate::export`]) and Typst emit (the
//! typst backend)) match on the enum where they are, so the guarantee crosses
//! the crate boundary. The `table` codec stays in [`crate::serial`]; this module
//! dispatches into it.
use crate;
use Value;
/// The island types this build understands. The wire discriminator is the open
/// string [`Island::island_type`](crate::model::Island::island_type); this is its
/// closed parse. Adding a variant forces every dispatch arm (here, the markdown
/// emitter, and the Typst one) to be supplied before the workspace compiles.
///
/// **Deliberately not `#[non_exhaustive]`**, unlike every other public enum in
/// this crate. The attribute forces a `_` arm on downstream matchers, and the
/// Typst emitter dispatches over the whole set from another crate, where that
/// `_` would swallow exactly the compile error described above. The markdown
/// emitter ([`crate::export`]) and the `table` codec ([`crate::serial`]) sit in
/// this crate, where the attribute would do nothing either way; the one
/// out-of-crate site is what the exemption is for.
///
/// The cost is that **adding a variant is a semver-major change**, since a
/// downstream exhaustive match stops compiling. That is the price of the
/// guarantee, paid on purpose: an island type wired into some emitters and not
/// others projects the island away silently, which is worse than a major bump.
/// Routing an internal exhaustive enum through a `#[non_exhaustive]` public
/// re-export would buy the semver back and lose the guarantee: not the trade
/// this module wants.
///
/// The trade runs the other way for [`MarkKind`](crate::model::MarkKind) and
/// [`LineKind`](crate::model::LineKind), which do carry the attribute. What an
/// unhandled arm costs there is decoration: the text still renders, the mark
/// loses its delimiters, the line lowers as a paragraph. An unhandled island
/// type costs the island: content leaves the projection entirely. A silent
/// gap in a total function is worth a major bump; a silent gap in a
/// degradation ladder that already has an `Unknown` rung is not.
// The three `Island`-taking wrappers below are where the open set's `None` arm
// is answered: once each, so `model.rs` calls a total function and no site
// re-decides what an unknown type does.
/// [`KnownIslandType::normalize_props`] for a known type; for an unknown one, a
/// no-op that leaves the opaque props verbatim.
pub
/// [`KnownIslandType::cell_marks`] for a known type; empty for an unknown one.
pub
/// [`KnownIslandType::shape_error`] for a known type; `None` for an unknown one.
/// [`normalize_island_structure`] guarantees `None` for the known ones too.
pub