Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_sbj/examples/gen_demo.rs

6.5 KiB, 3 runs

created by r1870400018:21942, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Writes the demo oxeweb document: one signed SBJ file, for every renderer to read.
2//!
3//! There is exactly one demo document, and both engines read it. Kiln renders it to pixels natively
4//! and the browser renders it to DOM through WebAssembly, from these same bytes. That is what makes
5//! the two comparable: a difference between them is a difference between the renderers, not between
6//! two documents that happened to look alike.
7//!
8//! Run with `cargo run -p oxedyne_fe2o3_sbj --example gen_demo -- <path>`.
9
10use oxedyne_fe2o3_sbj::{
11 doc,
12 kinds::NodeKind,
13};
14
15use oxedyne_fe2o3_core::prelude::*;
16use oxedyne_fe2o3_crypto::sign::SignatureScheme;
17use oxedyne_fe2o3_jdat::{
18 prelude::*,
19 usr::UsrKindId,
20};
21
22/// The time the demo document is signed at, fixed so that a rebuild changes only the signature.
23const TIME: u64 = 1_752_500_000_000;
24
25/// The user kind identifier of a node kind: its wire code, its label, and the shape of its payload.
26fn ukid(kind: NodeKind) -> UsrKindId {
27 let payload = if kind.payload_is_str() {
28 Kind::Str
29 } else {
30 Kind::Map
31 };
32 UsrKindId::new(kind.code(), Some(&fmt!("sbj_{}", kind.label())), Some(payload))
33}
34
35/// A node: a kind code, then its payload.
36fn node(kind: NodeKind, payload: Dat) -> Dat {
37 Dat::Usr(ukid(kind), Some(Box::new(payload)))
38}
39
40/// A text run, the one node whose payload is a bare string.
41fn text(s: &str) -> Dat {
42 node(NodeKind::Text, Dat::Str(s.to_string()))
43}
44
45/// A payload map.
46fn map(pairs: Vec<(&str, Dat)>) -> Dat {
47 let mut m = DaticleMap::new();
48 for (k, v) in pairs {
49 m.insert(Dat::Str(k.to_string()), v);
50 }
51 Dat::Map(m)
52}
53
54/// The demo document, exercising every kind the vocabulary holds.
55pub fn tree() -> Dat {
56 let tree = node(NodeKind::Doc, map(vec![
57 ("title", Dat::Str("Style without a cascade".to_string())),
58 ("lang", Dat::Str("en".to_string())),
59 ("styles", map(vec![
60 ("callout", map(vec![
61 ("bg", Dat::Str("muted".to_string())),
62 ("pad", Dat::U8(3)),
63 ])),
64 ("centred", map(vec![
65 ("align", Dat::Str("center".to_string())),
66 ("fill", Dat::Str("muted".to_string())),
67 ])),
68 ("flush", map(vec![
69 ("align", Dat::Str("justify".to_string())),
70 ])),
71 ])),
72 ("children", Dat::List(vec![
73
74 node(NodeKind::Heading, map(vec![
75 ("level", Dat::U8(1)),
76 ("children", Dat::List(vec![text("Style without a cascade")])),
77 ])),
78
79 node(NodeKind::Para, map(vec![
80 ("style", Dat::Str("flush".to_string())),
81 ("children", Dat::List(vec![
82 text("The oxeweb replaces the web's cascade with "),
83 node(NodeKind::Emph, map(vec![
84 ("strong", Dat::Bool(false)),
85 ("children", Dat::List(vec![text("locality")])),
86 ])),
87 text(". A node names a style; the style is defined once, in the document's \
88 style table; and a short inherited set flows down the tree. No rule \
89 reaches across the document, so a style error cannot escape the node \
90 that made it. This paragraph is long enough that it must wrap, which \
91 is the whole point of it."),
92 ])),
93 ])),
94
95 node(NodeKind::Para, map(vec![
96 ("children", Dat::List(vec![
97 text("Emphasis can be "),
98 node(NodeKind::Emph, map(vec![
99 ("strong", Dat::Bool(true)),
100 ("children", Dat::List(vec![text("strong")])),
101 ])),
102 text(", and a link points at "),
103 node(NodeKind::Link, map(vec![
104 ("to", map(vec![("name", Dat::Str("news.cricket".to_string()))])),
105 ("children", Dat::List(vec![text("a name, not a location")])),
106 ])),
107 text("."),
108 ])),
109 ])),
110
111 node(NodeKind::Boxx, map(vec![
112 ("style", Dat::Str("callout".to_string())),
113 ("children", Dat::List(vec![
114 node(NodeKind::Para, map(vec![
115 ("children", Dat::List(vec![
116 text("Reader preferences are applied after author styles, and \
117 always win."),
118 ])),
119 ])),
120 ])),
121 ])),
122
123 node(NodeKind::Heading, map(vec![
124 ("level", Dat::U8(2)),
125 ("children", Dat::List(vec![text("What the format holds")])),
126 ])),
127
128 node(NodeKind::List, map(vec![
129 ("ordered", Dat::Bool(false)),
130 ("children", Dat::List(vec![
131 node(NodeKind::Item, map(vec![
132 ("children", Dat::List(vec![
133 node(NodeKind::Para, map(vec![
134 ("children", Dat::List(vec![
135 text("Thirteen node kinds, and no more."),
136 ])),
137 ])),
138 ])),
139 ])),
140 node(NodeKind::Item, map(vec![
141 ("children", Dat::List(vec![
142 node(NodeKind::Para, map(vec![
143 ("children", Dat::List(vec![
144 text("Semantic colours and scale steps, never pixels."),
145 ])),
146 ])),
147 ])),
148 ])),
149 node(NodeKind::Item, map(vec![
150 ("children", Dat::List(vec![
151 node(NodeKind::Para, map(vec![
152 ("children", Dat::List(vec![
153 text("An address that is a hash of what it addresses."),
154 ])),
155 ])),
156 ])),
157 ])),
158 ])),
159 ])),
160
161 node(NodeKind::Quote, map(vec![
162 ("cite", Dat::Str("SPEC.md §6".to_string())),
163 ("children", Dat::List(vec![
164 node(NodeKind::Para, map(vec![
165 ("children", Dat::List(vec![
166 text("There is no repair, no quirks mode, and no best effort. The \
167 web gave that up in 1993 and spent thirty years paying for it."),
168 ])),
169 ])),
170 ])),
171 ])),
172
173 node(NodeKind::Code, map(vec![
174 ("lang", Dat::Str("jdat".to_string())),
175 ("text", Dat::Str(
176 "(box|{\n \"style\": \"callout\",\n \"children\": [...],\n})".to_string(),
177 )),
178 ])),
179
180 node(NodeKind::Image, map(vec![
181 ("hash", Dat::from([0x9fu8; 32])),
182 ("alt", Dat::Str("A diagram this reader has no copy of.".to_string())),
183 ])),
184
185 // Mixed direction, resolved by the bidi algorithm and reordered by rule L2.
186 node(NodeKind::Para, map(vec![
187 ("style", Dat::Str("centred".to_string())),
188 ("children", Dat::List(vec![
189 text("Arabic runs the other way: مرحبا بالعالم — and back to English."),
190 ])),
191 ])),
192 ])),
193 ]));
194 tree
195}
196
197fn main() -> Outcome<()> {
198 let args: Vec<String> = std::env::args().collect();
199 let path = match args.get(1) {
200 Some(p) => p.clone(),
201 None => return Err(err!(
202 "Give the path to write the document to: \
203 `cargo run -p sbj --example gen_demo -- <path>`.";
204 Invalid, Input, Missing)),
205 };
206
207 let signer = SignatureScheme::new_ed25519();
208 let bytes = res!(doc::write(&tree(), "oxeweb/doc/0", &signer, TIME));
209
210 // It must read back the way a reader would, or it is not a document, it is a file.
211 res!(doc::read(&bytes));
212
213 if let Some(dir) = std::path::Path::new(&path).parent() {
214 res!(std::fs::create_dir_all(dir));
215 }
216 res!(std::fs::write(&path, &bytes));
217 println!("Wrote {} ({} bytes), verified.", path, bytes.len());
218 Ok(())
219}