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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
//! Portable figures: a self-describing artifact that renders itself anywhere.
//!
//! This module implements the `.riz` artifact of `design/10-portable-figure.md`
//! (issue #287): a chunked binary container holding a strict, versioned wire
//! model of the semantic figure — `Figure → Axes → Artists`, data, and rcparams
//! — from which an identical [`Figure`](crate::figure::Figure) is
//! reconstructed and rendered through the ordinary pipeline. The promise is
//! **pixel identity**: `Figure::from_portable(fig.to_portable()?)` renders
//! byte-for-byte the same PNG as `fig` itself.
//!
//! Everything the format needs lives here, behind the default-on `portable`
//! feature:
//!
//! - [`container`] — the `RZFG` chunk framing, and the only part an
//! independent implementation has to reimplement.
//! - [`inspect`] — read an artifact's size, title, alt text, poster, and
//! schema **without building a figure or rendering one**. This is the entry
//! point a host uses for the common case, which is the case where it never
//! draws: a transcript holding hundreds of figures paints a card for nearly
//! all of them and renders one or two.
//! - [`Limits`] — the budgets a caller imposes on artifacts it did not
//! produce, enforced before allocating.
//! - `spec` — the wire model: plain serde mirrors of the live types, every
//! struct `deny_unknown_fields`, every enum closed. Unknown content is a hard
//! error, never silently dropped: a figure missing an artist is a
//! scientifically wrong figure.
//! - `data` — typed binary accessors: bulk arrays live in one binary chunk,
//! referenced by index from the JSON spec.
//!
//! The figure-level entry points live on `Figure`:
//! [`to_portable`](crate::figure::Figure::to_portable),
//! [`to_portable_with`](crate::figure::Figure::to_portable_with),
//! [`save_portable`](crate::figure::Figure::save_portable),
//! [`from_portable`](crate::figure::Figure::from_portable), and
//! [`from_portable_limited`](crate::figure::Figure::from_portable_limited).
//!
//! # Trust
//!
//! An artifact's `renderer.sha256` is an **identity and a lookup key, never an
//! authorization**. A hostile artifact can inline a hostile renderer and
//! honestly report its digest, so verifying artifact bytes against the
//! artifact's own claim is circular and buys nothing. A host keeps its own
//! allowlist mapping schema (and optionally version) to renderers *it* vetted,
//! serves its own copy, and ignores any `WASM` chunk for execution.
pub
pub
pub use ChunkRef;
pub use ;
pub use PortableError;
pub use ;
pub use inspect;
pub use Limits;
pub use ;
pub use ;
/// Wire-format schema version written into every artifact.
///
/// Bumped whenever anything changes that would alter how an existing artifact
/// renders. Schema 1 is the original static+interactive model; schema 2 adds
/// the [`Meta`] block and the poster chunk; schema 3 the [`Timeline`]; schema
/// 4 user-driven [`Control`]s; schema 5 axis minor ticks, minor grids and
/// title size/pad; schema 6 the figure-title size and the axes text size.
pub const SCHEMA_VERSION: u32 = 6;
/// Oldest schema version this build can still load.
pub const SCHEMA_MIN: u32 = 1;
/// The schema a document's features require it to declare, at minimum.
///
/// Schema is the compatibility decision a host makes *before* parsing the
/// figure — it selects the runtime. A forged document must not be able to
/// under-declare: a "schema 3" artifact carrying controls would render on a
/// schema-4 runtime while a host that correctly selected a schema-3 runtime
/// from the declaration watches it rejected for the unknown field. Both
/// import and inspection refuse a declaration below what the features demand.
pub
// Without the `portable` feature the artifact machinery is absent, but the
// public `Scale`/`Locator`/`Formatter` traits still name the wire forms below,
// so those stay compiled either way.
pub use PortableError;
/// The portable wire form of a built-in [`Scale`](crate::axis::scale::Scale).
///
/// Opaque by design. The wire model is rizzma's own schema, so only the
/// built-in scales can produce one: a third-party [`Scale`](crate::axis::scale::Scale)
/// returns `None` from [`Scale::portable_spec`](crate::axis::scale::Scale::portable_spec),
/// and exporting a figure that uses it fails loudly rather than substituting a
/// different scale.
ScaleWire);
/// The portable wire form of a built-in [`Locator`](crate::axis::ticker::Locator).
///
/// Opaque by design; see [`PortableScale`].
LocatorSpec);
/// The portable wire form of a built-in [`Formatter`](crate::axis::ticker::Formatter).
///
/// Opaque by design; see [`PortableScale`]. Notably
/// [`FuncFormatter`](crate::axis::ticker::FuncFormatter) has none — a Rust
/// closure cannot cross the wire — so exporting an axis that uses one fails
/// with `PortableError::Unsupported`.
FormatterSpec);
/// How [`Figure::to_portable_with`](crate::figure::Figure::to_portable_with)
/// writes an artifact.
///
/// [`PortableConfig::default`] writes a poster, because a figure that cannot
/// render and has nothing to show is the one failure mode with no recovery.