on a2873ae help sign in
95 files changed+291−13329
since splitsnapshots
unifiedsplit
dodrio/.gitignore−4 dodrio/.travis.yml−54 dodrio/Cargo.lock−1035 dodrio/Cargo.toml−105 dodrio/LICENSE−373 dodrio/README.md−108 dodrio/benches/benches.rs−118 dodrio/build.sh−11 dodrio/crates/js-api/Cargo.toml−22 dodrio/crates/js-api/src/lib.rs−324 dodrio/examples/counter/Cargo.toml−33 dodrio/examples/counter/README.md−21 dodrio/examples/counter/index.html−13 dodrio/examples/counter/src/lib.rs−87 dodrio/examples/game-of-life/Cargo.toml−29 dodrio/examples/game-of-life/README.md−23 dodrio/examples/game-of-life/index.html−44 dodrio/examples/game-of-life/src/lib.rs−218 dodrio/examples/hello-world/Cargo.toml−27 dodrio/examples/hello-world/README.md−21 dodrio/examples/hello-world/index.html−13 dodrio/examples/hello-world/src/lib.rs−42 dodrio/examples/input-form/Cargo.toml−32 dodrio/examples/input-form/README.md−22 dodrio/examples/input-form/index.html−13 dodrio/examples/input-form/src/lib.rs−81 dodrio/examples/js-component/Cargo.toml−26 dodrio/examples/js-component/README.md−23 dodrio/examples/js-component/greeting.js−41 dodrio/examples/js-component/index.html−14 dodrio/examples/js-component/src/lib.rs−63 dodrio/examples/moire/Cargo.toml−43 dodrio/examples/moire/README.md−26 dodrio/examples/moire/index.html−67 dodrio/examples/moire/src/colors.rs−162 dodrio/examples/moire/src/lib.rs−259 dodrio/examples/sierpinski-triangle/Cargo.toml−28 dodrio/examples/sierpinski-triangle/README.md−24 dodrio/examples/sierpinski-triangle/ferris.png dodrio/examples/sierpinski-triangle/index.html−69 dodrio/examples/sierpinski-triangle/src/lib.rs−157 dodrio/examples/todomvc/Cargo.toml−47 dodrio/examples/todomvc/README.md−33 dodrio/examples/todomvc/index.html−21 dodrio/examples/todomvc/src/controller.rs−140 dodrio/examples/todomvc/src/keys.rs−7 dodrio/examples/todomvc/src/lib.rs−60 dodrio/examples/todomvc/src/router.rs−68 dodrio/examples/todomvc/src/todo.rs−229 dodrio/examples/todomvc/src/todos.rs−300 dodrio/examples/todomvc/src/utils.rs−32 dodrio/examples/todomvc/src/visibility.rs−51 dodrio/js/change-list-interpreter.js−377 dodrio/js/strace.js−80 dodrio/publish.sh−11 dodrio/src/builder.rs−1075 dodrio/src/cached.rs−229 dodrio/src/cached_set.rs−186 dodrio/src/change_list/emitter.rs−292 dodrio/src/change_list/js.rs−40 dodrio/src/change_list/mod.rs−353 dodrio/src/change_list/strings.rs−66 dodrio/src/change_list/traversal.rs−344 dodrio/src/diff.rs−1125 dodrio/src/events.rs−138 dodrio/src/lib.rs−135 dodrio/src/logging.rs−65 dodrio/src/node.rs−272 dodrio/src/render.rs−168 dodrio/src/render_context.rs−104 dodrio/src/strace.rs−22 dodrio/src/vdom.rs−476 dodrio/tests/web/cached.js−11 dodrio/tests/web/cached.rs−289 dodrio/tests/web/events.rs−250 dodrio/tests/web/js_api.rs−153 dodrio/tests/web/keyed.js−25 dodrio/tests/web/keyed.rs−269 dodrio/tests/web/main.rs−226 dodrio/tests/web/render.rs−343 nock/Cargo.lock+5−141 nock/Cargo.toml−7 nock/bad.hoon−1 nock/hello.hoon+1−2 nock/hello.nock nock/src/interpreter.rs+54−228 nock/src/jets.rs+129−304 nock/src/lib.rs+1 nock/src/main.rs+43−69 nock/src/noun.rs−313 nock/test.hoon−3 nock/wock.hoon+13−201 wock-web/Cargo.toml+1−1 wock-web/src/lib.rs+44−72 wock.nock
dodrio/.gitignoredeleted−4
@@ -1,4 +0,0 @@
1−/target
2−**/*.rs.bk
3−wasm-pack.log
4−/.vscode
dodrio/.travis.ymldeleted−54
@@ -1,54 +0,0 @@
1−language: rust
2−sudo: false
3−
4−INSTALL_NODE_VIA_NVM: &INSTALL_NODE_VIA_NVM
5− |
6− rustup target add wasm32-unknown-unknown
7− curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
8− source ~/.nvm/nvm.sh
9− nvm install v10.5
10−
11−INSTALL_WASM_PPACK: &INSTALL_WASM_PACK
12− |
13− curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
14−
15−install:
16− - *INSTALL_NODE_VIA_NVM
17− - *INSTALL_WASM_PACK
18− - rustup target add wasm32-unknown-unknown
19−
20−matrix:
21− include:
22− - name: "build (stable)"
23− rust: stable
24− script: INSTALL=1 ./build.sh
25− - name: "build (beta)"
26− rust: beta
27− script: INSTALL=1 ./build.sh
28− - name: "headless browser tests (firefox)"
29− rust: stable
30− addons:
31− firefox: latest
32− script: wasm-pack test --firefox --headless -- --features xxx-unstable-internal-use-only
33− - name: "native tests"
34− rust: stable
35− install: echo "no install"
36− script: cargo test --all
37−
38− # TODO: A bug in `rustc` is making `js-sys` broken on nightly:
39− # https://github.com/rust-lang/rust/issues/69315
40− # - name: "build (nightly)"
41− # rust: nightly
42− # script: INSTALL=1 ./build.sh
43− # - name: "check native benches"
44− # rust: nightly
45− # install: echo "no install"
46− # script: cargo check --benches --features xxx-unstable-internal-use-only
47−
48− # TODO: `chromedriver` is complaining about ports or something. Temporarily
49− # disable in CI until we figure it out.
50− # - name: "headless browser tests (chrome)"
51− # rust: stable
52− # addons:
53− # chrome: stable
54− # script: wasm-pack test --chrome --headless -- --features xxx-unstable-internal-use-only
dodrio/Cargo.lockdeleted−1035
@@ -1,1035 +0,0 @@
1−# This file is automatically @generated by Cargo.
2−# It is not intended for manual editing.
3−[[package]]
4−name = "anyhow"
5−version = "1.0.25"
6−source = "registry+https://github.com/rust-lang/crates.io-index"
7−
8−[[package]]
9−name = "atty"
10−version = "0.2.13"
11−source = "registry+https://github.com/rust-lang/crates.io-index"
12−dependencies = [
13− "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
14− "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
15−]
16−
17−[[package]]
18−name = "autocfg"
19−version = "0.1.7"
20−source = "registry+https://github.com/rust-lang/crates.io-index"
21−
22−[[package]]
23−name = "bitflags"
24−version = "1.2.1"
25−source = "registry+https://github.com/rust-lang/crates.io-index"
26−
27−[[package]]
28−name = "bstr"
29−version = "0.2.8"
30−source = "registry+https://github.com/rust-lang/crates.io-index"
31−dependencies = [
32− "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
33− "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
34− "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
35− "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
36−]
37−
38−[[package]]
39−name = "bumpalo"
40−version = "2.6.0"
41−source = "registry+https://github.com/rust-lang/crates.io-index"
42−
43−[[package]]
44−name = "byteorder"
45−version = "1.3.2"
46−source = "registry+https://github.com/rust-lang/crates.io-index"
47−
48−[[package]]
49−name = "cast"
50−version = "0.2.3"
51−source = "registry+https://github.com/rust-lang/crates.io-index"
52−dependencies = [
53− "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
54−]
55−
56−[[package]]
57−name = "cfg-if"
58−version = "0.1.10"
59−source = "registry+https://github.com/rust-lang/crates.io-index"
60−
61−[[package]]
62−name = "clap"
63−version = "2.33.0"
64−source = "registry+https://github.com/rust-lang/crates.io-index"
65−dependencies = [
66− "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
67− "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
68− "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
69−]
70−
71−[[package]]
72−name = "console_error_panic_hook"
73−version = "0.1.6"
74−source = "registry+https://github.com/rust-lang/crates.io-index"
75−dependencies = [
76− "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
77− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
78−]
79−
80−[[package]]
81−name = "console_log"
82−version = "0.2.0"
83−source = "registry+https://github.com/rust-lang/crates.io-index"
84−dependencies = [
85− "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
86− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
87−]
88−
89−[[package]]
90−name = "criterion"
91−version = "0.3.3"
92−source = "registry+https://github.com/rust-lang/crates.io-index"
93−dependencies = [
94− "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
95− "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
96− "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
97− "criterion-plot 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
98− "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
99− "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
100− "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
101− "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
102− "oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
103− "plotters 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
104− "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
105− "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
106− "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
107− "serde_cbor 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
108− "serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
109− "serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)",
110− "tinytemplate 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
111− "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
112−]
113−
114−[[package]]
115−name = "criterion-plot"
116−version = "0.4.3"
117−source = "registry+https://github.com/rust-lang/crates.io-index"
118−dependencies = [
119− "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
120− "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
121−]
122−
123−[[package]]
124−name = "crossbeam-deque"
125−version = "0.7.2"
126−source = "registry+https://github.com/rust-lang/crates.io-index"
127−dependencies = [
128− "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
129− "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
130−]
131−
132−[[package]]
133−name = "crossbeam-epoch"
134−version = "0.8.0"
135−source = "registry+https://github.com/rust-lang/crates.io-index"
136−dependencies = [
137− "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
138− "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
139− "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
140− "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
141− "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
142− "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
143−]
144−
145−[[package]]
146−name = "crossbeam-queue"
147−version = "0.2.0"
148−source = "registry+https://github.com/rust-lang/crates.io-index"
149−dependencies = [
150− "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
151−]
152−
153−[[package]]
154−name = "crossbeam-utils"
155−version = "0.7.0"
156−source = "registry+https://github.com/rust-lang/crates.io-index"
157−dependencies = [
158− "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
159− "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
160− "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
161−]
162−
163−[[package]]
164−name = "csv"
165−version = "1.1.1"
166−source = "registry+https://github.com/rust-lang/crates.io-index"
167−dependencies = [
168− "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
169− "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
170− "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
171− "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
172− "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
173−]
174−
175−[[package]]
176−name = "csv-core"
177−version = "0.1.6"
178−source = "registry+https://github.com/rust-lang/crates.io-index"
179−dependencies = [
180− "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
181−]
182−
183−[[package]]
184−name = "dodrio"
185−version = "0.2.0"
186−dependencies = [
187− "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
188− "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
189− "console_log 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
190− "criterion 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
191− "dodrio-js-api 0.2.0",
192− "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
193− "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
194− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
195− "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
196− "longest-increasing-subsequence 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
197− "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
198− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
199− "wasm-bindgen-futures 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
200− "wasm-bindgen-test 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
201− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
202−]
203−
204−[[package]]
205−name = "dodrio-counter"
206−version = "0.1.0"
207−dependencies = [
208− "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
209− "console_log 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
210− "dodrio 0.2.0",
211− "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
212− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
213− "wasm-bindgen-test 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
214− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
215−]
216−
217−[[package]]
218−name = "dodrio-game-of-life"
219−version = "0.1.0"
220−dependencies = [
221− "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
222− "dodrio 0.2.0",
223− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
224− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
225− "wasm-bindgen-futures 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
226− "wasm-bindgen-test 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
227− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
228−]
229−
230−[[package]]
231−name = "dodrio-hello-world"
232−version = "0.1.0"
233−dependencies = [
234− "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
235− "dodrio 0.2.0",
236− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
237− "wasm-bindgen-test 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
238− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
239−]
240−
241−[[package]]
242−name = "dodrio-input-form"
243−version = "0.1.0"
244−dependencies = [
245− "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
246− "dodrio 0.2.0",
247− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
248− "wasm-bindgen-test 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
249− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
250−]
251−
252−[[package]]
253−name = "dodrio-js-api"
254−version = "0.2.0"
255−dependencies = [
256− "dodrio 0.2.0",
257− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
258− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
259− "wasm-bindgen-futures 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
260− "wasm-bindgen-test 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
261−]
262−
263−[[package]]
264−name = "dodrio-js-component"
265−version = "0.1.0"
266−dependencies = [
267− "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
268− "dodrio 0.2.0",
269− "dodrio-js-api 0.2.0",
270− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
271− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
272− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
273−]
274−
275−[[package]]
276−name = "dodrio-moire"
277−version = "0.1.0"
278−dependencies = [
279− "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
280− "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
281− "console_log 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
282− "dodrio 0.2.0",
283− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
284− "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
285− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
286− "wasm-bindgen-futures 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
287− "wasm-bindgen-test 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
288− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
289−]
290−
291−[[package]]
292−name = "dodrio-sierpinski"
293−version = "0.1.0"
294−dependencies = [
295− "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
296− "dodrio 0.2.0",
297− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
298− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
299− "wasm-bindgen-futures 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
300− "wasm-bindgen-test 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
301− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
302−]
303−
304−[[package]]
305−name = "dodrio-todomvc"
306−version = "0.1.0"
307−dependencies = [
308− "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
309− "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
310− "console_log 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
311− "dodrio 0.2.0",
312− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
313− "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
314− "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
315− "serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)",
316− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
317− "wasm-bindgen-futures 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
318− "wasm-bindgen-test 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
319− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
320−]
321−
322−[[package]]
323−name = "either"
324−version = "1.5.3"
325−source = "registry+https://github.com/rust-lang/crates.io-index"
326−
327−[[package]]
328−name = "futures"
329−version = "0.3.5"
330−source = "registry+https://github.com/rust-lang/crates.io-index"
331−dependencies = [
332− "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
333− "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
334− "futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
335− "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
336− "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
337− "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
338− "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
339−]
340−
341−[[package]]
342−name = "futures-channel"
343−version = "0.3.5"
344−source = "registry+https://github.com/rust-lang/crates.io-index"
345−dependencies = [
346− "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
347− "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
348−]
349−
350−[[package]]
351−name = "futures-core"
352−version = "0.3.5"
353−source = "registry+https://github.com/rust-lang/crates.io-index"
354−
355−[[package]]
356−name = "futures-executor"
357−version = "0.3.5"
358−source = "registry+https://github.com/rust-lang/crates.io-index"
359−dependencies = [
360− "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
361− "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
362− "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
363−]
364−
365−[[package]]
366−name = "futures-io"
367−version = "0.3.5"
368−source = "registry+https://github.com/rust-lang/crates.io-index"
369−
370−[[package]]
371−name = "futures-macro"
372−version = "0.3.5"
373−source = "registry+https://github.com/rust-lang/crates.io-index"
374−dependencies = [
375− "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
376− "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
377− "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
378− "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
379−]
380−
381−[[package]]
382−name = "futures-sink"
383−version = "0.3.5"
384−source = "registry+https://github.com/rust-lang/crates.io-index"
385−
386−[[package]]
387−name = "futures-task"
388−version = "0.3.5"
389−source = "registry+https://github.com/rust-lang/crates.io-index"
390−dependencies = [
391− "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
392−]
393−
394−[[package]]
395−name = "futures-util"
396−version = "0.3.5"
397−source = "registry+https://github.com/rust-lang/crates.io-index"
398−dependencies = [
399− "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
400− "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
401− "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
402− "futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
403− "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
404− "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
405− "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
406− "pin-project 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)",
407− "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
408− "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
409− "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
410− "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
411−]
412−
413−[[package]]
414−name = "fxhash"
415−version = "0.2.1"
416−source = "registry+https://github.com/rust-lang/crates.io-index"
417−dependencies = [
418− "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
419−]
420−
421−[[package]]
422−name = "half"
423−version = "1.6.0"
424−source = "registry+https://github.com/rust-lang/crates.io-index"
425−
426−[[package]]
427−name = "heck"
428−version = "0.3.1"
429−source = "registry+https://github.com/rust-lang/crates.io-index"
430−dependencies = [
431− "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
432−]
433−
434−[[package]]
435−name = "hermit-abi"
436−version = "0.1.3"
437−source = "registry+https://github.com/rust-lang/crates.io-index"
438−dependencies = [
439− "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
440−]
441−
442−[[package]]
443−name = "itertools"
444−version = "0.9.0"
445−source = "registry+https://github.com/rust-lang/crates.io-index"
446−dependencies = [
447− "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
448−]
449−
450−[[package]]
451−name = "itoa"
452−version = "0.4.4"
453−source = "registry+https://github.com/rust-lang/crates.io-index"
454−
455−[[package]]
456−name = "js-sys"
457−version = "0.3.32"
458−source = "registry+https://github.com/rust-lang/crates.io-index"
459−dependencies = [
460− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
461−]
462−
463−[[package]]
464−name = "lazy_static"
465−version = "1.4.0"
466−source = "registry+https://github.com/rust-lang/crates.io-index"
467−
468−[[package]]
469−name = "libc"
470−version = "0.2.66"
471−source = "registry+https://github.com/rust-lang/crates.io-index"
472−
473−[[package]]
474−name = "log"
475−version = "0.4.11"
476−source = "registry+https://github.com/rust-lang/crates.io-index"
477−dependencies = [
478− "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
479−]
480−
481−[[package]]
482−name = "longest-increasing-subsequence"
483−version = "0.1.0"
484−source = "registry+https://github.com/rust-lang/crates.io-index"
485−
486−[[package]]
487−name = "memchr"
488−version = "2.2.1"
489−source = "registry+https://github.com/rust-lang/crates.io-index"
490−dependencies = [
491− "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
492−]
493−
494−[[package]]
495−name = "memoffset"
496−version = "0.5.3"
497−source = "registry+https://github.com/rust-lang/crates.io-index"
498−dependencies = [
499− "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
500−]
501−
502−[[package]]
503−name = "nom"
504−version = "4.2.3"
505−source = "registry+https://github.com/rust-lang/crates.io-index"
506−dependencies = [
507− "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
508− "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
509−]
510−
511−[[package]]
512−name = "num-traits"
513−version = "0.2.10"
514−source = "registry+https://github.com/rust-lang/crates.io-index"
515−dependencies = [
516− "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
517−]
518−
519−[[package]]
520−name = "num_cpus"
521−version = "1.11.1"
522−source = "registry+https://github.com/rust-lang/crates.io-index"
523−dependencies = [
524− "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
525− "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
526−]
527−
528−[[package]]
529−name = "once_cell"
530−version = "1.3.1"
531−source = "registry+https://github.com/rust-lang/crates.io-index"
532−
533−[[package]]
534−name = "oorandom"
535−version = "11.1.0"
536−source = "registry+https://github.com/rust-lang/crates.io-index"
537−
538−[[package]]
539−name = "pin-project"
540−version = "0.4.16"
541−source = "registry+https://github.com/rust-lang/crates.io-index"
542−dependencies = [
543− "pin-project-internal 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)",
544−]
545−
546−[[package]]
547−name = "pin-project-internal"
548−version = "0.4.16"
549−source = "registry+https://github.com/rust-lang/crates.io-index"
550−dependencies = [
551− "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
552− "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
553− "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
554−]
555−
556−[[package]]
557−name = "pin-utils"
558−version = "0.1.0"
559−source = "registry+https://github.com/rust-lang/crates.io-index"
560−
561−[[package]]
562−name = "plotters"
563−version = "0.2.12"
564−source = "registry+https://github.com/rust-lang/crates.io-index"
565−dependencies = [
566− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
567− "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
568− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
569− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
570−]
571−
572−[[package]]
573−name = "proc-macro-hack"
574−version = "0.5.11"
575−source = "registry+https://github.com/rust-lang/crates.io-index"
576−dependencies = [
577− "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
578− "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
579− "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
580−]
581−
582−[[package]]
583−name = "proc-macro-nested"
584−version = "0.1.3"
585−source = "registry+https://github.com/rust-lang/crates.io-index"
586−
587−[[package]]
588−name = "proc-macro2"
589−version = "1.0.18"
590−source = "registry+https://github.com/rust-lang/crates.io-index"
591−dependencies = [
592− "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
593−]
594−
595−[[package]]
596−name = "quote"
597−version = "1.0.2"
598−source = "registry+https://github.com/rust-lang/crates.io-index"
599−dependencies = [
600− "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
601−]
602−
603−[[package]]
604−name = "rayon"
605−version = "1.3.0"
606−source = "registry+https://github.com/rust-lang/crates.io-index"
607−dependencies = [
608− "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
609− "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
610− "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
611−]
612−
613−[[package]]
614−name = "rayon-core"
615−version = "1.7.0"
616−source = "registry+https://github.com/rust-lang/crates.io-index"
617−dependencies = [
618− "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
619− "crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
620− "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
621− "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
622− "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
623−]
624−
625−[[package]]
626−name = "regex"
627−version = "1.3.3"
628−source = "registry+https://github.com/rust-lang/crates.io-index"
629−dependencies = [
630− "regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
631−]
632−
633−[[package]]
634−name = "regex-automata"
635−version = "0.1.8"
636−source = "registry+https://github.com/rust-lang/crates.io-index"
637−dependencies = [
638− "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
639−]
640−
641−[[package]]
642−name = "regex-syntax"
643−version = "0.6.13"
644−source = "registry+https://github.com/rust-lang/crates.io-index"
645−
646−[[package]]
647−name = "rustc_version"
648−version = "0.2.3"
649−source = "registry+https://github.com/rust-lang/crates.io-index"
650−dependencies = [
651− "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
652−]
653−
654−[[package]]
655−name = "ryu"
656−version = "1.0.2"
657−source = "registry+https://github.com/rust-lang/crates.io-index"
658−
659−[[package]]
660−name = "same-file"
661−version = "1.0.5"
662−source = "registry+https://github.com/rust-lang/crates.io-index"
663−dependencies = [
664− "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
665−]
666−
667−[[package]]
668−name = "scoped-tls"
669−version = "1.0.0"
670−source = "registry+https://github.com/rust-lang/crates.io-index"
671−
672−[[package]]
673−name = "scopeguard"
674−version = "1.0.0"
675−source = "registry+https://github.com/rust-lang/crates.io-index"
676−
677−[[package]]
678−name = "semver"
679−version = "0.9.0"
680−source = "registry+https://github.com/rust-lang/crates.io-index"
681−dependencies = [
682− "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
683−]
684−
685−[[package]]
686−name = "semver-parser"
687−version = "0.7.0"
688−source = "registry+https://github.com/rust-lang/crates.io-index"
689−
690−[[package]]
691−name = "serde"
692−version = "1.0.114"
693−source = "registry+https://github.com/rust-lang/crates.io-index"
694−dependencies = [
695− "serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
696−]
697−
698−[[package]]
699−name = "serde_cbor"
700−version = "0.11.1"
701−source = "registry+https://github.com/rust-lang/crates.io-index"
702−dependencies = [
703− "half 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
704− "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
705−]
706−
707−[[package]]
708−name = "serde_derive"
709−version = "1.0.114"
710−source = "registry+https://github.com/rust-lang/crates.io-index"
711−dependencies = [
712− "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
713− "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
714− "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
715−]
716−
717−[[package]]
718−name = "serde_json"
719−version = "1.0.57"
720−source = "registry+https://github.com/rust-lang/crates.io-index"
721−dependencies = [
722− "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
723− "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
724− "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
725−]
726−
727−[[package]]
728−name = "slab"
729−version = "0.4.2"
730−source = "registry+https://github.com/rust-lang/crates.io-index"
731−
732−[[package]]
733−name = "sourcefile"
734−version = "0.1.4"
735−source = "registry+https://github.com/rust-lang/crates.io-index"
736−
737−[[package]]
738−name = "syn"
739−version = "1.0.33"
740−source = "registry+https://github.com/rust-lang/crates.io-index"
741−dependencies = [
742− "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
743− "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
744− "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
745−]
746−
747−[[package]]
748−name = "textwrap"
749−version = "0.11.0"
750−source = "registry+https://github.com/rust-lang/crates.io-index"
751−dependencies = [
752− "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
753−]
754−
755−[[package]]
756−name = "tinytemplate"
757−version = "1.1.0"
758−source = "registry+https://github.com/rust-lang/crates.io-index"
759−dependencies = [
760− "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
761− "serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)",
762−]
763−
764−[[package]]
765−name = "unicode-segmentation"
766−version = "1.6.0"
767−source = "registry+https://github.com/rust-lang/crates.io-index"
768−
769−[[package]]
770−name = "unicode-width"
771−version = "0.1.6"
772−source = "registry+https://github.com/rust-lang/crates.io-index"
773−
774−[[package]]
775−name = "unicode-xid"
776−version = "0.2.0"
777−source = "registry+https://github.com/rust-lang/crates.io-index"
778−
779−[[package]]
780−name = "version_check"
781−version = "0.1.5"
782−source = "registry+https://github.com/rust-lang/crates.io-index"
783−
784−[[package]]
785−name = "walkdir"
786−version = "2.3.1"
787−source = "registry+https://github.com/rust-lang/crates.io-index"
788−dependencies = [
789− "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
790− "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
791− "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
792−]
793−
794−[[package]]
795−name = "wasm-bindgen"
796−version = "0.2.55"
797−source = "registry+https://github.com/rust-lang/crates.io-index"
798−dependencies = [
799− "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
800− "wasm-bindgen-macro 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
801−]
802−
803−[[package]]
804−name = "wasm-bindgen-backend"
805−version = "0.2.55"
806−source = "registry+https://github.com/rust-lang/crates.io-index"
807−dependencies = [
808− "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
809− "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
810− "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
811− "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
812− "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
813− "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
814− "wasm-bindgen-shared 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
815−]
816−
817−[[package]]
818−name = "wasm-bindgen-futures"
819−version = "0.4.5"
820−source = "registry+https://github.com/rust-lang/crates.io-index"
821−dependencies = [
822− "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
823− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
824− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
825− "web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
826−]
827−
828−[[package]]
829−name = "wasm-bindgen-macro"
830−version = "0.2.55"
831−source = "registry+https://github.com/rust-lang/crates.io-index"
832−dependencies = [
833− "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
834− "wasm-bindgen-macro-support 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
835−]
836−
837−[[package]]
838−name = "wasm-bindgen-macro-support"
839−version = "0.2.55"
840−source = "registry+https://github.com/rust-lang/crates.io-index"
841−dependencies = [
842− "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
843− "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
844− "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
845− "wasm-bindgen-backend 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
846− "wasm-bindgen-shared 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
847−]
848−
849−[[package]]
850−name = "wasm-bindgen-shared"
851−version = "0.2.55"
852−source = "registry+https://github.com/rust-lang/crates.io-index"
853−
854−[[package]]
855−name = "wasm-bindgen-test"
856−version = "0.3.5"
857−source = "registry+https://github.com/rust-lang/crates.io-index"
858−dependencies = [
859− "console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
860− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
861− "scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
862− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
863− "wasm-bindgen-futures 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
864− "wasm-bindgen-test-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
865−]
866−
867−[[package]]
868−name = "wasm-bindgen-test-macro"
869−version = "0.3.5"
870−source = "registry+https://github.com/rust-lang/crates.io-index"
871−dependencies = [
872− "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
873− "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
874−]
875−
876−[[package]]
877−name = "wasm-bindgen-webidl"
878−version = "0.2.55"
879−source = "registry+https://github.com/rust-lang/crates.io-index"
880−dependencies = [
881− "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
882− "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
883− "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
884− "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
885− "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
886− "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
887− "wasm-bindgen-backend 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
888− "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
889−]
890−
891−[[package]]
892−name = "web-sys"
893−version = "0.3.32"
894−source = "registry+https://github.com/rust-lang/crates.io-index"
895−dependencies = [
896− "anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
897− "js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
898− "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
899− "wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
900− "wasm-bindgen-webidl 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
901−]
902−
903−[[package]]
904−name = "weedle"
905−version = "0.10.0"
906−source = "registry+https://github.com/rust-lang/crates.io-index"
907−dependencies = [
908− "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
909−]
910−
911−[[package]]
912−name = "winapi"
913−version = "0.3.8"
914−source = "registry+https://github.com/rust-lang/crates.io-index"
915−dependencies = [
916− "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
917− "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
918−]
919−
920−[[package]]
921−name = "winapi-i686-pc-windows-gnu"
922−version = "0.4.0"
923−source = "registry+https://github.com/rust-lang/crates.io-index"
924−
925−[[package]]
926−name = "winapi-util"
927−version = "0.1.2"
928−source = "registry+https://github.com/rust-lang/crates.io-index"
929−dependencies = [
930− "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
931−]
932−
933−[[package]]
934−name = "winapi-x86_64-pc-windows-gnu"
935−version = "0.4.0"
936−source = "registry+https://github.com/rust-lang/crates.io-index"
937−
938−[metadata]
939−"checksum anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14"
940−"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
941−"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
942−"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
943−"checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245"
944−"checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708"
945−"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
946−"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
947−"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
948−"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
949−"checksum console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
950−"checksum console_log 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494"
951−"checksum criterion 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "70daa7ceec6cf143990669a04c7df13391d55fb27bd4079d252fca774ba244d8"
952−"checksum criterion-plot 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d"
953−"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca"
954−"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
955−"checksum crossbeam-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfd6515864a82d2f877b42813d4553292c6659498c9a2aa31bab5a15243c2700"
956−"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
957−"checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d"
958−"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c"
959−"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
960−"checksum futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
961−"checksum futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
962−"checksum futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
963−"checksum futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314"
964−"checksum futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
965−"checksum futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
966−"checksum futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
967−"checksum futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
968−"checksum futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
969−"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
970−"checksum half 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
971−"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
972−"checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120"
973−"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
974−"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
975−"checksum js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "1c840fdb2167497b0bd0db43d6dfe61e91637fa72f9d061f8bd17ddc44ba6414"
976−"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
977−"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
978−"checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
979−"checksum longest-increasing-subsequence 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86"
980−"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
981−"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
982−"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
983−"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
984−"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72"
985−"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"
986−"checksum oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405"
987−"checksum pin-project 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)" = "81d480cb4e89522ccda96d0eed9af94180b7a5f93fb28f66e1fd7d68431663d1"
988−"checksum pin-project-internal 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)" = "a82996f11efccb19b685b14b5df818de31c1edcee3daa256ab5775dd98e72feb"
989−"checksum pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
990−"checksum plotters 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "4e3bb8da247d27ae212529352020f3e5ee16e83c0c258061d27b08ab92675eeb"
991−"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
992−"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e"
993−"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
994−"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
995−"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098"
996−"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9"
997−"checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87"
998−"checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9"
999−"checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90"
1000−"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
1001−"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
1002−"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"
1003−"checksum scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
1004−"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
1005−"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
1006−"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
1007−"checksum serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
1008−"checksum serde_cbor 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622"
1009−"checksum serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
1010−"checksum serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)" = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
1011−"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
1012−"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3"
1013−"checksum syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd"
1014−"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
1015−"checksum tinytemplate 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f"
1016−"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
1017−"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
1018−"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
1019−"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
1020−"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
1021−"checksum wasm-bindgen 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "29ae32af33bacd663a9a28241abecf01f2be64e6a185c6139b04f18b6385c5f2"
1022−"checksum wasm-bindgen-backend 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "1845584bd3593442dc0de6e6d9f84454a59a057722f36f005e44665d6ab19d85"
1023−"checksum wasm-bindgen-futures 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1458706aa1b8fe6898d19433c9f110d93a05d1f22ae6adf55810409a94df34b4"
1024−"checksum wasm-bindgen-macro 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "87fcc747e6b73c93d22c947a6334644d22cfec5abd8b66238484dc2b0aeb9fe4"
1025−"checksum wasm-bindgen-macro-support 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "3dc4b3f2c4078c8c4a5f363b92fcf62604c5913cbd16c6ff5aaf0f74ec03f570"
1026−"checksum wasm-bindgen-shared 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ca0b78d6d3be8589b95d1d49cdc0794728ca734adf36d7c9f07e6459508bb53d"
1027−"checksum wasm-bindgen-test 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ed5833682054e941db0b7e2b4877b61bbaafd680608e40d268e070d34558b305"
1028−"checksum wasm-bindgen-test-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9c5dcf70d21d0ff0ea5cdb97dba9a9b56ddad4a9bd10c8912eedc55618be022f"
1029−"checksum wasm-bindgen-webidl 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "3126356474ceb717c8fb5549ae387c9fbf4872818454f4d87708bee997214bb5"
1030−"checksum web-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "98405c0a2e722ed3db341b4c5b70eb9fe0021621f7350bab76df93b09b649bbf"
1031−"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164"
1032−"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
1033−"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1034−"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
1035−"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
dodrio/Cargo.tomldeleted−105
@@ -1,105 +0,0 @@
1−[package]
2−authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
3−categories = ["wasm", "web-programming"]
4−description = "A fast, bump-allocated virtual DOM library."
5−edition = "2018"
6−keywords = ["virtual-dom", "vdom", "render", "bump"]
7−license = "MPL-2.0"
8−name = "dodrio"
9−readme = "./README.md"
10−repository = "https://github.com/fitzgen/dodrio"
11−version = "0.2.0"
12−
13−[badges]
14−travis-ci = { repository = "fitzgen/dodrio" }
15−
16−[lib]
17−path = "src/lib.rs"
18−bench = false
19−
20−[[test]]
21−name = "web"
22−path = "tests/web/main.rs"
23−required-features = ["xxx-unstable-internal-use-only"]
24−
25−[[bench]]
26−name = "benches"
27−path = "benches/benches.rs"
28−harness = false
29−required-features = ["xxx-unstable-internal-use-only"]
30−
31−[features]
32−# Don't rely on this feature! Its behavior will change and it could go away at
33−# any time. This enables monkey-patching DOM methods so that we wrap them and
34−# trace what DOM methods are used and how many times they are invoked when
35−# applying our emitted change lists to the physical DOM. This is useful for
36−# evaluating the differences between how Dodrio applies changes from virtual
37−# nodes to the physical DOM versus how other virtual DOM libraries do the same.
38−xxx-unstable-strace = []
39−
40−# Don't use this feature! It only exists for exposing otherwise-internal APIs
41−# for testing and benchmarks.
42−xxx-unstable-internal-use-only = []
43−
44−[dependencies]
45−bumpalo = "2.6.0"
46−cfg-if = "0.1.10"
47−fxhash = "0.2.1"
48−js-sys = "0.3.32"
49−log = { version = "0.4.11", optional = true }
50−longest-increasing-subsequence = "0.1.0"
51−serde = { version = "1.0.114", optional = true }
52−wasm-bindgen = "0.2.55"
53−wasm-bindgen-futures = "0.4.5"
54−
55−[dependencies.web-sys]
56−version = "0.3.32"
57−features = [
58− "console",
59− "Document",
60− "Element",
61− "Event",
62− "Node",
63− "Window",
64−]
65−
66−[dev-dependencies]
67−console_log = "0.2.0"
68−dodrio-js-api = { version = "=0.2.0", path = "./crates/js-api" }
69−futures = "0.3.5"
70−log = "0.4.11"
71−wasm-bindgen-test = "0.3.5"
72−
73−[dev-dependencies.web-sys]
74−version = "0.3.32"
75−features = [
76− "Attr",
77− "EventTarget",
78− "HtmlElement",
79− "NamedNodeMap",
80− "NodeList",
81−]
82−
83−[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
84−criterion = "0.3.3"
85−
86−[profile.bench]
87−debug = true
88−
89−[profile.release]
90−incremental = false
91−lto = true
92−opt-level = "s"
93−
94−[workspace]
95−members = [
96− "./crates/js-api",
97− "./examples/counter",
98− "./examples/game-of-life",
99− "./examples/hello-world",
100− "./examples/input-form",
101− "./examples/js-component",
102− "./examples/moire",
103− "./examples/sierpinski-triangle",
104− "./examples/todomvc",
105−]
dodrio/LICENSEdeleted−373
@@ -1,373 +0,0 @@
1−Mozilla Public License Version 2.0
2−==================================
3−
4−1. Definitions
5−--------------
6−
7−1.1. "Contributor"
8− means each individual or legal entity that creates, contributes to
9− the creation of, or owns Covered Software.
10−
11−1.2. "Contributor Version"
12− means the combination of the Contributions of others (if any) used
13− by a Contributor and that particular Contributor's Contribution.
14−
15−1.3. "Contribution"
16− means Covered Software of a particular Contributor.
17−
18−1.4. "Covered Software"
19− means Source Code Form to which the initial Contributor has attached
20− the notice in Exhibit A, the Executable Form of such Source Code
21− Form, and Modifications of such Source Code Form, in each case
22− including portions thereof.
23−
24−1.5. "Incompatible With Secondary Licenses"
25− means
26−
27− (a) that the initial Contributor has attached the notice described
28− in Exhibit B to the Covered Software; or
29−
30− (b) that the Covered Software was made available under the terms of
31− version 1.1 or earlier of the License, but not also under the
32− terms of a Secondary License.
33−
34−1.6. "Executable Form"
35− means any form of the work other than Source Code Form.
36−
37−1.7. "Larger Work"
38− means a work that combines Covered Software with other material, in
39− a separate file or files, that is not Covered Software.
40−
41−1.8. "License"
42− means this document.
43−
44−1.9. "Licensable"
45− means having the right to grant, to the maximum extent possible,
46− whether at the time of the initial grant or subsequently, any and
47− all of the rights conveyed by this License.
48−
49−1.10. "Modifications"
50− means any of the following:
51−
52− (a) any file in Source Code Form that results from an addition to,
53− deletion from, or modification of the contents of Covered
54− Software; or
55−
56− (b) any new file in Source Code Form that contains any Covered
57− Software.
58−
59−1.11. "Patent Claims" of a Contributor
60− means any patent claim(s), including without limitation, method,
61− process, and apparatus claims, in any patent Licensable by such
62− Contributor that would be infringed, but for the grant of the
63− License, by the making, using, selling, offering for sale, having
64− made, import, or transfer of either its Contributions or its
65− Contributor Version.
66−
67−1.12. "Secondary License"
68− means either the GNU General Public License, Version 2.0, the GNU
69− Lesser General Public License, Version 2.1, the GNU Affero General
70− Public License, Version 3.0, or any later versions of those
71− licenses.
72−
73−1.13. "Source Code Form"
74− means the form of the work preferred for making modifications.
75−
76−1.14. "You" (or "Your")
77− means an individual or a legal entity exercising rights under this
78− License. For legal entities, "You" includes any entity that
79− controls, is controlled by, or is under common control with You. For
80− purposes of this definition, "control" means (a) the power, direct
81− or indirect, to cause the direction or management of such entity,
82− whether by contract or otherwise, or (b) ownership of more than
83− fifty percent (50%) of the outstanding shares or beneficial
84− ownership of such entity.
85−
86−2. License Grants and Conditions
87−--------------------------------
88−
89−2.1. Grants
90−
91−Each Contributor hereby grants You a world-wide, royalty-free,
92−non-exclusive license:
93−
94−(a) under intellectual property rights (other than patent or trademark)
95− Licensable by such Contributor to use, reproduce, make available,
96− modify, display, perform, distribute, and otherwise exploit its
97− Contributions, either on an unmodified basis, with Modifications, or
98− as part of a Larger Work; and
99−
100−(b) under Patent Claims of such Contributor to make, use, sell, offer
101− for sale, have made, import, and otherwise transfer either its
102− Contributions or its Contributor Version.
103−
104−2.2. Effective Date
105−
106−The licenses granted in Section 2.1 with respect to any Contribution
107−become effective for each Contribution on the date the Contributor first
108−distributes such Contribution.
109−
110−2.3. Limitations on Grant Scope
111−
112−The licenses granted in this Section 2 are the only rights granted under
113−this License. No additional rights or licenses will be implied from the
114−distribution or licensing of Covered Software under this License.
115−Notwithstanding Section 2.1(b) above, no patent license is granted by a
116−Contributor:
117−
118−(a) for any code that a Contributor has removed from Covered Software;
119− or
120−
121−(b) for infringements caused by: (i) Your and any other third party's
122− modifications of Covered Software, or (ii) the combination of its
123− Contributions with other software (except as part of its Contributor
124− Version); or
125−
126−(c) under Patent Claims infringed by Covered Software in the absence of
127− its Contributions.
128−
129−This License does not grant any rights in the trademarks, service marks,
130−or logos of any Contributor (except as may be necessary to comply with
131−the notice requirements in Section 3.4).
132−
133−2.4. Subsequent Licenses
134−
135−No Contributor makes additional grants as a result of Your choice to
136−distribute the Covered Software under a subsequent version of this
137−License (see Section 10.2) or under the terms of a Secondary License (if
138−permitted under the terms of Section 3.3).
139−
140−2.5. Representation
141−
142−Each Contributor represents that the Contributor believes its
143−Contributions are its original creation(s) or it has sufficient rights
144−to grant the rights to its Contributions conveyed by this License.
145−
146−2.6. Fair Use
147−
148−This License is not intended to limit any rights You have under
149−applicable copyright doctrines of fair use, fair dealing, or other
150−equivalents.
151−
152−2.7. Conditions
153−
154−Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155−in Section 2.1.
156−
157−3. Responsibilities
158−-------------------
159−
160−3.1. Distribution of Source Form
161−
162−All distribution of Covered Software in Source Code Form, including any
163−Modifications that You create or to which You contribute, must be under
164−the terms of this License. You must inform recipients that the Source
165−Code Form of the Covered Software is governed by the terms of this
166−License, and how they can obtain a copy of this License. You may not
167−attempt to alter or restrict the recipients' rights in the Source Code
168−Form.
169−
170−3.2. Distribution of Executable Form
171−
172−If You distribute Covered Software in Executable Form then:
173−
174−(a) such Covered Software must also be made available in Source Code
175− Form, as described in Section 3.1, and You must inform recipients of
176− the Executable Form how they can obtain a copy of such Source Code
177− Form by reasonable means in a timely manner, at a charge no more
178− than the cost of distribution to the recipient; and
179−
180−(b) You may distribute such Executable Form under the terms of this
181− License, or sublicense it under different terms, provided that the
182− license for the Executable Form does not attempt to limit or alter
183− the recipients' rights in the Source Code Form under this License.
184−
185−3.3. Distribution of a Larger Work
186−
187−You may create and distribute a Larger Work under terms of Your choice,
188−provided that You also comply with the requirements of this License for
189−the Covered Software. If the Larger Work is a combination of Covered
190−Software with a work governed by one or more Secondary Licenses, and the
191−Covered Software is not Incompatible With Secondary Licenses, this
192−License permits You to additionally distribute such Covered Software
193−under the terms of such Secondary License(s), so that the recipient of
194−the Larger Work may, at their option, further distribute the Covered
195−Software under the terms of either this License or such Secondary
196−License(s).
197−
198−3.4. Notices
199−
200−You may not remove or alter the substance of any license notices
201−(including copyright notices, patent notices, disclaimers of warranty,
202−or limitations of liability) contained within the Source Code Form of
203−the Covered Software, except that You may alter any license notices to
204−the extent required to remedy known factual inaccuracies.
205−
206−3.5. Application of Additional Terms
207−
208−You may choose to offer, and to charge a fee for, warranty, support,
209−indemnity or liability obligations to one or more recipients of Covered
210−Software. However, You may do so only on Your own behalf, and not on
211−behalf of any Contributor. You must make it absolutely clear that any
212−such warranty, support, indemnity, or liability obligation is offered by
213−You alone, and You hereby agree to indemnify every Contributor for any
214−liability incurred by such Contributor as a result of warranty, support,
215−indemnity or liability terms You offer. You may include additional
216−disclaimers of warranty and limitations of liability specific to any
217−jurisdiction.
218−
219−4. Inability to Comply Due to Statute or Regulation
220−---------------------------------------------------
221−
222−If it is impossible for You to comply with any of the terms of this
223−License with respect to some or all of the Covered Software due to
224−statute, judicial order, or regulation then You must: (a) comply with
225−the terms of this License to the maximum extent possible; and (b)
226−describe the limitations and the code they affect. Such description must
227−be placed in a text file included with all distributions of the Covered
228−Software under this License. Except to the extent prohibited by statute
229−or regulation, such description must be sufficiently detailed for a
230−recipient of ordinary skill to be able to understand it.
231−
232−5. Termination
233−--------------
234−
235−5.1. The rights granted under this License will terminate automatically
236−if You fail to comply with any of its terms. However, if You become
237−compliant, then the rights granted under this License from a particular
238−Contributor are reinstated (a) provisionally, unless and until such
239−Contributor explicitly and finally terminates Your grants, and (b) on an
240−ongoing basis, if such Contributor fails to notify You of the
241−non-compliance by some reasonable means prior to 60 days after You have
242−come back into compliance. Moreover, Your grants from a particular
243−Contributor are reinstated on an ongoing basis if such Contributor
244−notifies You of the non-compliance by some reasonable means, this is the
245−first time You have received notice of non-compliance with this License
246−from such Contributor, and You become compliant prior to 30 days after
247−Your receipt of the notice.
248−
249−5.2. If You initiate litigation against any entity by asserting a patent
250−infringement claim (excluding declaratory judgment actions,
251−counter-claims, and cross-claims) alleging that a Contributor Version
252−directly or indirectly infringes any patent, then the rights granted to
253−You by any and all Contributors for the Covered Software under Section
254−2.1 of this License shall terminate.
255−
256−5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257−end user license agreements (excluding distributors and resellers) which
258−have been validly granted by You or Your distributors under this License
259−prior to termination shall survive termination.
260−
261−************************************************************************
262−* *
263−* 6. Disclaimer of Warranty *
264−* ------------------------- *
265−* *
266−* Covered Software is provided under this License on an "as is" *
267−* basis, without warranty of any kind, either expressed, implied, or *
268−* statutory, including, without limitation, warranties that the *
269−* Covered Software is free of defects, merchantable, fit for a *
270−* particular purpose or non-infringing. The entire risk as to the *
271−* quality and performance of the Covered Software is with You. *
272−* Should any Covered Software prove defective in any respect, You *
273−* (not any Contributor) assume the cost of any necessary servicing, *
274−* repair, or correction. This disclaimer of warranty constitutes an *
275−* essential part of this License. No use of any Covered Software is *
276−* authorized under this License except under this disclaimer. *
277−* *
278−************************************************************************
279−
280−************************************************************************
281−* *
282−* 7. Limitation of Liability *
283−* -------------------------- *
284−* *
285−* Under no circumstances and under no legal theory, whether tort *
286−* (including negligence), contract, or otherwise, shall any *
287−* Contributor, or anyone who distributes Covered Software as *
288−* permitted above, be liable to You for any direct, indirect, *
289−* special, incidental, or consequential damages of any character *
290−* including, without limitation, damages for lost profits, loss of *
291−* goodwill, work stoppage, computer failure or malfunction, or any *
292−* and all other commercial damages or losses, even if such party *
293−* shall have been informed of the possibility of such damages. This *
294−* limitation of liability shall not apply to liability for death or *
295−* personal injury resulting from such party's negligence to the *
296−* extent applicable law prohibits such limitation. Some *
297−* jurisdictions do not allow the exclusion or limitation of *
298−* incidental or consequential damages, so this exclusion and *
299−* limitation may not apply to You. *
300−* *
301−************************************************************************
302−
303−8. Litigation
304−-------------
305−
306−Any litigation relating to this License may be brought only in the
307−courts of a jurisdiction where the defendant maintains its principal
308−place of business and such litigation shall be governed by laws of that
309−jurisdiction, without reference to its conflict-of-law provisions.
310−Nothing in this Section shall prevent a party's ability to bring
311−cross-claims or counter-claims.
312−
313−9. Miscellaneous
314−----------------
315−
316−This License represents the complete agreement concerning the subject
317−matter hereof. If any provision of this License is held to be
318−unenforceable, such provision shall be reformed only to the extent
319−necessary to make it enforceable. Any law or regulation which provides
320−that the language of a contract shall be construed against the drafter
321−shall not be used to construe this License against a Contributor.
322−
323−10. Versions of the License
324−---------------------------
325−
326−10.1. New Versions
327−
328−Mozilla Foundation is the license steward. Except as provided in Section
329−10.3, no one other than the license steward has the right to modify or
330−publish new versions of this License. Each version will be given a
331−distinguishing version number.
332−
333−10.2. Effect of New Versions
334−
335−You may distribute the Covered Software under the terms of the version
336−of the License under which You originally received the Covered Software,
337−or under the terms of any subsequent version published by the license
338−steward.
339−
340−10.3. Modified Versions
341−
342−If you create software not governed by this License, and you want to
343−create a new license for such software, you may create and use a
344−modified version of this License if you rename the license and remove
345−any references to the name of the license steward (except to note that
346−such modified license differs from this License).
347−
348−10.4. Distributing Source Code Form that is Incompatible With Secondary
349−Licenses
350−
351−If You choose to distribute Source Code Form that is Incompatible With
352−Secondary Licenses under the terms of this version of the License, the
353−notice described in Exhibit B of this License must be attached.
354−
355−Exhibit A - Source Code Form License Notice
356−-------------------------------------------
357−
358− This Source Code Form is subject to the terms of the Mozilla Public
359− License, v. 2.0. If a copy of the MPL was not distributed with this
360− file, You can obtain one at http://mozilla.org/MPL/2.0/.
361−
362−If it is not possible or desirable to put the notice in a particular
363−file, then You may include the notice in a location (such as a LICENSE
364−file in a relevant directory) where a recipient would be likely to look
365−for such a notice.
366−
367−You may add additional accurate notices of copyright ownership.
368−
369−Exhibit B - "Incompatible With Secondary Licenses" Notice
370−---------------------------------------------------------
371−
372− This Source Code Form is "Incompatible With Secondary Licenses", as
373− defined by the Mozilla Public License, v. 2.0.
dodrio/README.mddeleted−108
@@ -1,108 +0,0 @@
1−# Dodrio
2−
3−A fast, bump-allocated virtual DOM library for Rust and WebAssembly. Note that
4−Dodrio is still **experimental**.
5−
6−<!-- START doctoc generated TOC please keep comment here to allow auto update -->
7−<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
8−
9−
10−- [Warning](#warning)
11−- [Examples](#examples)
12−- [Design](#design)
13− - [Bump Allocation](#bump-allocation)
14− - [Change List as Stack Machine](#change-list-as-stack-machine)
15− - [Library — Not Framework](#library--not-framework)
16−
17−<!-- END doctoc generated TOC please keep comment here to allow auto update -->
18−
19−## Warning
20−
21−I reiterate that Dodrio is in a very **experimental** state. It probably has
22−bugs, and no one is using it in production.
23−
24−## Examples
25−
26−Here is the classic "Hello, World!" example:
27−
28−```rust
29−struct Hello {
30− who: String,
31−}
32−
33−impl Render for Hello {
34− fn render<'a>(&self, cx: &mut RenderContext<a>) -> Node<'a> {
35− let who = bumpalo::format!(in cx.bump, "Hello, {}!", self.who);
36− div(cx)
37− .children([text(who.into_bump_str())])
38− .finish()
39− }
40−}
41−```
42−
43−More examples can be found in [the `examples`
44−directory](https://github.com/fitzgen/dodrio/tree/master/examples), including:
45−
46−* [`counter`](https://github.com/fitzgen/dodrio/tree/master/examples/counter):
47− Incrementing and decrementing a counter.
48−* [`input-form`](https://github.com/fitzgen/dodrio/tree/master/examples/input-form):
49− Reading an `<input>` and displaying its contents.
50−* [`todomvc`](https://github.com/fitzgen/dodrio/tree/master/examples/todomvc):
51− An implementation of the infamous TodoMVC application.
52−* [`moire`](https://github.com/fitzgen/dodrio/tree/master/examples/moire): The
53− WebRender Moiré patterns demo.
54−* [`game-of-life`](https://github.com/fitzgen/dodrio/tree/master/examples/game-of-life):
55− The Rust and WebAssembly book's Game of Life tutorial rendered with Dodrio
56− instead of to 2D canvas.
57−* [`js-component`](https://github.com/fitzgen/dodrio/tree/master/examples/js-component):
58− Defines a rendering component in JavaScript with the `dodrio-js-api` crate.
59−
60−## Cargo Features
61−
62−* `log` &mdash; enable debugging-oriented log messages with the `log` crate's
63− facade. You still have to initialize a logger for the messages to go anywhere,
64− such as [`console_log`](https://github.com/iamcodemaker/console_log).
65−
66−* `serde` &mdash; enable `serde::{Serialize, Deserialize}` implementations for
67− `Cached<R>` where `R` is serializable and deserializable.
68−
69−## Design
70−
71−### Bump Allocation
72−
73−Bump allocation is essentially the fastest method of allocating objects. It has
74−constraints, but works particularly well when allocation lifetimes match program
75−phases. And virtual DOMs are very phase oriented.
76−
77−Dodrio maintains three bump allocation arenas:
78−
79−1. The newest, most up-to-date virtual DOM. The virtual DOM nodes themselves and
80− any temporary containers needed while creating them are allocated into this
81− arena.
82−2. The previous virtual DOM. This reflects the current state of the physical
83− DOM.
84−3. The difference between (1) and (2). This is a sequence of DOM mutation
85− operations — colloquially known as a "change list" — which if applied to
86− the physical DOM, will make the physical DOM match (1).
87−
88−Rendering happens as follows:
89−
90−1. The application state is rendered into bump allocation arena (1).
91−2. (1) is diffed with (2) and the changes are emitted into (3).
92−3. JavaScript code applies the change list in (3) to the physical DOM.
93−4. (1) and (2) are swapped, double-buffering style, and the new (1) has its bump
94− allocation pointer reset, as does (3).
95−5. Rinse and repeat.
96−
97−### Change List as Stack Machine
98−
99−The change list that represents the difference between how the physical DOM
100−currently looks, and our ideal virtual DOM state is encoded in a tiny stack
101−machine language. A stack machine works particularly well for applying DOM
102−diffs, a task that is essentially a tree traversal.
103−
104−### Library — Not Framework
105−
106−Dodrio is just a library. (And did I mention it is experimental?!) It is not a
107−full-fledged, complete, batteries-included solution for all frontend Web
108−development. And it never intends to become that either.
dodrio/benches/benches.rsdeleted−118
@@ -1,118 +0,0 @@
1−#![cfg(all(
2− feature = "xxx-unstable-internal-use-only",
3− not(target_arch = "wasm32")
4−))]
5−
6−use criterion::{
7− black_box, criterion_group, criterion_main, Benchmark, Criterion, ParameterizedBenchmark,
8− Throughput,
9−};
10−use dodrio::{
11− builder::*,
12− bumpalo::{self, Bump},
13− CachedSet, Node, Render, RenderContext, Vdom,
14−};
15−use std::cell::RefCell;
16−use std::convert::TryInto;
17−
18−/// The simplest thing we can render: `<div/>`.
19−struct Empty;
20−impl<'a> Render<'a> for Empty {
21− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
22− div(&cx).finish()
23− }
24−}
25−
26−/// Render a list that is `self.0` items long, has attributes and listeners.
27−struct SimpleList(usize);
28−impl<'a> Render<'a> for SimpleList {
29− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
30− let mut children = bumpalo::collections::Vec::with_capacity_in(self.0, cx.bump);
31− children.extend((0..self.0).map(|_| {
32− li(&cx)
33− .attr("class", "my-list-item")
34− .on("click", |_root, _vdom, _event| {
35− panic!("no one should call this")
36− })
37− .children([text("a list item")])
38− .finish()
39− }));
40− ol(&cx).attr("id", "my-list").children(children).finish()
41− }
42−}
43−
44−fn criterion_benchmark(c: &mut Criterion) {
45− c.bench(
46− "render",
47− Benchmark::new("empty", {
48− let mut bump = Bump::new();
49− let cached_set = RefCell::new(CachedSet::default());
50− let mut templates = Default::default();
51− move |b| {
52− bump.reset();
53− let mut cx = RenderContext::new(&bump, &cached_set, &mut templates);
54− b.iter(|| {
55− black_box(Empty.render(&mut cx));
56− })
57− }
58− }),
59− );
60−
61− c.bench(
62− "render",
63− ParameterizedBenchmark::new(
64− "list",
65− {
66− let mut bump = Bump::new();
67− let cached_set = RefCell::new(CachedSet::default());
68− let mut templates = Default::default();
69− move |b, n| {
70− bump.reset();
71− let mut cx = RenderContext::new(&bump, &cached_set, &mut templates);
72− b.iter(|| {
73− black_box(SimpleList(*n).render(&mut cx));
74− })
75− }
76− },
77− vec![100, 1_000, 10_000],
78− )
79− .throughput(|n| Throughput::Elements((*n).try_into().unwrap())),
80− );
81−
82− c.bench(
83− "render-and-diff",
84− ParameterizedBenchmark::new(
85− "same-list",
86− |b, &n| {
87− let vdom = Vdom::new(&(), SimpleList(n));
88− b.iter(|| {
89− vdom.immediately_render_and_diff(SimpleList(n));
90− black_box(&vdom);
91− })
92− },
93− vec![100, 1_000, 10_000],
94− )
95− .with_function("empty-to-full-list-to-empty", |b, &n| {
96− b.iter(|| {
97− let vdom = Vdom::new(&(), Empty);
98− vdom.immediately_render_and_diff(SimpleList(n));
99− black_box(&vdom);
100− vdom.immediately_render_and_diff(Empty);
101− black_box(&vdom);
102− });
103− })
104− .with_function("append-one-and-remove-one", |b, &n| {
105− let vdom = Vdom::new(&(), SimpleList(n));
106− b.iter(|| {
107− vdom.immediately_render_and_diff(SimpleList(n + 1));
108− black_box(&vdom);
109− vdom.immediately_render_and_diff(SimpleList(n));
110− black_box(&vdom);
111− })
112− })
113− .throughput(|n| Throughput::Elements((*n).try_into().unwrap())),
114− );
115−}
116−
117−criterion_group!(benches, criterion_benchmark);
118−criterion_main!(benches);
dodrio/build.shdeleted−11
@@ -1,11 +0,0 @@
1−#!/usr/bin/env bash
2−
3−cd $(dirname $0)
4−set -eux
5−
6−cargo fmt --all || true
7−cargo build --all --target wasm32-unknown-unknown
8−
9−for x in ./examples/*; do
10− wasm-pack build --target web "$x"
11−done
dodrio/crates/js-api/Cargo.tomldeleted−22
@@ -1,22 +0,0 @@
1−[package]
2−authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
3−categories = ["wasm", "web-programming"]
4−description = "An API for implementing Dodrio rendering components with JavaScript"
5−edition = "2018"
6−keywords = ["javascript", "js", "vdom", "virtual-dom"]
7−license = "MPL-2.0"
8−name = "dodrio-js-api"
9−repository = "https://github.com/fitzgen/dodrio"
10−version = "0.2.0"
11−
12−[badges]
13−travis-ci = { repository = "fitzgen/dodrio" }
14−
15−[dependencies]
16−dodrio = { version = "=0.2.0", path = "../.." }
17−wasm-bindgen = "0.2.55"
18−js-sys = "0.3.32"
19−wasm-bindgen-futures = "0.4.5"
20−
21−[dev-dependencies]
22−wasm-bindgen-test = "0.3.5"
dodrio/crates/js-api/src/lib.rsdeleted−324
@@ -1,324 +0,0 @@
1−/*!
2−
3−Implementing `dodrio` render components with JavaScript.
4−
5−This crate provides a Rust type `JsRender` that wraps a JavaScript object with a
6−`render` method. `JsRender` implements `dodrio::Render` by calling its wrapped
7−object's `render` method to get a JavaScript virtual DOM represented as a tree
8−of JavaScript values. It then converts this tree of JavaScript values into
9−`dodrio`'s normal bump-allocated virtual DOM representation.
10−
11−This is likely much slower than rendering virtual DOMs directly into the bump
12−allocator from the Rust side of things! Additionally, the shape of the
13−JavaScript virtual DOM is a bit funky and unidiomatic. Keep in mind that this
14−crate exists as a proof of concept for integrating JavaScript components into
15−`dodrio` -- which is itself *also* experimental -- and so this crate definitely
16−has some rough edges.
17−
18−# Example
19−
20−Here is a JavaScript implementation of a rendering component:
21−
22−```javascript
23−class Greeting {
24− constructor(who) {
25− this.who = who;
26− }
27−
28− render() {
29− return {
30− tagName: "p",
31− attributes: [
32− {
33− name: "class",
34− value: "greeting",
35− },
36− ],
37− listeners: [
38− {
39− on: "click",
40− callback: this.onClick.bind(this),
41− }
42− ],
43− children: [
44− "Hello, ",
45− {
46− tagName: "strong",
47− children: [this.who],
48− }
49− ],
50− };
51− }
52−
53− async onClick(vdom, event) {
54− // Be more excited!
55− this.who += "!";
56−
57− // Schedule a re-render.
58− await vdom.render();
59−
60− console.log("re-rendering finished!");
61− }
62−}
63−```
64−
65−And here is a Rust rendering component that internally uses the JS rendering
66−component:
67−
68−```rust,no_run
69−use dodrio::{Node, Render, RenderContext, Vdom};
70−use dodrio_js_api::JsRender;
71−use js_sys::Object;
72−use wasm_bindgen::prelude::*;
73−
74−#[wasm_bindgen]
75−extern {
76− // Import the JS `Greeting` class.
77− #[wasm_bindgen(extends = Object)]
78− #[derive(Clone, Debug)]
79− type Greeting;
80−
81− // And the `Greeting` class's constructor.
82− #[wasm_bindgen(constructor)]
83− fn new(who: &str) -> Greeting;
84−}
85−
86−/// This is our Rust rendering component that wraps the JS rendering component.
87−pub struct GreetingViaJs {
88− js: JsRender,
89−}
90−
91−impl GreetingViaJs {
92− /// Create a new `GreetingViaJs`, which will internally create a new JS
93− /// `Greeting`.
94− pub fn new(who: &str) -> GreetingViaJs {
95− let js = JsRender::new(Greeting::new(who));
96− GreetingViaJs { js }
97− }
98−}
99−
100−/// And finally the `Render` implementation! This adds a `<p>` element and some
101−/// text around whatever the inner JS `Greeting` component renders.
102−impl<'a> Render<'a> for GreetingViaJs {
103− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
104− use dodrio::builder::*;
105− p(&cx)
106− .children([
107− text("JavaScript says: "),
108− self.js.render(cx),
109− ])
110− .finish()
111− }
112−}
113−```
114−
115− */
116−#![deny(missing_docs, missing_debug_implementations)]
117−
118−use dodrio::{builder, bumpalo, Node, Render, RenderContext};
119−use js_sys::{Object, Promise, Reflect};
120−use wasm_bindgen::prelude::*;
121−use wasm_bindgen::JsCast;
122−
123−#[wasm_bindgen]
124−extern "C" {
125− /// A rendering component implemented in JavaScript.
126− ///
127− /// The rendering API is a bit duck-typed: any JS object with a `render`
128− /// method that returns a virtual DOM as JS values with the right shape
129− /// works.
130− ///
131− /// See `JsRender::new` for converting existing JS objects into `JsRender`s.
132− #[derive(Debug, Clone)]
133− pub type JsRender;
134− #[wasm_bindgen(structural, method)]
135− fn render(this: &JsRender) -> JsValue;
136−
137− #[wasm_bindgen(extends = Object)]
138− #[derive(Debug, Clone)]
139− type Element;
140− #[wasm_bindgen(structural, getter, method, js_name = tagName)]
141− fn tag_name(this: &Element) -> String;
142− #[wasm_bindgen(structural, getter, method)]
143− fn listeners(this: &Element) -> js_sys::Array;
144− #[wasm_bindgen(structural, getter, method)]
145− fn attributes(this: &Element) -> js_sys::Array;
146− #[wasm_bindgen(structural, getter, method)]
147− fn children(this: &Element) -> js_sys::Array;
148−
149− #[wasm_bindgen(extends = Object)]
150− #[derive(Debug, Clone)]
151− type Listener;
152− #[wasm_bindgen(structural, getter, method)]
153− fn on(this: &Listener) -> String;
154− #[wasm_bindgen(structural, getter, method)]
155− fn callback(this: &Listener) -> js_sys::Function;
156−
157− #[wasm_bindgen(extends = Object)]
158− #[derive(Debug, Clone)]
159− type Attribute;
160− #[wasm_bindgen(structural, getter, method)]
161− fn name(this: &Attribute) -> String;
162− #[wasm_bindgen(structural, getter, method)]
163− fn value(this: &Attribute) -> String;
164−}
165−
166−/// A weak handle to a virtual DOM.
167−///
168−/// This is essentially the same as `dodrio::VdomWeak`, but exposed to
169−/// JavaScript.
170−#[wasm_bindgen]
171−#[derive(Clone, Debug)]
172−pub struct VdomWeak {
173− inner: dodrio::VdomWeak,
174−}
175−
176−impl VdomWeak {
177− fn new(inner: dodrio::VdomWeak) -> VdomWeak {
178− VdomWeak { inner }
179− }
180−}
181−
182−#[wasm_bindgen]
183−impl VdomWeak {
184− /// Schedule re-rendering of the virtual DOM. A promise is returned that is
185− /// resolved after the rendering has happened.
186− pub fn render(&self) -> Promise {
187− let future = self.inner.render();
188−
189− wasm_bindgen_futures::future_to_promise(async move {
190− if let Err(e) = future.await {
191− let msg = e.to_string();
192− Err(js_sys::Error::new(&msg).into())
193− } else {
194− Ok(JsValue::null())
195− }
196− })
197− }
198−}
199−
200−impl JsRender {
201− /// Convert a `js_sys::Object` into a `JsRender`.
202− ///
203− /// The given object must have a `render` method that conforms to the
204− /// duck-typed virtual DOM interface which is described in the crate-level
205− /// documentation.
206− pub fn new<O>(object: O) -> JsRender
207− where
208− O: Into<Object>,
209− {
210− let object = object.into();
211− debug_assert!(
212− has_property(&object, "render"),
213− "JS rendering components must have a `render` method"
214− );
215− object.unchecked_into::<JsRender>()
216− }
217−}
218−
219−impl<'a> Render<'a> for JsRender {
220− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
221− create(cx, self.render())
222− }
223−}
224−
225−fn has_property(obj: &Object, property: &str) -> bool {
226− Reflect::has(obj, &property.into()).unwrap_or_default()
227−}
228−
229−fn create<'a>(cx: &mut RenderContext<'a>, val: JsValue) -> Node<'a> {
230− if let Some(txt) = val.as_string() {
231− let text = bumpalo::collections::String::from_str_in(&txt, cx.bump);
232− return builder::text(text.into_bump_str());
233− }
234−
235− let elem = val.unchecked_into::<Element>();
236− debug_assert!(
237− elem.is_instance_of::<Object>(),
238− "JS render methods should only return strings for text nodes or objects for elements"
239− );
240− debug_assert!(
241− has_property(&elem, "tagName"),
242− "element objects returned by JS render methods must have a `tagName` property"
243− );
244−
245− let tag_name = elem.tag_name();
246− let tag_name = bumpalo::collections::String::from_str_in(&tag_name, cx.bump);
247−
248− builder::ElementBuilder::new(cx.bump, tag_name.into_bump_str())
249− .listeners({
250− let mut listeners =
251− bumpalo::collections::Vec::new_in(cx.bump);
252− if has_property(&elem, "listeners") {
253− let js_listeners = elem.listeners();
254− listeners.reserve(js_listeners.length() as usize);
255− js_listeners.for_each(&mut |listener, _index, _array| {
256− let listener = listener.unchecked_into::<Listener>();
257− debug_assert!(
258− listener.is_instance_of::<Object>(),
259− "listeners returned by JS render methods must be objects"
260− );
261− debug_assert!(
262− has_property(&listener, "on"),
263− "listener objects returned by JS render methods must have an `on` property"
264− );
265− debug_assert!(
266− has_property(&listener, "callback"),
267− "listener objects returned by JS render methods must have an `callback` property"
268− );
269− let on = listener.on();
270− let on = bumpalo::collections::String::from_str_in(&on, cx.bump);
271− let callback = listener.callback();
272− let elem = elem.clone();
273− listeners.push(builder::on(cx.bump, on.into_bump_str(), move |_root, vdom, event| {
274− let vdom = VdomWeak::new(vdom);
275− let vdom: JsValue = vdom.into();
276− if let Err(e) = callback.call2(&elem, &vdom, &event) {
277− wasm_bindgen::throw_val(e);
278− }
279− }));
280− });
281− }
282− listeners
283− })
284− .attributes({
285− let mut attributes = bumpalo::collections::Vec::new_in(cx.bump);
286− if has_property(&elem, "attributes") {
287− let js_attributes = elem.attributes();
288− attributes.reserve(js_attributes.length() as usize);
289− js_attributes.for_each(&mut |attribute, _index, _array| {
290− let attribute = attribute.unchecked_into::<Attribute>();
291− debug_assert!(
292− attribute.is_instance_of::<Object>(),
293− "attributes returned by JS render methods must be objects"
294− );
295− debug_assert!(
296− has_property(&attribute, "name"),
297− "attribute objects returned by JS render methods must have a `name` property"
298− );
299− debug_assert!(
300− has_property(&attribute, "value"),
301− "attribute objects returned by JS render methods must have a `value` property"
302− );
303− let name = attribute.name();
304− let name = bumpalo::collections::String::from_str_in(&name, cx.bump);
305− let value = attribute.value();
306− let value = bumpalo::collections::String::from_str_in(&value, cx.bump);
307− attributes.push(builder::attr(name.into_bump_str(), value.into_bump_str()));
308− });
309− }
310− attributes
311− })
312− .children({
313− let mut children = bumpalo::collections::Vec::new_in(cx.bump);
314− if has_property(&elem, "children") {
315− let js_children = elem.children();
316− children.reserve(js_children.length() as usize);
317− js_children.for_each(&mut |child, _index, _array| {
318− children.push(create(cx, child));
319− });
320− }
321− children
322− })
323− .finish()
324−}
dodrio/examples/counter/Cargo.tomldeleted−33
@@ -1,33 +0,0 @@
1−[package]
2−name = "dodrio-counter"
3−version = "0.1.0"
4−authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
5−edition = "2018"
6−
7−[lib]
8−crate-type = ["cdylib"]
9−
10−[features]
11−
12−[dependencies]
13−console_error_panic_hook = "0.1.6"
14−console_log = "0.2.0"
15−dodrio = { path = "../.." }
16−log = "0.4.11"
17−wasm-bindgen = "0.2.55"
18−
19−[dependencies.web-sys]
20−version = "0.3.32"
21−features = [
22− "console",
23− "Document",
24− "Event",
25− "EventTarget",
26− "HtmlElement",
27− "MouseEvent",
28− "Node",
29− "Window",
30−]
31−
32−[dev-dependencies]
33−wasm-bindgen-test = "0.3.5"
dodrio/examples/counter/README.mddeleted−21
@@ -1,21 +0,0 @@
1−# Counter
2−
3−A counter that can be incremented and decremented.
4−
5−## Source
6−
7−See `src/lib.rs`.
8−
9−## Build
10−
11−```
12−wasm-pack build --target web
13−```
14−
15−## Serve
16−
17−Use any HTTP server, for example:
18−
19−```
20−python -m SimpleHTTPServer
21−```
dodrio/examples/counter/index.htmldeleted−13
@@ -1,13 +0,0 @@
1−<!DOCTYPE html>
2−<html>
3− <head>
4− <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
5− <title>Counter</title>
6− </head>
7− <body>
8− <script type="module">
9− import init from "./pkg/dodrio_counter.js";
10− init("./pkg/dodrio_counter_bg.wasm");
11− </script>
12− </body>
13−</html>
dodrio/examples/counter/src/lib.rsdeleted−87
@@ -1,87 +0,0 @@
1−use dodrio::{bumpalo, Node, Render, RenderContext};
2−use log::*;
3−use wasm_bindgen::prelude::*;
4−
5−/// A counter that can be incremented and decremented!
6−struct Counter {
7− count: isize,
8−}
9−
10−impl Counter {
11− /// Construct a new, zeroed counter.
12− fn new() -> Counter {
13− Counter { count: 0 }
14− }
15−
16− /// Increment this counter's count.
17− fn increment(&mut self) {
18− self.count += 1;
19− }
20−
21− /// Decrement this counter's count.
22− fn decrement(&mut self) {
23− self.count -= 1;
24− }
25−}
26−
27−// The `Render` implementation for `Counter`s displays the current count and has
28−// buttons to increment and decrement the count.
29−impl<'a> Render<'a> for Counter {
30− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
31− use dodrio::builder::*;
32−
33− // Stringify the count as a bump-allocated string.
34− let count = bumpalo::format!(in cx.bump, "{}", self.count);
35−
36− div(&cx)
37− .children([
38− button(&cx)
39− .on("click", |root, vdom, _event| {
40− // Cast the root render component to a `Counter`, since
41− // we know that's what it is.
42− let counter = root.unwrap_mut::<Counter>();
43−
44− // Increment the counter.
45− counter.increment();
46−
47− // Since the count has updated, we should re-render the
48− // counter on the next animation frame.
49− vdom.schedule_render();
50− })
51− .children([text("+")])
52− .finish(),
53− text(count.into_bump_str()),
54− button(&cx)
55− .on("click", |root, vdom, _event| {
56− // Same as above, but decrementing instead of incrementing.
57− let counter = root.unwrap_mut::<Counter>();
58− counter.decrement();
59− vdom.schedule_render();
60− })
61− .children([text("-")])
62− .finish(),
63− ])
64− .finish()
65− }
66−}
67−
68−#[wasm_bindgen(start)]
69−pub fn run() {
70− // Initialize debug logging for if/when things go wrong.
71− console_error_panic_hook::set_once();
72− console_log::init_with_level(Level::Trace).expect("should initialize logging OK");
73−
74− // Get the document's `<body>`.
75− let window = web_sys::window().unwrap();
76− let document = window.document().unwrap();
77− let body = document.body().unwrap();
78−
79− // Construct a new counter component.
80− let counter = Counter::new();
81−
82− // Mount our counter component to the `<body>`.
83− let vdom = dodrio::Vdom::new(&body, counter);
84−
85− // Run the virtual DOM and its listeners forever.
86− vdom.forget();
87−}
dodrio/examples/game-of-life/Cargo.tomldeleted−29
@@ -1,29 +0,0 @@
1−[package]
2−name = "dodrio-game-of-life"
3−version = "0.1.0"
4−authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
5−edition = "2018"
6−
7−[lib]
8−crate-type = ["cdylib"]
9−
10−[features]
11−
12−[dependencies]
13−dodrio = { path = "../.." }
14−wasm-bindgen = "0.2.55"
15−console_error_panic_hook = "0.1.6"
16−js-sys = "0.3.32"
17−wasm-bindgen-futures = "0.4.5"
18−
19−[dependencies.web-sys]
20−version = "0.3.32"
21−features = [
22− "Document",
23− "HtmlElement",
24− "Node",
25− "Window",
26−]
27−
28−[dev-dependencies]
29−wasm-bindgen-test = "0.3.5"
dodrio/examples/game-of-life/README.mddeleted−23
@@ -1,23 +0,0 @@
1−# Conway's Game of Life
2−
3−A port of the [Rust and WebAssembly tutorial's Game of Life
4−implementation](https://rustwasm.github.io/book/game-of-life/introduction.html)
5−to rendering to HTML with Dodrio instead of to a canvas via JavaScript.
6−
7−## Source
8−
9−See `src/lib.rs`.
10−
11−## Build
12−
13−```
14−wasm-pack build --target web
15−```
16−
17−## Serve
18−
19−Use any HTTP server, for example:
20−
21−```
22−python -m SimpleHTTPServer
23−```
dodrio/examples/game-of-life/index.htmldeleted−44
@@ -1,44 +0,0 @@
1−<!DOCTYPE html>
2−<html>
3− <head>
4− <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
5− <title>Conway's Game of Life</title>
6− <style>
7− .cell {
8− display: inline-block;
9− width: 1vh;
10− height: 1vh;
11− border: 1px #cccccc solid;
12− margin: -1px;
13− padding: 0;
14− }
15−
16− .row {
17− height: 1vh;
18− margin: 0;
19− padding: 0;
20− }
21−
22− body {
23− width: 100vw;
24− height: 100vh;
25− display: flex;
26− align-items: center;
27− justify-content: center;
28− padding: 0;
29− margin: 0;
30− }
31−
32− html {
33− padding: 0;
34− margin: 0;
35− }
36− </style>
37− </head>
38− <body>
39− <script type="module">
40− import init from "./pkg/dodrio_game_of_life.js";
41− init("./pkg/dodrio_game_of_life_bg.wasm");
42− </script>
43− </body>
44−</html>
dodrio/examples/game-of-life/src/lib.rsdeleted−218
@@ -1,218 +0,0 @@
1−use dodrio::{bumpalo, Node, Render, RenderContext, Vdom};
2−use std::cell::RefCell;
3−use std::rc::Rc;
4−use wasm_bindgen::{prelude::*, JsCast};
5−
6−#[repr(u8)]
7−#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8−pub enum Cell {
9− Dead = 0,
10− Alive = 1,
11−}
12−
13−/// The universe containing all of the cells for our Game of Life.
14−struct Universe {
15− width: u32,
16− height: u32,
17− cells: Vec<Cell>,
18−}
19−
20−impl Universe {
21− /// Construct a new universe.
22− pub fn new() -> Universe {
23− let width = 64;
24− let height = 64;
25−
26− let cells = (0..width * height)
27− .map(|i| {
28− if i % 2 == 0 || i % 7 == 0 {
29− Cell::Alive
30− } else {
31− Cell::Dead
32− }
33− })
34− .collect();
35−
36− Universe {
37− width,
38− height,
39− cells,
40− }
41− }
42−
43− /// Compute one tick of the universe.
44− pub fn tick(&mut self) {
45− let mut next = self.cells.clone();
46−
47− for row in 0..self.height {
48− for col in 0..self.width {
49− let idx = self.get_index(row, col);
50− let cell = self.cells[idx];
51− let live_neighbors = self.live_neighbor_count(row, col);
52−
53− let next_cell = match (cell, live_neighbors) {
54− // Rule 1: Any live cell with fewer than two live neighbours
55− // dies, as if caused by underpopulation.
56− (Cell::Alive, x) if x < 2 => Cell::Dead,
57− // Rule 2: Any live cell with two or three live neighbours
58− // lives on to the next generation.
59− (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,
60− // Rule 3: Any live cell with more than three live
61− // neighbours dies, as if by overpopulation.
62− (Cell::Alive, x) if x > 3 => Cell::Dead,
63− // Rule 4: Any dead cell with exactly three live neighbours
64− // becomes a live cell, as if by reproduction.
65− (Cell::Dead, 3) => Cell::Alive,
66− // All other cells remain in the same state.
67− (otherwise, _) => otherwise,
68− };
69−
70− next[idx] = next_cell;
71− }
72− }
73−
74− self.cells = next;
75− }
76−
77− fn get_index(&self, row: u32, column: u32) -> usize {
78− (row * self.width + column) as usize
79− }
80−
81− fn live_neighbor_count(&self, row: u32, column: u32) -> u8 {
82− let mut count = 0;
83−
84− let north = if row == 0 { self.height - 1 } else { row - 1 };
85−
86− let south = if row == self.height - 1 { 0 } else { row + 1 };
87−
88− let west = if column == 0 {
89− self.width - 1
90− } else {
91− column - 1
92− };
93−
94− let east = if column == self.width - 1 {
95− 0
96− } else {
97− column + 1
98− };
99−
100− let nw = self.get_index(north, west);
101− count += self.cells[nw] as u8;
102−
103− let n = self.get_index(north, column);
104− count += self.cells[n] as u8;
105−
106− let ne = self.get_index(north, east);
107− count += self.cells[ne] as u8;
108−
109− let w = self.get_index(row, west);
110− count += self.cells[w] as u8;
111−
112− let e = self.get_index(row, east);
113− count += self.cells[e] as u8;
114−
115− let sw = self.get_index(south, west);
116− count += self.cells[sw] as u8;
117−
118− let s = self.get_index(south, column);
119− count += self.cells[s] as u8;
120−
121− let se = self.get_index(south, east);
122− count += self.cells[se] as u8;
123−
124− count
125− }
126−}
127−
128−/// The rendering implementation for our Game of Life.
129−impl<'a> Render<'a> for Universe {
130− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
131− use dodrio::builder::*;
132−
133− let mut rows = bumpalo::collections::Vec::with_capacity_in(self.height as usize, cx.bump);
134−
135− for row in self.cells.chunks(self.width as usize) {
136− let mut cells =
137− bumpalo::collections::Vec::with_capacity_in(self.width as usize, cx.bump);
138−
139− for cell in row {
140− cells.push(
141− span(&cx)
142− .attr("class", "cell")
143− .attr(
144− "style",
145− match cell {
146− Cell::Alive => "background-color: black",
147− Cell::Dead => "background-color: white",
148− },
149− )
150− .finish(),
151− );
152− }
153−
154− rows.push(div(&cx).attr("class", "row").children(cells).finish());
155− }
156−
157− div(&cx).attr("class", "universe").children(rows).finish()
158− }
159−}
160−
161−#[wasm_bindgen(start)]
162−pub fn run() {
163− // Set up the panic hook for debugging when things go wrong.
164− console_error_panic_hook::set_once();
165−
166− // Grab the document's `<body>`.
167− let window = web_sys::window().unwrap_throw();
168− let document = window.document().unwrap_throw();
169− let body = document.body().unwrap_throw();
170−
171− // Create a new Game of Life `Universe` render component.
172− let universe = Universe::new();
173−
174− // Create a virtual DOM and mount it and the `Universe` render component to the
175− // `<body>`.
176− let vdom = Vdom::new(body.as_ref(), universe);
177−
178− // Kick off a loop that keeps computing a tick of the universe and then
179− // re-rendering on every animation frame.
180− let rc = <Rc<RefCell<Option<Closure<dyn FnMut()>>>>>::default();
181− let rc2 = rc.clone();
182− let window2 = window.clone();
183− let weak = vdom.weak();
184− let f = Closure::wrap(Box::new(move || {
185− weak.schedule_render();
186−
187− wasm_bindgen_futures::spawn_local({
188− let weak = weak.clone();
189− async move {
190− let fut = weak.with_component(|root| {
191− let universe = root.unwrap_mut::<Universe>();
192− universe.tick();
193− });
194−
195− if fut.await.is_err() {
196− wasm_bindgen::throw_str("impossible, we always keep the vdom alive");
197− }
198− }
199− });
200−
201− window
202− .request_animation_frame(
203− rc.borrow()
204− .as_ref()
205− .unwrap_throw()
206− .as_ref()
207− .unchecked_ref::<js_sys::Function>(),
208− )
209− .unwrap_throw();
210− }) as Box<dyn FnMut()>);
211− window2
212− .request_animation_frame(f.as_ref().unchecked_ref::<js_sys::Function>())
213− .unwrap_throw();
214− *rc2.borrow_mut() = Some(f);
215−
216− // Run the virtual DOM forever and don't unmount it.
217− vdom.forget();
218−}
dodrio/examples/hello-world/Cargo.tomldeleted−27
@@ -1,27 +0,0 @@
1−[package]
2−name = "dodrio-hello-world"
3−version = "0.1.0"
4−authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
5−edition = "2018"
6−
7−[lib]
8−crate-type = ["cdylib"]
9−
10−[features]
11−
12−[dependencies]
13−dodrio = { path = "../.." }
14−wasm-bindgen = "0.2.55"
15−console_error_panic_hook = "0.1.6"
16−
17−[dependencies.web-sys]
18−version = "0.3.32"
19−features = [
20− "Document",
21− "HtmlElement",
22− "Node",
23− "Window",
24−]
25−
26−[dev-dependencies]
27−wasm-bindgen-test = "0.3.5"
dodrio/examples/hello-world/README.mddeleted−21
@@ -1,21 +0,0 @@
1−# Hello World
2−
3−The most basic example!
4−
5−## Source
6−
7−See `src/lib.rs`.
8−
9−## Build
10−
11−```
12−wasm-pack build --target web
13−```
14−
15−## Serve
16−
17−Use any HTTP server, for example:
18−
19−```
20−python -m SimpleHTTPServer
21−```
dodrio/examples/hello-world/index.htmldeleted−13
@@ -1,13 +0,0 @@
1−<!DOCTYPE html>
2−<html>
3− <head>
4− <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
5− <title>Hello, World!</title>
6− </head>
7− <body>
8− <script type="module">
9− import init from "./pkg/dodrio_hello_world.js";
10− init("./pkg/dodrio_hello_world_bg.wasm");
11− </script>
12− </body>
13−</html>
dodrio/examples/hello-world/src/lib.rsdeleted−42
@@ -1,42 +0,0 @@
1−use dodrio::{builder::*, bumpalo};
2−use dodrio::{Node, Render, RenderContext, Vdom};
3−use wasm_bindgen::prelude::*;
4−
5−/// A rendering component that displays a greeting.
6−struct Hello {
7− /// Who to greet.
8− who: String,
9−}
10−
11−// The `Render` implementation describes how to render a `Hello` component into
12−// HTML.
13−impl<'a> Render<'a> for Hello {
14− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
15− let msg = bumpalo::format!(in cx.bump, "Hello, {}!", self.who);
16− let msg = msg.into_bump_str();
17− p(&cx).children([text(msg)]).finish()
18− }
19−}
20−
21−#[wasm_bindgen(start)]
22−pub fn run() {
23− // Set up the panic hook for debugging when things go wrong.
24− console_error_panic_hook::set_once();
25−
26− // Grab the document's `<body>`.
27− let window = web_sys::window().unwrap_throw();
28− let document = window.document().unwrap_throw();
29− let body = document.body().unwrap_throw();
30−
31− // Create a new `Hello` render component.
32− let component = Hello {
33− who: String::from("World"),
34− };
35−
36− // Create a virtual DOM and mount it and the `Hello` render component to the
37− // `<body>`.
38− let vdom = Vdom::new(body.as_ref(), component);
39−
40− // Run the virtual DOM forever and don't unmount it.
41− vdom.forget();
42−}
dodrio/examples/input-form/Cargo.tomldeleted−32
@@ -1,32 +0,0 @@
1−[package]
2−name = "dodrio-input-form"
3−version = "0.1.0"
4−authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
5−edition = "2018"
6−
7−[lib]
8−crate-type = ["cdylib"]
9−
10−[features]
11−
12−[dependencies]
13−dodrio = { path = "../.." }
14−wasm-bindgen = "0.2.55"
15−console_error_panic_hook = "0.1.6"
16−
17−[dependencies.web-sys]
18−version = "0.3.32"
19−features = [
20− "console",
21− "Document",
22− "Event",
23− "EventTarget",
24− "HtmlElement",
25− "HtmlInputElement",
26− "MouseEvent",
27− "Node",
28− "Window",
29−]
30−
31−[dev-dependencies]
32−wasm-bindgen-test = "0.3.5"
dodrio/examples/input-form/README.mddeleted−22
@@ -1,22 +0,0 @@
1−# Input Forms
2−
3−Renders a greeting to a user-supplied person via an `<input>` value. The
4−greeting is live-updated with new inputs.
5−
6−## Source
7−
8−See `src/lib.rs`.
9−
10−## Build
11−
12−```
13−wasm-pack build --target web
14−```
15−
16−## Serve
17−
18−Use any HTTP server, for example:
19−
20−```
21−python -m SimpleHTTPServer
22−```
dodrio/examples/input-form/index.htmldeleted−13
@@ -1,13 +0,0 @@
1−<!DOCTYPE html>
2−<html>
3− <head>
4− <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
5− <title>Input Form</title>
6− </head>
7− <body>
8− <script type="module">
9− import init from "./pkg/dodrio_input_form.js";
10− init("./pkg/dodrio_input_form_bg.wasm");
11− </script>
12− </body>
13−</html>
dodrio/examples/input-form/src/lib.rsdeleted−81
@@ -1,81 +0,0 @@
1−use dodrio::{bumpalo, Node, Render, RenderContext};
2−use wasm_bindgen::prelude::*;
3−use wasm_bindgen::JsCast;
4−
5−/// Say hello to someone.
6−struct SayHelloTo {
7− /// Who to say hello to.
8− who: String,
9−}
10−
11−impl SayHelloTo {
12− /// Construct a new `SayHelloTo` component.
13− fn new<S: Into<String>>(who: S) -> SayHelloTo {
14− let who = who.into();
15− SayHelloTo { who }
16− }
17−
18− /// Update who to say hello to.
19− fn set_who(&mut self, who: String) {
20− self.who = who;
21− }
22−}
23−
24−// The `Render` implementation has a text `<input>` and a `<div>` that shows a
25−// greeting to the `<input>`'s value.
26−impl<'a> Render<'a> for SayHelloTo {
27− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
28− use dodrio::builder::*;
29−
30− div(&cx)
31− .children([
32− input(&cx)
33− .attr("type", "text")
34− .attr(
35− "value",
36− bumpalo::format!(in cx.bump, "{}", self.who).into_bump_str(),
37− )
38− .on("input", |root, vdom, event| {
39− // If the event's target is our input...
40− let input = match event
41− .target()
42− .and_then(|t| t.dyn_into::<web_sys::HtmlInputElement>().ok())
43− {
44− None => return,
45− Some(input) => input,
46− };
47−
48− // ...then get its value and update who we are greeting.
49− let value = input.value();
50− let hello = root.unwrap_mut::<SayHelloTo>();
51− hello.set_who(value);
52−
53− // Finally, re-render the component on the next animation frame.
54− vdom.schedule_render();
55− })
56− .finish(),
57− text(bumpalo::format!(in cx.bump, "Hello, {}!", self.who).into_bump_str()),
58− ])
59− .finish()
60− }
61−}
62−
63−#[wasm_bindgen(start)]
64−pub fn run() {
65− // Initialize debugging for when/if something goes wrong.
66− console_error_panic_hook::set_once();
67−
68− // Get the document's `<body>`.
69− let window = web_sys::window().unwrap();
70− let document = window.document().unwrap();
71− let body = document.body().unwrap();
72−
73− // Construct a new `SayHelloTo` rendering component.
74− let say_hello = SayHelloTo::new("World");
75−
76− // Mount the component to the `<body>`.
77− let vdom = dodrio::Vdom::new(&body, say_hello);
78−
79− // Run the component forever.
80− vdom.forget();
81−}
dodrio/examples/js-component/Cargo.tomldeleted−26
@@ -1,26 +0,0 @@
1−[package]
2−name = "dodrio-js-component"
3−version = "0.1.0"
4−authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
5−edition = "2018"
6−
7−[lib]
8−crate-type = ["cdylib"]
9−
10−[features]
11−
12−[dependencies]
13−dodrio = { path = "../.." }
14−dodrio-js-api = { path = "../../crates/js-api" }
15−wasm-bindgen = "0.2.55"
16−console_error_panic_hook = "0.1.6"
17−js-sys = "0.3.32"
18−
19−[dependencies.web-sys]
20−version = "0.3.32"
21−features = [
22− "Document",
23− "HtmlElement",
24− "Node",
25− "Window",
26−]
dodrio/examples/js-component/README.mddeleted−23
@@ -1,23 +0,0 @@
1−# Defining a Rendering Component in JavaScript
2−
3−Heterogeneous Rust and JavaScript rendering component trees!
4−
5−## Source
6−
7−See `greeting.js` for the JavaScript code.
8−
9−See `src/lib.rs` for the Rust code.
10−
11−## Build
12−
13−```
14−wasm-pack build --target web
15−```
16−
17−## Serve
18−
19−Use any HTTP server, for example:
20−
21−```
22−python -m SimpleHTTPServer
23−```
dodrio/examples/js-component/greeting.jsdeleted−41
@@ -1,41 +0,0 @@
1−// A rendering component defined in JavaScript!
2−class Greeting {
3− constructor(who) {
4− this.who = who;
5− }
6−
7− render() {
8− return {
9− tagName: "p",
10− attributes: [
11− {
12− name: "class",
13− value: "greeting",
14− },
15− ],
16− listeners: [
17− {
18− on: "click",
19− callback: this.onClick.bind(this),
20− }
21− ],
22− children: [
23− "Hello, ",
24− {
25− tagName: "strong",
26− children: [this.who],
27− }
28− ],
29− };
30− }
31−
32− async onClick(vdom, event) {
33− // Be more excited!
34− this.who += "!";
35−
36− // Schedule a re-render.
37− await vdom.render();
38−
39− console.log("re-rendering finished!");
40− }
41−}
dodrio/examples/js-component/index.htmldeleted−14
@@ -1,14 +0,0 @@
1−<!DOCTYPE html>
2−<html>
3− <head>
4− <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
5− <title>A JavaScript component!</title>
6− </head>
7− <body>
8− <script src="greeting.js"></script>
9− <script type="module">
10− import init from "./pkg/dodrio_js_component.js";
11− init("./pkg/dodrio_js_component_bg.wasm");
12− </script>
13− </body>
14−</html>
dodrio/examples/js-component/src/lib.rsdeleted−63
@@ -1,63 +0,0 @@
1−use dodrio::{Node, Render, RenderContext, Vdom};
2−use dodrio_js_api::JsRender;
3−use js_sys::Object;
4−use wasm_bindgen::prelude::*;
5−
6−#[wasm_bindgen]
7−extern "C" {
8− // Import the JS `Greeting` class.
9− #[wasm_bindgen(extends = Object)]
10− #[derive(Clone, Debug)]
11− type Greeting;
12−
13− // And the `Greeting` class's constructor.
14− #[wasm_bindgen(constructor)]
15− fn new(who: &str) -> Greeting;
16−}
17−
18−/// This is our Rust rendering component that wraps the JS rendering component.
19−pub struct GreetingViaJs {
20− js: JsRender,
21−}
22−
23−impl GreetingViaJs {
24− /// Create a new `GreetingViaJs`, which will internally create a new JS
25− /// `Greeting`.
26− pub fn new(who: &str) -> GreetingViaJs {
27− let js = JsRender::new(Greeting::new(who));
28− GreetingViaJs { js }
29− }
30−}
31−
32−/// Here's the `Render` implementation! This adds a `<p>` element and some text
33−/// around whatever the inner JS `Greeting` component renders.
34−impl<'a> Render<'a> for GreetingViaJs {
35− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
36− use dodrio::builder::*;
37− p(&cx)
38− .children([text("JavaScript says: "), self.js.render(cx)])
39− .finish()
40− }
41−}
42−
43−/// Finally our main initialization function that kicks everything off!
44−#[wasm_bindgen(start)]
45−pub fn run() {
46− // Set up the panic hook for debugging when things go wrong.
47− console_error_panic_hook::set_once();
48−
49− // Grab the document's `<body>`.
50− let window = web_sys::window().unwrap_throw();
51− let document = window.document().unwrap_throw();
52− let body = document.body().unwrap_throw();
53−
54− // Create a new `GreetingViaJs` render component.
55− let component = GreetingViaJs::new("World");
56−
57− // Create a virtual DOM and mount it and the `GreetingViaJs` render component to the
58− // `<body>`.
59− let vdom = Vdom::new(body.as_ref(), component);
60−
61− // Run the virtual DOM forever and don't unmount it.
62− vdom.forget();
63−}
dodrio/examples/moire/Cargo.tomldeleted−43
@@ -1,43 +0,0 @@
1−[package]
2−name = "dodrio-moire"
3−version = "0.1.0"
4−authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
5−edition = "2018"
6−
7−[lib]
8−crate-type = ["cdylib"]
9−
10−[dependencies]
11−cfg-if = "0.1.10"
12−dodrio = { path = "../.." }
13−log = "0.4.11"
14−wasm-bindgen = "0.2.55"
15−wasm-bindgen-futures = "0.4.5"
16−
17−# Optional dependencies for logging.
18−console_error_panic_hook = { optional = true, version = "0.1.6" }
19−console_log = { optional = true, version = "0.2.0" }
20−js-sys = "0.3.32"
21−
22−[dependencies.web-sys]
23−version = "0.3.32"
24−features = [
25− "Document",
26− "Element",
27− "Event",
28− "EventTarget",
29− "HtmlElement",
30− "HtmlInputElement",
31− "Node",
32− "Performance",
33− "Window",
34−]
35−
36−[dev-dependencies]
37−wasm-bindgen-test = "0.3.5"
38−
39−[features]
40−logging = [
41− "console_log",
42− "console_error_panic_hook",
43−]
dodrio/examples/moire/README.mddeleted−26
@@ -1,26 +0,0 @@
1−# Moiré Patterns!
2−
3−This was originally a demo meant to show off [WebRender][], and so the
4−performance bottleneck is painting, not DOM manipulation, but it looks cool
5−either way!
6−
7−[WebRender]: https://hacks.mozilla.org/2017/10/the-whole-web-at-maximum-fps-how-webrender-gets-rid-of-jank/
8−
9−## Source
10−
11−See `src/lib.rs` for the main bits, and `src/colors.rs` for HSV to RGB color
12−conversion and other color utilities.
13−
14−## Build
15−
16−```
17−wasm-pack build --target web
18−```
19−
20−## Serve
21−
22−Use any HTTP server, for example:
23−
24−```
25−python -m SimpleHTTPServer
26−```
dodrio/examples/moire/index.htmldeleted−67
@@ -1,67 +0,0 @@
1−<!DOCTYPE html>
2−<html>
3− <head>
4− <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
5− <title>Moire patterns! Originally a WebRender demo</title>
6− <style>
7− main {
8− position: absolute;
9− top: 0;
10− left: 0;
11− right: 0;
12− bottom: 0;
13− overflow: hidden;
14− }
15− .circle {
16− border-radius: 50%;
17− position: absolute;
18− top: 50%;
19− left: 50%;
20− border-style: solid;
21− border-width: 3px;
22− }
23− .object {
24− position: absolute;
25− width: 100%;
26− height: 100%;
27− left: 0;
28− top: 0;
29− }
30− code {
31− position: relative;
32− font-size: 150%;
33− z-index: 10;
34− }
35− #circle-count {
36− position: absolute;
37− margin-top: 30px;
38− z-index: 2;
39− }
40− </style>
41− </head>
42− <body>
43− <code>FPS: <output id="fps"></code></code>
44− <div id="scene"></div>
45− <script type="module">
46− import init from "./pkg/dodrio_moire.js";
47− init("./pkg/dodrio_moire_bg.wasm");
48− </script>
49− <script>
50− let lastTime = 0
51− let frameCount = 0
52− const fps = document.getElementById('fps')
53− function updateFPS(time) {
54− frameCount++
55− if (lastTime + 1000.0 <= time) {
56− fps.textContent = frameCount
57− lastTime = time
58− frameCount = 0
59− }
60− }
61− requestAnimationFrame(function loop(now) {
62− updateFPS(now);
63− requestAnimationFrame(loop);
64− })
65− </script>
66− </body>
67−</html>
dodrio/examples/moire/src/colors.rsdeleted−162
@@ -1,162 +0,0 @@
1−//! Color utility functions.
2−
3−use std::f64;
4−
5−/// An RGB color.
6−#[derive(Copy, Clone)]
7−pub struct Rgb {
8− pub r: u8,
9− pub g: u8,
10− pub b: u8,
11−}
12−
13−/// An HSV color.
14−#[derive(Copy, Clone)]
15−pub struct Hsv {
16− pub h: f64,
17− pub s: f64,
18− pub v: f64,
19−}
20−
21−/// Convert an RGB color into HSV.
22−impl From<Rgb> for Hsv {
23− fn from(Rgb { r, g, b }: Rgb) -> Hsv {
24− let r = r as f64 / 255.0;
25− let g = g as f64 / 255.0;
26− let b = b as f64 / 255.0;
27− let max = f64::max(f64::max(r, g), b);
28− let min = f64::min(f64::min(r, g), b);
29− let v = max;
30− let diff = v - min;
31− let mut h = 0.0;
32− let mut s = 0.0;
33− if diff != 0.0 {
34− s = diff / v;
35− let diffc = |c| (v - c) / 6.0 / diff + 1.0 / 2.0;
36− let rr = diffc(r);
37− let gg = diffc(g);
38− let bb = diffc(b);
39− if r == v {
40− h = bb - gg;
41− } else if g == v {
42− h = (1.0 / 3.0) + rr - bb;
43− } else if b == v {
44− h = (2.0 / 3.0) + gg - rr;
45− }
46− }
47− Hsv { h, s, v }
48− }
49−}
50−
51−/// Convert an HSV color into RGB.
52−impl From<Hsv> for Rgb {
53− fn from(Hsv { h, s, v }: Hsv) -> Rgb {
54− let i = (h * 6.0).floor();
55− let f = h * 6.0 - i;
56− let p = v * (1.0 - s);
57− let q = v * (1.0 - f * s);
58− let t = v * (1.0 - (1.0 - f) * s);
59− let r;
60− let g;
61− let b;
62− match i as u64 % 6 {
63− 0 => {
64− r = v;
65− g = t;
66− b = p;
67− }
68− 1 => {
69− r = q;
70− g = v;
71− b = p;
72− }
73− 2 => {
74− r = p;
75− g = v;
76− b = t;
77− }
78− 3 => {
79− r = p;
80− g = q;
81− b = v;
82− }
83− 4 => {
84− r = t;
85− g = p;
86− b = v;
87− }
88− 5 => {
89− r = v;
90− g = p;
91− b = q;
92− }
93− _ => wasm_bindgen::throw_str("impossible"),
94− }
95− let r = (r * 255.0).round() as u8;
96− let g = (g * 255.0).round() as u8;
97− let b = (b * 255.0).round() as u8;
98− Rgb { r, g, b }
99− }
100−}
101−
102−/// Linearly interpolate between two values.
103−fn lerp(a: f64, b: f64, t: f64) -> f64 {
104− a + (b - a) * t
105−}
106−
107−/// Clamp the `x` value to within the range `lo..hi`.
108−fn clamp(lo: f64, hi: f64, x: f64) -> f64 {
109− if x < lo {
110− lo
111− } else if x > hi {
112− hi
113− } else {
114− x
115− }
116−}
117−
118−/// Linearly interpolate two HSV colors and convert the result into RGB.
119−fn lerp_hsv_to_rgb(a: Hsv, b: Hsv, t: f64) -> Rgb {
120− let h = lerp(a.h, b.h, t);
121− let s = lerp(a.s, b.s, t);
122− let v = lerp(a.v, b.v, t);
123− let hsv = Hsv { h, s, v };
124− hsv.into()
125−}
126−
127−thread_local! {
128− static PALETTES: [(Hsv, Hsv); 3] = [
129− (
130− (Rgb { r: 0xfe, g: 0xf6, b: 0xdf }).into(),
131− (Rgb { r: 0x64, g: 0xc3, b: 0xc8 }).into(),
132− ),
133− (
134− (Rgb { r: 0xfc, g: 0xcf, b: 0x74 }).into(),
135− (Rgb { r: 0x41, g: 0x65, b: 0x7f }).into(),
136− ),
137− (
138− (Rgb { r: 0xd9, g: 0xe0, b: 0xe8 }).into(),
139− (Rgb { r: 0x94, g: 0x5d, b: 0x72 }).into(),
140− ),
141− ];
142−}
143−
144−/// Get a color from our color palette that is linearly interpolated based on
145−/// the `t` elapsed time value.
146−///
147−/// The `which` function allows callers to select whether they want the
148−/// foreground or background color.
149−pub fn get_interpolated_color<F>(mut which: F, t: f64) -> Rgb
150−where
151− F: FnMut((Hsv, Hsv)) -> Hsv,
152−{
153− PALETTES.with(|palettes| {
154− let t = t * (palettes.len() as f64);
155− let prev_index = t.floor() as usize % palettes.len();
156− let next_index = (prev_index + 1) % palettes.len();
157− let t = clamp(-0.5, 0.5, (t % 1.0 - 0.5) * 5.0) + 0.5;
158− let a = which(palettes[prev_index]);
159− let b = which(palettes[next_index]);
160− lerp_hsv_to_rgb(a, b, t)
161− })
162−}
dodrio/examples/moire/src/lib.rsdeleted−259
@@ -1,259 +0,0 @@
1−mod colors;
2−
3−use cfg_if::cfg_if;
4−use dodrio::{bumpalo, Node, Render, RenderContext, Vdom};
5−use std::cell::RefCell;
6−use std::rc::Rc;
7−use std::str;
8−use wasm_bindgen::{prelude::*, JsCast};
9−
10−/// This is the main function that is automatically invoked when the wasm module
11−/// is loaded.
12−#[wasm_bindgen(start)]
13−pub fn run() {
14− // Set up the panic hook for debugging when things go wrong.
15− init_logging();
16−
17− // Grab the document's `<body>`.
18− let window = web_sys::window().unwrap_throw();
19− let document = window.document().unwrap_throw();
20− let scene = document.get_element_by_id("scene").unwrap_throw();
21−
22− // Create a new `Moire` render component.
23− let component = Moire::new();
24−
25− // Create a virtual DOM and mount it and the `Moire` render component to the
26− // scene.
27− let vdom = Vdom::new(&scene, component);
28−
29− // Kick off a loop that keeps re-rendering on every animation frame.
30− let rc = <Rc<RefCell<Option<Closure<dyn FnMut()>>>>>::default();
31− let rc2 = rc.clone();
32− let window2 = window.clone();
33− let weak = vdom.weak();
34− let f = Closure::wrap(Box::new(move || {
35− weak.schedule_render();
36− window
37− .request_animation_frame(
38− rc.borrow()
39− .as_ref()
40− .unwrap_throw()
41− .as_ref()
42− .unchecked_ref::<js_sys::Function>(),
43− )
44− .unwrap_throw();
45− }) as Box<dyn FnMut()>);
46− window2
47− .request_animation_frame(f.as_ref().unchecked_ref::<js_sys::Function>())
48− .unwrap_throw();
49− *rc2.borrow_mut() = Some(f);
50−
51− // Run the virtual DOM forever and don't unmount it.
52− vdom.forget();
53−}
54−
55−cfg_if! {
56− if #[cfg(feature = "logging")] {
57− fn init_logging() {
58− console_error_panic_hook::set_once();
59− console_log::init_with_level(log::Level::Trace)
60− .expect_throw("should initialize logging OK");
61− }
62− } else {
63− fn init_logging() {
64− // Do nothing.
65− }
66− }
67−}
68−
69−/// A rendering component that renders two overlapping sets of concentric
70−/// circles that move around and form Moiré patterns.
71−///
72−/// https://en.wikipedia.org/wiki/Moir%C3%A9_pattern
73−pub struct Moire {
74− // How many circles to render in each of our sets. This is controlled by the
75− // `<input>` slider.
76− count: u32,
77−}
78−
79−impl Moire {
80− /// Construct a new `Moire` rendering component.
81− ///
82− /// Defaults to 200 circles.
83− pub fn new() -> Moire {
84− Moire { count: 200 }
85− }
86−
87− /// Callback for "change" events emitted on the `<input>` slider that
88− /// controls how many circles we render on each frame.
89− fn on_change(&mut self, event: web_sys::Event) {
90− // Get the `<input>` element.
91− let input = event
92− .target()
93− .unwrap_throw()
94− .unchecked_into::<web_sys::HtmlInputElement>();
95−
96− // Parse its new value.
97− let value = input.value();
98− let value: u32 = value
99− .parse()
100− .expect_throw("<input type=\"range\"> value should always be an integer");
101−
102− // And update our circle count!
103− self.count = value;
104− }
105−}
106−
107−impl<'a> Render<'a> for Moire {
108− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
109− use dodrio::builder::*;
110−
111− let elapsed = web_sys::window()
112− .unwrap_throw()
113− .performance()
114− .unwrap_throw()
115− .now();
116− let elapsed = elapsed / 1600.0;
117−
118− main(&cx)
119− .attr("style", self.moire_style(cx, elapsed))
120− .children([
121− // The `<input>` that lets users control how many circles to
122− // render.
123− input(&cx)
124− .attr("id", "circle-count")
125− .attr("type", "range")
126− .attr("min", "30")
127− .attr("max", "500")
128− .attr("value", {
129− let count = bumpalo::format!(in cx.bump, "{}", self.count);
130− count.into_bump_str()
131− })
132− .on("change", |root, _vdom, event| {
133− root.unwrap_mut::<Moire>().on_change(event);
134−
135− // Note: there is no need to manually schedule a
136− // re-render here, since we already started a
137− // `requestAnimationFrame` loop that automatically
138− // re-renders on every frame in the `run` function.
139− })
140− .finish(),
141− // And the actual concentric circles that form the Moire patterns.
142− self.orbiting_objects(cx, elapsed),
143− self.lemniscate_objects(cx, elapsed),
144− ])
145− .finish()
146− }
147−}
148−
149−/// Rendering helper methods.
150−impl Moire {
151− /// Generate the main Moire element's inline CSS styles.
152− fn moire_style<'a>(&self, cx: &mut RenderContext<'a>, elapsed: f64) -> &'a str {
153− let elapsed = elapsed / 3.0;
154− let color = colors::get_interpolated_color(|(_, bg)| bg, elapsed % 1.0);
155− let style = bumpalo::format!(
156− in cx.bump,
157− "background-color: rgb({}, {}, {})",
158− color.r,
159− color.g,
160− color.b
161− );
162− style.into_bump_str()
163− }
164−
165− /// Generate the orbiting circle objects.
166− fn orbiting_objects<'a>(&self, cx: &mut RenderContext<'a>, elapsed: f64) -> Node<'a> {
167− const D: f64 = 200.0;
168− let x = elapsed.sin() * D;
169− let y = elapsed.cos() * D;
170− self.moving_object(cx, elapsed, x, y)
171− }
172−
173− /// Generate the lemniscate circle objects that interact with the orbiting
174− /// circle objects to form Moire patterns.
175− fn lemniscate_objects<'a>(&self, cx: &mut RenderContext<'a>, elapsed: f64) -> Node<'a> {
176− const A: f64 = 150.0;
177− let x = elapsed.sin() * A;
178− let y = elapsed.sin() * elapsed.cos() * A;
179− self.moving_object(cx, elapsed, x, y)
180− }
181−
182− fn moving_object<'a>(
183− &self,
184− cx: &mut RenderContext<'a>,
185− elapsed: f64,
186− x: f64,
187− y: f64,
188− ) -> Node<'a> {
189− use dodrio::builder::*;
190− div(&cx)
191− .attr("class", "object")
192− .attr("style", self.moving_object_style(cx, x, y))
193− .children([self.circle(cx, elapsed, self.count)])
194− .finish()
195− }
196−
197− /// Generate inline CSS styles for a moving object.
198− fn moving_object_style<'a>(&self, cx: &mut RenderContext<'a>, x: f64, y: f64) -> &'a str {
199− bumpalo::format!(in cx.bump, "left: {}px; top: {}px;", x, y).into_bump_str()
200− }
201−
202− /// Recursively generate `self.count` concentric circles.
203− fn circle<'a>(&self, cx: &mut RenderContext<'a>, elapsed: f64, n: u32) -> Node<'a> {
204− use dodrio::builder::*;
205−
206− let r = n * 16;
207−
208− let mut circle = div(&cx)
209− .attr("class", "circle")
210− .attr("data-radius", {
211− let r = bumpalo::format!(in cx.bump, "{}", r);
212− r.into_bump_str()
213− })
214− .attr("style", self.circle_style(cx, elapsed, n, r));
215−
216− if n > 0 {
217− circle = circle.child(self.circle(cx, elapsed, n - 1));
218− }
219−
220− circle.finish()
221− }
222−
223− /// Generate inline CSS styles for a circle.
224− fn circle_style<'a>(
225− &self,
226− cx: &mut RenderContext<'a>,
227− elapsed: f64,
228− n: u32,
229− r: u32,
230− ) -> &'a str {
231− let (border, alpha) = self.circle_color(elapsed, n);
232− let margin = r / 2 + 3;
233− let style = bumpalo::format!(
234− in cx.bump,
235− "border-color: rgba({}, {}, {}, {});\
236− margin-left: -{}px;\
237− margin-top: -{}px;\
238− width: {}px;\
239− height: {}px;",
240− border.r,
241− border.g,
242− border.b,
243− alpha,
244− margin,
245− margin,
246− r,
247− r
248− );
249− style.into_bump_str()
250− }
251−
252− /// Compute the color for a circle.
253− fn circle_color(&self, elapsed: f64, n: u32) -> (colors::Rgb, f64) {
254− let elapsed = elapsed / 3.0;
255− let base = colors::get_interpolated_color(|(fg, _)| fg, elapsed % 1.0);
256− let lightness = 1.0 - (n as f64) / (self.count as f64);
257− (base, lightness)
258− }
259−}
dodrio/examples/sierpinski-triangle/Cargo.tomldeleted−28
@@ -1,28 +0,0 @@
1−[package]
2−name = "dodrio-sierpinski"
3−version = "0.1.0"
4−authors = ["Steven Seguin <steven.seguin@gmail.com>"]
5−edition = "2018"
6−
7−[lib]
8−crate-type = ["cdylib"]
9−
10−[dependencies]
11−dodrio = { path = "../.." }
12−wasm-bindgen = "0.2.55"
13−console_error_panic_hook = "0.1.6"
14−js-sys = "0.3.32"
15−wasm-bindgen-futures = "0.4.5"
16−
17−[dependencies.web-sys]
18−version = "0.3.32"
19−features = [
20− "Document",
21− "HtmlElement",
22− "Node",
23− "Performance",
24− "Window",
25−]
26−
27−[dev-dependencies]
28−wasm-bindgen-test = "0.3.5"
dodrio/examples/sierpinski-triangle/README.mddeleted−24
@@ -1,24 +0,0 @@
1−# Sierpinski Triangle
2−
3−A Sierpinkski triangle that constantly shrinks and grows, and whose nodes have
4−a value that increments by one every second.
5−
6−Ported from the React Fiber example [here](https://github.com/claudiopro/react-fiber-vs-stack-demo).
7−
8−## Source
9−
10−See `src/lib.rs`.
11−
12−## Build
13−
14−```
15−wasm-pack build --target web
16−```
17−
18−## Serve
19−
20−Use any HTTP server, for example:
21−
22−```
23−python -m SimpleHTTPServer
24−```
dodrio/examples/sierpinski-triangle/ferris.pngdeleted

Binary file not shown.

dodrio/examples/sierpinski-triangle/index.htmldeleted−69
@@ -1,69 +0,0 @@
1−<!DOCTYPE html>
2−<html>
3−
4−<head>
5− <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
6− <title>Sierpinski Triangle</title>
7− <style>
8− .container {
9− position: absolute;
10− transform-origin: 0 0;
11− left: 50%;
12− top: 50%;
13− width: 10px;
14− height: 10px;
15− background: #eee;
16− }
17−
18− .dot {
19− position: absolute;
20− background: #61dafb;
21− font: normal 15px sans-serif;
22− text-align: center;
23− cursor: pointer;
24− }
25−
26− .dot:hover {
27− background: url("ferris.png") no-repeat center center;
28− z-index: 999;
29− }
30−
31− code {
32− position: relative;
33− font-size: 150%;
34− }
35− </style>
36−</head>
37−
38−<body>
39− <code>FPS: <output id="fps">...</output></code>
40− <div id="scene"></div>
41−
42− <script type="module">
43− import init from "./pkg/dodrio_sierpinski.js";
44− init("./pkg/dodrio_sierpinski_bg.wasm");
45− </script>
46−
47− <script>
48− let lastTime = 0;
49− let frameCount = 0;
50− const fps = document.getElementById('fps');
51−
52− function updateFPS(time) {
53− frameCount++;
54− if (lastTime + 1000.0 <= time) {
55− fps.textContent = frameCount;
56− lastTime = time;
57− frameCount = 0;
58− }
59− }
60−
61− requestAnimationFrame(function loop(now) {
62− updateFPS(now);
63− requestAnimationFrame(loop);
64− })
65−
66− </script>
67−</body>
68−
69−</html>
dodrio/examples/sierpinski-triangle/src/lib.rsdeleted−157
@@ -1,157 +0,0 @@
1−use dodrio::{bumpalo, Node, Render, RenderContext, Vdom};
2−use std::cell::RefCell;
3−use std::rc::Rc;
4−use wasm_bindgen::{prelude::*, JsCast};
5−use web_sys::Window;
6−
7−// Triangle container defining target size.
8−struct Container {
9− size: f64,
10−}
11−
12−impl Container {
13− // Construct a new container.
14− pub fn new() -> Container {
15− Container { size: 25.0 }
16− }
17−
18− // Generate the container style to fluctuate triangle width.
19− fn container_transform<'a>(&self, cx: &mut RenderContext<'a>, elapsed: f64) -> &'a str {
20− let t = elapsed % 10.0;
21− let scale = 0.45 + (if t > 5.0 { 10.0 - t } else { t }) / 40.0;
22− let transform = bumpalo::format!(
23− in cx.bump,
24− "transform: scaleX({}) scaleY(0.7) translateZ(0.1px)",
25− scale
26− );
27− transform.into_bump_str()
28− }
29−
30− // Generate the dot's position on the grid.
31− fn dot_style<'a>(&self, cx: &mut RenderContext<'a>, x: f64, y: f64) -> &'a str {
32− let s = self.size * 1.3;
33− let radius = s / 2.0;
34− let styles = bumpalo::format!(
35− in cx.bump,
36− r#"
37− width: {}px;
38− height: {}px;
39− left: {}px;
40− top: {}px;
41− border-radius: {}px;
42− line-height: {}px;
43− "#,
44− s, s, x, y, radius, s
45− );
46− styles.into_bump_str()
47− }
48−
49− // Create a dot node.
50− fn dot<'a>(&self, cx: &mut RenderContext<'a>, x: f64, y: f64, content: u32) -> Node<'a> {
51− use dodrio::builder::{div, text};
52−
53− div(&cx)
54− .attr("class", "dot")
55− .attr("style", self.dot_style(cx, x, y))
56− .child(text(
57− bumpalo::format!(in cx.bump, "{}", content).into_bump_str(),
58− ))
59− .finish()
60− }
61−
62− // Create a flattened vector of dot nodes (arranged in recursive triangles)
63− fn triangle<'a>(
64− &self,
65− cx: &mut RenderContext<'a>,
66− x: f64,
67− y: f64,
68− s: f64,
69− content: u32,
70− children: &mut bumpalo::collections::Vec<Node<'a>>,
71− ) {
72− if s <= self.size {
73− children.push(self.dot(cx, x, y, content));
74− return;
75− }
76−
77− let s = s / 2.0;
78−
79− // Top of triangle
80− self.triangle(cx, x, y - (s / 2.0), s, content, children);
81− // Bottom-left of triangle
82− self.triangle(cx, x - s, y + (s / 2.0), s, content, children);
83− // Bottom-right of triangle
84− self.triangle(cx, x + s, y + (s / 2.0), s, content, children);
85− }
86−}
87−
88−impl<'a> Render<'a> for Container {
89− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
90− use dodrio::builder::div;
91−
92− let elapsed = web_sys::window()
93− .unwrap_throw()
94− .performance()
95− .unwrap_throw()
96− .now()
97− / 1000.0;
98−
99− let modulus = elapsed as u32 % 10;
100−
101− let mut children = bumpalo::collections::Vec::new_in(cx.bump);
102− self.triangle(cx, 0.0, 0.0, 1000.0, modulus, &mut children);
103−
104− div(&cx)
105− .attr("class", "container")
106− .attr("style", self.container_transform(cx, elapsed))
107− .children(children)
108− .finish()
109− }
110−}
111−
112−// Kick off a loop that keeps re-rendering on every animation frame.
113−fn animate(window: Window, vdom: Vdom) {
114− let rc = <Rc<RefCell<Option<Closure<dyn FnMut()>>>>>::default();
115− let rc2 = rc.clone();
116− let window2 = window.clone();
117− let weak = vdom.weak();
118− let f = Closure::wrap(Box::new(move || {
119− weak.schedule_render();
120− window
121− .request_animation_frame(
122− rc.borrow()
123− .as_ref()
124− .unwrap_throw()
125− .as_ref()
126− .unchecked_ref::<js_sys::Function>(),
127− )
128− .unwrap_throw();
129− }) as Box<dyn FnMut()>);
130− window2
131− .request_animation_frame(f.as_ref().unchecked_ref::<js_sys::Function>())
132− .unwrap_throw();
133− *rc2.borrow_mut() = Some(f);
134−
135− // Run the virtual DOM and its listeners forever.
136− vdom.forget();
137−}
138−
139−#[wasm_bindgen(start)]
140−pub fn run() {
141− // Set up the panic hook
142− console_error_panic_hook::set_once();
143−
144− // Get the scene element to render within
145− let window = web_sys::window().unwrap();
146− let document = window.document().unwrap();
147− let scene = document.get_element_by_id("scene").unwrap();
148−
149− // Construct a new Container component.
150− let container = Container::new();
151−
152− // Mount our container component to the scene div.
153− let vdom = Vdom::new(&scene, container);
154−
155− // Kick off animation loop
156− animate(window, vdom);
157−}
dodrio/examples/todomvc/Cargo.tomldeleted−47
@@ -1,47 +0,0 @@
1−[package]
2−authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
3−edition = "2018"
4−name = "dodrio-todomvc"
5−version = "0.1.0"
6−
7−[dependencies]
8−cfg-if = "0.1.10"
9−dodrio = { path = "../..", features = ["serde"] }
10−js-sys = "0.3.32"
11−serde = { features = ["derive"], version = "1.0.114" }
12−serde_json = "1.0.57"
13−wasm-bindgen = "0.2.55"
14−wasm-bindgen-futures = "0.4.5"
15−
16−# Optional dependencies for logging.
17−console_error_panic_hook = { optional = true, version = "0.1.6" }
18−console_log = { optional = true, version = "0.2.0" }
19−log = { optional = true, version = "0.4.11" }
20−
21−[dependencies.web-sys]
22−version = "0.3.32"
23−features = [
24− "Document",
25− "Event",
26− "EventTarget",
27− "HtmlElement",
28− "HtmlInputElement",
29− "KeyboardEvent",
30− "Location",
31− "Storage",
32− "Node",
33− "Window",
34−]
35−
36−[dev-dependencies]
37−wasm-bindgen-test = "0.3.5"
38−
39−[features]
40−logging = [
41− "console_log",
42− "console_error_panic_hook",
43− "log",
44−]
45−
46−[lib]
47−crate-type = ["cdylib"]
dodrio/examples/todomvc/README.mddeleted−33
@@ -1,33 +0,0 @@
1−# TodoMVC
2−
3−`dodrio` implementation of the popular [TodoMVC](http://todomvc.com/) app. It
4−correctly and completely fulfills [the
5−specification](https://github.com/tastejs/todomvc/blob/master/app-spec.md) to
6−the best of my knowledge.
7−
8−## Source
9−
10−There are a number of modules in this `dodrio` implementation of TodoMVC. The
11−most important are:
12−
13−* `src/lib.rs`: The entry point to the application.
14−* `src/todos.rs`: Definition of `Todos` model and its rendering.
15−* `src/todo.rs`: Definition of `Todo` model and its rendering.
16−* `src/controller.rs`: The controller handles UI interactions and translates
17− them into updates on the model. Finally, it triggers re-rendering after those
18− updates.
19−* `src/router.rs`: A simple URL hash-based router.
20−
21−## Build
22−
23−```
24−wasm-pack build --target web
25−```
26−
27−## Serve
28−
29−Use any HTTP server, for example:
30−
31−```
32−python -m SimpleHTTPServer
33−```
dodrio/examples/todomvc/index.htmldeleted−21
@@ -1,21 +0,0 @@
1−<!doctype html>
2−<html lang="en">
3− <head>
4− <meta charset="utf-8">
5− <meta name="viewport" content="width=device-width, initial-scale=1">
6− <title>dodrio • TodoMVC</title>
7− <link rel="stylesheet" href="https://unpkg.com/todomvc-common@^1.0.0/base.css">
8− <link rel="stylesheet" href="https://unpkg.com/todomvc-app-css@^2.0.0/index.css">
9− </head>
10− <body>
11− <section class="todoapp">
12− </section>
13− <footer class="info">
14− <p>Double-click to edit a todo</p>
15− </footer>
16− <script type="module">
17− import init from "./pkg/dodrio_todomvc.js";
18− init("./pkg/dodrio_todomvc_bg.wasm");
19− </script>
20− </body>
21−</html>
dodrio/examples/todomvc/src/controller.rsdeleted−140
@@ -1,140 +0,0 @@
1−//! The controller handles UI events, translates them into updates on the model,
2−//! and schedules re-renders.
3−
4−use crate::todo::{Todo, TodoActions};
5−use crate::todos::{Todos, TodosActions};
6−use crate::visibility::Visibility;
7−use dodrio::{RootRender, VdomWeak};
8−use serde::{Deserialize, Serialize};
9−use std::ops::{Deref, DerefMut};
10−
11−/// The controller for the TodoMVC app.
12−///
13−/// This `Controller` struct is never actually instantiated. It is only used for
14−/// its `*Actions` trait implementations, none of which take a `self` parameter.
15−///
16−/// One could imagine alternative controller implementations with `*Actions`
17−/// trait implementations for (e.g.) testing that will assert various expected
18−/// action methods are called after rendering todo items and sending DOM events.
19−#[derive(Default, Deserialize, Serialize)]
20−pub struct Controller;
21−
22−impl TodosActions for Controller {
23− fn toggle_all(root: &mut dyn RootRender, vdom: VdomWeak) {
24− let mut todos = AutoCommitTodos::new(root, vdom);
25− let all_complete = todos.todos().iter().all(|t| t.is_complete());
26− for t in todos.todos_mut() {
27− t.set_complete(!all_complete);
28− }
29− }
30−
31− fn update_draft(root: &mut dyn RootRender, vdom: VdomWeak, draft: String) {
32− let mut todos = AutoCommitTodos::new(root, vdom);
33− todos.set_draft(draft);
34− }
35−
36− fn finish_draft(root: &mut dyn RootRender, vdom: VdomWeak) {
37− let mut todos = AutoCommitTodos::new(root, vdom);
38− let title = todos.take_draft();
39− let title = title.trim();
40− if !title.is_empty() {
41− let id = todos.todos().len();
42− let new = Todo::new(id, title);
43− todos.add_todo(new);
44− }
45− }
46−
47− fn change_visibility(root: &mut dyn RootRender, vdom: VdomWeak, vis: Visibility) {
48− let mut todos = AutoCommitTodos::new(root, vdom);
49− todos.set_visibility(vis);
50− }
51−
52− fn delete_completed(root: &mut dyn RootRender, vdom: VdomWeak) {
53− let mut todos = AutoCommitTodos::new(root, vdom);
54− todos.delete_completed();
55− }
56−}
57−
58−impl TodoActions for Controller {
59− fn toggle_completed(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
60− let mut todos = AutoCommitTodos::new(root, vdom);
61− let t = &mut todos.todos_mut()[id];
62− let completed = t.is_complete();
63− t.set_complete(!completed);
64− }
65−
66− fn delete(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
67− let mut todos = AutoCommitTodos::new(root, vdom);
68− todos.delete_todo(id);
69− }
70−
71− fn begin_editing(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
72− let mut todos = AutoCommitTodos::new(root, vdom);
73− let t = &mut todos.todos_mut()[id];
74− let desc = t.title().to_string();
75− t.set_edits(Some(desc));
76− }
77−
78− fn update_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize, edits: String) {
79− let mut todos = AutoCommitTodos::new(root, vdom);
80− let t = &mut todos.todos_mut()[id];
81− t.set_edits(Some(edits));
82− }
83−
84− fn finish_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
85− let mut todos = AutoCommitTodos::new(root, vdom);
86− let t = &mut todos.todos_mut()[id];
87− if let Some(edits) = t.take_edits() {
88− let edits = edits.trim();
89− if edits.is_empty() {
90− todos.delete_todo(id);
91− } else {
92− t.set_title(edits);
93− }
94− }
95− }
96−
97− fn cancel_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
98− let mut todos = AutoCommitTodos::new(root, vdom);
99− let t = &mut todos.todos_mut()[id];
100− let _ = t.take_edits();
101− }
102−}
103−
104−/// An RAII type that dereferences to a `Todos` and once it is dropped, saves
105−/// the (presumably just modified) todos to local storage, and schedules a new
106−/// `dodrio` render.
107−pub struct AutoCommitTodos<'a> {
108− todos: &'a mut Todos,
109− vdom: VdomWeak,
110−}
111−
112−impl AutoCommitTodos<'_> {
113− /// Construct a new `AutoCommitTodos` from the root rendering component and
114− /// `vdom` handle.
115− pub fn new(root: &mut dyn RootRender, vdom: VdomWeak) -> AutoCommitTodos {
116− let todos = root.unwrap_mut::<Todos>();
117− AutoCommitTodos { todos, vdom }
118− }
119−}
120−
121−impl Drop for AutoCommitTodos<'_> {
122− fn drop(&mut self) {
123− self.todos.save_to_local_storage();
124− self.vdom.schedule_render();
125− }
126−}
127−
128−impl Deref for AutoCommitTodos<'_> {
129− type Target = Todos;
130−
131− fn deref(&self) -> &Todos {
132− &self.todos
133− }
134−}
135−
136−impl DerefMut for AutoCommitTodos<'_> {
137− fn deref_mut(&mut self) -> &mut Todos {
138− &mut self.todos
139− }
140−}
dodrio/examples/todomvc/src/keys.rsdeleted−7
@@ -1,7 +0,0 @@
1−//! Constants for `KeyboardEvent::key_code`.`
2−
3−/// The key code for the enter key.
4−pub const ENTER: u32 = 13;
5−
6−/// The key code for the escape key.
7−pub const ESCAPE: u32 = 27;
dodrio/examples/todomvc/src/lib.rsdeleted−60
@@ -1,60 +0,0 @@
1−//! TodoMVC implemented in `dodrio`!
2−
3−#![deny(missing_docs)]
4−
5−pub mod controller;
6−pub mod keys;
7−pub mod router;
8−pub mod todo;
9−pub mod todos;
10−pub mod utils;
11−pub mod visibility;
12−
13−use crate::controller::Controller;
14−use crate::todos::Todos;
15−use dodrio::Vdom;
16−use wasm_bindgen::prelude::*;
17−
18−/// Run the TodoMVC app!
19−///
20−/// Since this is marked `#[wasm_bindgen(start)]` it is automatically invoked
21−/// once the wasm module instantiated on the Web page.
22−#[wasm_bindgen(start)]
23−pub fn run() -> Result<(), JsValue> {
24− // Set up the logging for debugging if/when things go wrong.
25− init_logging();
26−
27− // Grab the todo app container.
28− let document = utils::document();
29− let container = document
30− .query_selector(".todoapp")?
31− .ok_or_else(|| js_sys::Error::new("could not find `.todoapp` container"))?;
32−
33− // Create a new `Todos` render component.
34− let todos = Todos::<Controller>::new();
35−
36− // Create a virtual DOM and mount it and the `Todos` render component.
37− let vdom = Vdom::new(&container, todos);
38−
39− // Start the URL router.
40− router::start(vdom.weak());
41−
42− // Run the virtual DOM forever and don't unmount it.
43− vdom.forget();
44−
45− Ok(())
46−}
47−
48−cfg_if::cfg_if! {
49− if #[cfg(feature = "logging")] {
50− fn init_logging() {
51− console_error_panic_hook::set_once();
52− console_log::init_with_level(log::Level::Trace)
53− .expect_throw("should initialize logging OK");
54− }
55− } else {
56− fn init_logging() {
57− // Do nothing.
58− }
59− }
60−}
dodrio/examples/todomvc/src/router.rsdeleted−68
@@ -1,68 +0,0 @@
1−//! A simple `#`-fragment router.
2−
3−use crate::todos::Todos;
4−use crate::utils;
5−use crate::visibility::Visibility;
6−use dodrio::VdomWeak;
7−use std::str::FromStr;
8−use wasm_bindgen::{prelude::*, JsCast};
9−
10−/// Start the router.
11−pub fn start(vdom: VdomWeak) {
12− // Callback fired whenever the URL's hash fragment changes. Keeps the root
13− // todos collection's visibility in sync with the `#` fragment.
14− let on_hash_change = move || {
15− let new_vis = utils::hash()
16− .and_then(|hash| {
17− if hash.starts_with("#/") {
18− Visibility::from_str(&hash[2..]).ok()
19− } else {
20− None
21− }
22− })
23− .unwrap_or_else(|| {
24− // If we couldn't parse a visibility, make sure we canonicalize
25− // it back to our default hash.
26− let v = Visibility::default();
27− utils::set_hash(&format!("#/{}", v));
28− v
29− });
30−
31− wasm_bindgen_futures::spawn_local({
32− let vdom = vdom.clone();
33− async move {
34− let _ = vdom
35− .with_component({
36− let vdom = vdom.clone();
37− move |root| {
38− let todos = root.unwrap_mut::<Todos>();
39− // If the todos' visibility already matches the event's
40− // visibility, then there is nothing to do (ha). If they
41− // don't match, then we need to update the todos' visibility
42− // and re-render.
43− if todos.visibility() != new_vis {
44− todos.set_visibility(new_vis);
45− vdom.schedule_render();
46− }
47− }
48− })
49− .await;
50− }
51− });
52− };
53−
54− // Call it once to handle the initial `#` fragment.
55− on_hash_change();
56−
57− // Now listen for hash changes forever.
58− //
59− // Note that if we ever intended to unmount our todos app, we would want to
60− // provide a method for removing this router's event listener and cleaning
61− // up after ourselves.
62− let on_hash_change = Closure::wrap(Box::new(on_hash_change) as Box<dyn FnMut()>);
63− let window = utils::window();
64− window
65− .add_event_listener_with_callback("hashchange", on_hash_change.as_ref().unchecked_ref())
66− .unwrap_throw();
67− on_hash_change.forget();
68−}
dodrio/examples/todomvc/src/todo.rsdeleted−229
@@ -1,229 +0,0 @@
1−//! Type definition and `dodrio::Render` implementation for a single todo item.
2−
3−use crate::keys;
4−use dodrio::{Cached, Node, Render, RenderContext, RootRender, VdomWeak};
5−use serde::{Deserialize, Serialize};
6−use std::marker::PhantomData;
7−use wasm_bindgen::{prelude::*, JsCast};
8−
9−/// A single todo item.
10−#[derive(Default)]
11−pub struct Todo<C> {
12− inner: Cached<TodoInner<C>>,
13−}
14−
15−#[derive(Serialize, Deserialize)]
16−struct TodoInner<C> {
17− id: usize,
18− title: String,
19− completed: bool,
20−
21− #[serde(skip)]
22− edits: Option<String>,
23−
24− #[serde(skip)]
25− _controller: PhantomData<C>,
26−}
27−
28−/// Actions on a single todo item that can be triggered from the UI.
29−pub trait TodoActions {
30− /// Toggle the completion state of the todo item with the given id.
31− fn toggle_completed(root: &mut dyn RootRender, vdom: VdomWeak, id: usize);
32−
33− /// Delete the todo item with the given id.
34− fn delete(root: &mut dyn RootRender, vdom: VdomWeak, id: usize);
35−
36− /// Begin editing the todo item with the given id.
37− fn begin_editing(root: &mut dyn RootRender, vdom: VdomWeak, id: usize);
38−
39− /// Update the edits for the todo with the given id.
40− fn update_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize, edits: String);
41−
42− /// Finish editing the todo with the given id.
43− fn finish_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize);
44−
45− /// Cancel editing the todo with the given id.
46− fn cancel_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize);
47−}
48−
49−impl<C> Todo<C> {
50− /// Construct a new `Todo` with the given identifier and title.
51− pub fn new<S: Into<String>>(id: usize, title: S) -> Self {
52− let title = title.into();
53− let completed = false;
54− let edits = None;
55− Todo {
56− inner: Cached::new(TodoInner {
57− id,
58− title,
59− completed,
60− edits,
61− _controller: PhantomData,
62− }),
63− }
64− }
65−
66− /// Set this todo item's id.
67− pub fn set_id(&mut self, id: usize) {
68− if id != self.inner.id {
69− Cached::invalidate(&self.inner);
70− }
71− self.inner.id = id;
72− }
73−
74− /// Is this `Todo` complete?
75− pub fn is_complete(&self) -> bool {
76− self.inner.completed
77− }
78−
79− /// Mark the `Todo` as complete or not.
80− pub fn set_complete(&mut self, to: bool) {
81− if to != self.inner.completed {
82− Cached::invalidate(&self.inner);
83− }
84− self.inner.completed = to;
85− }
86−
87− /// Get this todo's title.
88− pub fn title(&self) -> &str {
89− &self.inner.title
90− }
91−
92− /// Set this todo item's title.
93− pub fn set_title<S: Into<String>>(&mut self, title: S) {
94− let title = title.into();
95− if title != self.inner.title {
96− Cached::invalidate(&self.inner);
97− }
98− self.inner.title = title;
99− }
100−
101− /// Set the edits for this todo.
102− pub fn set_edits<S: Into<String>>(&mut self, edits: Option<S>) {
103− let edits = edits.map(Into::into);
104− if edits != self.inner.edits {
105− Cached::invalidate(&self.inner);
106− }
107− self.inner.edits = edits;
108− }
109−
110− /// Take this todo's edits, leaving `None` in their place.
111− pub fn take_edits(&mut self) -> Option<String> {
112− if self.inner.edits.is_some() {
113− Cached::invalidate(&self.inner);
114− }
115− self.inner.edits.take()
116− }
117−}
118−
119−impl<'a, C: TodoActions> Render<'a> for Todo<C> {
120− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
121− use dodrio::{
122− builder::*,
123− bumpalo::{self, collections::String},
124− };
125−
126− let id = self.inner.id;
127− let title = self.inner.edits.as_ref().unwrap_or(&self.inner.title);
128− let title = bumpalo::format!(in cx.bump, "{}", title).into_bump_str();
129−
130− li(&cx)
131− .attr("class", {
132− let mut class = String::new_in(cx.bump);
133− if self.inner.completed {
134− class.push_str("completed ");
135− }
136− if self.inner.edits.is_some() {
137− class.push_str("editing");
138− }
139− class.into_bump_str()
140− })
141− .children([
142− div(&cx)
143− .attr("class", "view")
144− .children([
145− input(&cx)
146− .attr("class", "toggle")
147− .attr("type", "checkbox")
148− .bool_attr("checked", self.inner.completed)
149− .on("click", move |root, vdom, _event| {
150− C::toggle_completed(root, vdom, id);
151− })
152− .finish(),
153− label(&cx)
154− .on("dblclick", move |root, vdom, _event| {
155− C::begin_editing(root, vdom, id);
156− })
157− .children([text(title)])
158− .finish(),
159− button(&cx)
160− .attr("class", "destroy")
161− .on("click", move |root, vdom, _event| {
162− C::delete(root, vdom, id);
163− })
164− .finish(),
165− ])
166− .finish(),
167− input(&cx)
168− .attr("class", "edit")
169− .attr("value", title)
170− .attr("name", "title")
171− .attr(
172− "id",
173− bumpalo::format!(in cx.bump, "todo-{}", id).into_bump_str(),
174− )
175− .on("input", move |root, vdom, event| {
176− let input = event
177− .target()
178− .unwrap_throw()
179− .unchecked_into::<web_sys::HtmlInputElement>();
180− C::update_edits(root, vdom, id, input.value());
181− })
182− .on("blur", move |root, vdom, _event| {
183− C::finish_edits(root, vdom, id);
184− })
185− .on("keydown", move |root, vdom, event| {
186− let event = event.unchecked_into::<web_sys::KeyboardEvent>();
187− match event.key_code() {
188− keys::ENTER => C::finish_edits(root, vdom, id),
189− keys::ESCAPE => C::cancel_edits(root, vdom, id),
190− _ => {}
191− }
192− })
193− .finish(),
194− ])
195− .finish()
196− }
197−}
198−
199−impl<C> Default for TodoInner<C> {
200− fn default() -> Self {
201− TodoInner {
202− id: Default::default(),
203− title: Default::default(),
204− completed: Default::default(),
205− edits: Default::default(),
206− _controller: PhantomData,
207− }
208− }
209−}
210−
211−impl<C> serde::Serialize for Todo<C> {
212− fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
213− where
214− S: serde::Serializer,
215− {
216− self.inner.serialize(serializer)
217− }
218−}
219−
220−impl<'de, C> Deserialize<'de> for Todo<C> {
221− fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
222− where
223− D: serde::Deserializer<'de>,
224− {
225− Ok(Todo {
226− inner: <Cached<TodoInner<C>>>::deserialize(deserializer)?,
227− })
228− }
229−}
dodrio/examples/todomvc/src/todos.rsdeleted−300
@@ -1,300 +0,0 @@
1−//! Type definitions and `dodrio::Render` implementation for a collection of
2−//! todo items.
3−
4−use crate::controller::Controller;
5−use crate::todo::{Todo, TodoActions};
6−use crate::visibility::Visibility;
7−use crate::{keys, utils};
8−use dodrio::{bumpalo, Node, Render, RenderContext, RootRender, VdomWeak};
9−use serde::{Deserialize, Serialize};
10−use std::marker::PhantomData;
11−use std::mem;
12−use wasm_bindgen::{prelude::*, JsCast};
13−
14−/// A collection of todos.
15−#[derive(Default, Serialize, Deserialize)]
16−#[serde(rename = "todos-dodrio", bound = "")]
17−pub struct Todos<C = Controller> {
18− todos: Vec<Todo<C>>,
19−
20− #[serde(skip)]
21− draft: String,
22−
23− #[serde(skip)]
24− visibility: Visibility,
25−
26− #[serde(skip)]
27− _controller: PhantomData<C>,
28−}
29−
30−/// Actions for `Todos` that can be triggered by UI interactions.
31−pub trait TodosActions: 'static + TodoActions {
32− /// Toggle the completion state of all todo items.
33− fn toggle_all(root: &mut dyn RootRender, vdom: VdomWeak);
34−
35− /// Update the draft todo item's text.
36− fn update_draft(root: &mut dyn RootRender, vdom: VdomWeak, draft: String);
37−
38− /// Finish the current draft todo item and add it to the collection of
39− /// todos.
40− fn finish_draft(root: &mut dyn RootRender, vdom: VdomWeak);
41−
42− /// Change the todo item visibility filtering to the given `Visibility`.
43− fn change_visibility(root: &mut dyn RootRender, vdom: VdomWeak, vis: Visibility);
44−
45− /// Delete all completed todo items.
46− fn delete_completed(root: &mut dyn RootRender, vdom: VdomWeak);
47−}
48−
49−impl<C> Todos<C> {
50− /// Construct a new todos set.
51− ///
52− /// If an existing set is available in local storage, then use that,
53− /// otherwise create a new set.
54− pub fn new() -> Self
55− where
56− C: Default,
57− {
58− Self::from_local_storage().unwrap_or_default()
59− }
60−
61− /// Deserialize a set of todos from local storage.
62− pub fn from_local_storage() -> Option<Self> {
63− utils::local_storage()
64− .get("todomvc-dodrio")
65− .ok()
66− .and_then(|opt| opt)
67− .and_then(|json| serde_json::from_str(&json).ok())
68− }
69−
70− /// Serialize this set of todos to local storage.
71− pub fn save_to_local_storage(&self) {
72− let serialized = serde_json::to_string(self).unwrap_throw();
73− utils::local_storage()
74− .set("todomvc-dodrio", &serialized)
75− .unwrap_throw();
76− }
77−
78− /// Add a new todo item to this collection.
79− pub fn add_todo(&mut self, todo: Todo<C>) {
80− self.todos.push(todo);
81− }
82−
83− /// Delete the todo with the given id.
84− pub fn delete_todo(&mut self, id: usize) {
85− self.todos.remove(id);
86− self.fix_ids();
87− }
88−
89− /// Delete all completed todo items.
90− pub fn delete_completed(&mut self) {
91− self.todos.retain(|t| !t.is_complete());
92− self.fix_ids();
93− }
94−
95− // Fix all todo identifiers so that they match their index once again.
96− fn fix_ids(&mut self) {
97− for (id, todo) in self.todos.iter_mut().enumerate() {
98− todo.set_id(id);
99− }
100− }
101−
102− /// Get a shared slice of the underlying set of todo items.
103− pub fn todos(&self) -> &[Todo<C>] {
104− &self.todos
105− }
106−
107− /// Get an exclusive slice of the underlying set of todo items.
108− pub fn todos_mut(&mut self) -> &mut [Todo<C>] {
109− &mut self.todos
110− }
111−
112− /// Set the draft todo item text.
113− pub fn set_draft<S: Into<String>>(&mut self, draft: S) {
114− self.draft = draft.into();
115− }
116−
117− /// Take the current draft text and replace it with an empty string.
118− pub fn take_draft(&mut self) -> String {
119− mem::replace(&mut self.draft, String::new())
120− }
121−
122− /// Get the current visibility for these todos.
123− pub fn visibility(&self) -> Visibility {
124− self.visibility
125− }
126−
127− /// Set the visibility for these todoS.
128− pub fn set_visibility(&mut self, vis: Visibility) {
129− self.visibility = vis;
130− }
131−}
132−
133−/// Rendering helpers.
134−impl<C: TodosActions> Todos<C> {
135− fn header<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
136− use dodrio::builder::*;
137−
138− header(&cx)
139− .attr("class", "header")
140− .children([
141− h1(&cx).children([text("todos")]).finish(),
142− input(&cx)
143− .on("input", |root, vdom, event| {
144− let input = event
145− .target()
146− .unwrap_throw()
147− .unchecked_into::<web_sys::HtmlInputElement>();
148− C::update_draft(root, vdom, input.value());
149− })
150− .on("keydown", |root, vdom, event| {
151− let event = event.unchecked_into::<web_sys::KeyboardEvent>();
152− if event.key_code() == keys::ENTER {
153− C::finish_draft(root, vdom);
154− }
155− })
156− .attr("class", "new-todo")
157− .attr("placeholder", "What needs to be done?")
158− .attr("autofocus", "")
159− .attr(
160− "value",
161− bumpalo::format!(in cx.bump, "{}", self.draft).into_bump_str(),
162− )
163− .finish(),
164− ])
165− .finish()
166− }
167−
168− fn todos_list<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
169− use dodrio::{builder::*, bumpalo::collections::Vec};
170−
171− let mut todos = Vec::with_capacity_in(self.todos.len(), cx.bump);
172− todos.extend(
173− self.todos
174− .iter()
175− .filter(|t| match self.visibility {
176− Visibility::All => true,
177− Visibility::Active => !t.is_complete(),
178− Visibility::Completed => t.is_complete(),
179− })
180− .map(|t| t.render(cx)),
181− );
182−
183− section(&cx)
184− .attr("class", "main")
185− .attr(
186− "visibility",
187− if self.todos.is_empty() {
188− "hidden"
189− } else {
190− "visible"
191− },
192− )
193− .children([
194− input(&cx)
195− .attr("class", "toggle-all")
196− .attr("id", "toggle-all")
197− .attr("type", "checkbox")
198− .attr("name", "toggle")
199− .bool_attr("checked", self.todos.iter().all(|t| t.is_complete()))
200− .on("click", |root, vdom, _event| {
201− C::toggle_all(root, vdom);
202− })
203− .finish(),
204− label(&cx)
205− .attr("for", "toggle-all")
206− .children([text("Mark all as complete")])
207− .finish(),
208− ul(&cx).attr("class", "todo-list").children(todos).finish(),
209− ])
210− .finish()
211− }
212−
213− fn footer<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
214− use dodrio::builder::*;
215−
216− let completed_count = self.todos.iter().filter(|t| t.is_complete()).count();
217− let incomplete_count = self.todos.len() - completed_count;
218− let items_left = if incomplete_count == 1 {
219− " item left"
220− } else {
221− " items left"
222− };
223− let incomplete_count = bumpalo::format!(in cx.bump, "{}", incomplete_count);
224−
225− let clear_completed_text = bumpalo::format!(
226− in cx.bump,
227− "Clear completed ({})",
228− self.todos.iter().filter(|t| t.is_complete()).count()
229− );
230−
231− footer(&cx)
232− .attr("class", "footer")
233− .bool_attr("hidden", self.todos.is_empty())
234− .children([
235− span(&cx)
236− .attr("class", "todo-count")
237− .children([
238− strong(&cx)
239− .children([text(incomplete_count.into_bump_str())])
240− .finish(),
241− text(items_left),
242− ])
243− .finish(),
244− ul(&cx)
245− .attr("class", "filters")
246− .children([
247− self.visibility_swap(cx, "#/", Visibility::All),
248− self.visibility_swap(cx, "#/active", Visibility::Active),
249− self.visibility_swap(cx, "#/completed", Visibility::Completed),
250− ])
251− .finish(),
252− button(&cx)
253− .on("click", |root, vdom, _event| {
254− C::delete_completed(root, vdom);
255− })
256− .attr("class", "clear-completed")
257− .bool_attr("hidden", completed_count == 0)
258− .children([text(clear_completed_text.into_bump_str())])
259− .finish(),
260− ])
261− .finish()
262− }
263−
264− fn visibility_swap<'a>(
265− &self,
266− cx: &mut RenderContext<'a>,
267− url: &'static str,
268− target_vis: Visibility,
269− ) -> Node<'a> {
270− use dodrio::builder::*;
271−
272− li(&cx)
273− .on("click", move |root, vdom, _event| {
274− C::change_visibility(root, vdom, target_vis);
275− })
276− .children([a(&cx)
277− .attr("href", url)
278− .attr(
279− "class",
280− if self.visibility == target_vis {
281− "selected"
282− } else {
283− ""
284− },
285− )
286− .children([text(target_vis.label())])
287− .finish()])
288− .finish()
289− }
290−}
291−
292−impl<'a, C: TodosActions> Render<'a> for Todos<C> {
293− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
294− use dodrio::builder::*;
295−
296− div(&cx)
297− .children([self.header(cx), self.todos_list(cx), self.footer(cx)])
298− .finish()
299− }
300−}
dodrio/examples/todomvc/src/utils.rsdeleted−32
@@ -1,32 +0,0 @@
1−//! Small utility functions.
2−
3−use wasm_bindgen::UnwrapThrowExt;
4−
5−/// Get the top-level window.
6−pub fn window() -> web_sys::Window {
7− web_sys::window().unwrap_throw()
8−}
9−
10−/// Get the current location hash, if any.
11−pub fn hash() -> Option<String> {
12− window()
13− .location()
14− .hash()
15− .ok()
16− .and_then(|h| if h.is_empty() { None } else { Some(h) })
17−}
18−
19−/// Set the current location hash.
20−pub fn set_hash(hash: &str) {
21− window().location().set_hash(hash).unwrap_throw();
22−}
23−
24−/// Get the top-level document.
25−pub fn document() -> web_sys::Document {
26− window().document().unwrap_throw()
27−}
28−
29−/// Get the top-level window's local storage.
30−pub fn local_storage() -> web_sys::Storage {
31− window().local_storage().unwrap_throw().unwrap_throw()
32−}
dodrio/examples/todomvc/src/visibility.rsdeleted−51
@@ -1,51 +0,0 @@
1−//! Visibility filtering.
2−
3−use std::fmt;
4−use std::str::FromStr;
5−
6−/// The visibility filtering for todo items.
7−#[derive(Clone, Copy, Eq, PartialEq)]
8−pub enum Visibility {
9− /// Show all todos.
10− All,
11− /// Show only active, incomplete todos.
12− Active,
13− /// Show only inactive, completed todos.
14− Completed,
15−}
16−
17−impl Default for Visibility {
18− fn default() -> Visibility {
19− Visibility::All
20− }
21−}
22−
23−impl FromStr for Visibility {
24− type Err = ();
25−
26− fn from_str(s: &str) -> Result<Self, Self::Err> {
27− match s {
28− "all" => Ok(Visibility::All),
29− "active" => Ok(Visibility::Active),
30− "completed" => Ok(Visibility::Completed),
31− _ => Err(()),
32− }
33− }
34−}
35−
36−impl fmt::Display for Visibility {
37− fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38− write!(f, "{}", self.label().to_lowercase())
39− }
40−}
41−
42−impl Visibility {
43− /// Get a string label for this visibility.
44− pub fn label(self) -> &'static str {
45− match self {
46− Visibility::All => "All",
47− Visibility::Active => "Active",
48− Visibility::Completed => "Completed",
49− }
50− }
51−}
dodrio/js/change-list-interpreter.jsdeleted−377
@@ -1,377 +0,0 @@
1−const decoder = new TextDecoder();
2−
3−function top(stack) {
4− return stack[stack.length - 1];
5−}
6−
7−function string(mem, pointer, length) {
8− const buf = mem.subarray(pointer, pointer + length);
9− return decoder.decode(buf);
10−}
11−
12−const OP_TABLE = [
13− // 0
14− function setText(interpreter, mem8, mem32, i) {
15− const pointer = mem32[i++];
16− const length = mem32[i++];
17− const str = string(mem8, pointer, length);
18− top(interpreter.stack).textContent = str;
19− return i;
20− },
21−
22− // 1
23− function removeSelfAndNextSiblings(interpreter, mem8, mem32, i) {
24− const node = interpreter.stack.pop();
25− let sibling = node.nextSibling;
26− while (sibling) {
27− const temp = sibling.nextSibling;
28− sibling.remove();
29− sibling = temp;
30− }
31− node.remove();
32− return i;
33− },
34−
35− // 2
36− function replaceWith(interpreter, mem8, mem32, i) {
37− const newNode = interpreter.stack.pop();
38− const oldNode = interpreter.stack.pop();
39− oldNode.replaceWith(newNode);
40− interpreter.stack.push(newNode);
41− return i;
42− },
43−
44− // 3
45− function setAttribute(interpreter, mem8, mem32, i) {
46− const nameId = mem32[i++];
47− const valueId = mem32[i++];
48− const name = interpreter.getCachedString(nameId);
49− const value = interpreter.getCachedString(valueId);
50− const node = top(interpreter.stack);
51− node.setAttribute(name, value);
52−
53− // Some attributes are "volatile" and don't work through `setAttribute`.
54− if (name === "value") {
55− node.value = value;
56− }
57− if (name === "checked") {
58− node.checked = true;
59− }
60− if (name === "selected") {
61− node.selected = true;
62− }
63−
64− return i;
65− },
66−
67− // 4
68− function removeAttribute(interpreter, mem8, mem32, i) {
69− const nameId = mem32[i++];
70− const name = interpreter.getCachedString(nameId);
71− const node = top(interpreter.stack);
72− node.removeAttribute(name);
73−
74− // Some attributes are "volatile" and don't work through `removeAttribute`.
75− if (name === "value") {
76− node.value = null;
77− }
78− if (name === "checked") {
79− node.checked = false;
80− }
81− if (name === "selected") {
82− node.selected = false;
83− }
84−
85− return i;
86− },
87−
88− // 5
89− function pushReverseChild(interpreter, mem8, mem32, i) {
90− const n = mem32[i++];
91− const parent = top(interpreter.stack);
92− const children = parent.childNodes;
93− const child = children[children.length - n - 1];
94− interpreter.stack.push(child);
95− return i;
96− },
97−
98− // 6
99− function popPushChild(interpreter, mem8, mem32, i) {
100− const n = mem32[i++];
101− interpreter.stack.pop();
102− const parent = top(interpreter.stack);
103− const children = parent.childNodes;
104− const child = children[n];
105− interpreter.stack.push(child);
106− return i;
107− },
108−
109− // 7
110− function pop(interpreter, mem8, mem32, i) {
111− interpreter.stack.pop();
112− return i;
113− },
114−
115− // 8
116− function appendChild(interpreter, mem8, mem32, i) {
117− const child = interpreter.stack.pop();
118− top(interpreter.stack).appendChild(child);
119− return i;
120− },
121−
122− // 9
123− function createTextNode(interpreter, mem8, mem32, i) {
124− const pointer = mem32[i++];
125− const length = mem32[i++];
126− const text = string(mem8, pointer, length);
127− interpreter.stack.push(document.createTextNode(text));
128− return i;
129− },
130−
131− // 10
132− function createElement(interpreter, mem8, mem32, i) {
133− const tagNameId = mem32[i++];
134− const tagName = interpreter.getCachedString(tagNameId);
135− interpreter.stack.push(document.createElement(tagName));
136− return i;
137− },
138−
139− // 11
140− function newEventListener(interpreter, mem8, mem32, i) {
141− const eventId = mem32[i++];
142− const eventType = interpreter.getCachedString(eventId);
143− const a = mem32[i++];
144− const b = mem32[i++];
145− const el = top(interpreter.stack);
146− el.addEventListener(eventType, interpreter.eventHandler);
147− el[`dodrio-a-${eventType}`] = a;
148− el[`dodrio-b-${eventType}`] = b;
149− return i;
150− },
151−
152− // 12
153− function updateEventListener(interpreter, mem8, mem32, i) {
154− const eventId = mem32[i++];
155− const eventType = interpreter.getCachedString(eventId);
156− const el = top(interpreter.stack);
157− el[`dodrio-a-${eventType}`] = mem32[i++];
158− el[`dodrio-b-${eventType}`] = mem32[i++];
159− return i;
160− },
161−
162− // 13
163− function removeEventListener(interpreter, mem8, mem32, i) {
164− const eventId = mem32[i++];
165− const eventType = interpreter.getCachedString(eventId);
166− const el = top(interpreter.stack);
167− el.removeEventListener(eventType, interpreter.eventHandler);
168− return i;
169− },
170−
171− // 14
172− function addCachedString(interpreter, mem8, mem32, i) {
173− const pointer = mem32[i++];
174− const length = mem32[i++];
175− const id = mem32[i++];
176− const str = string(mem8, pointer, length);
177− interpreter.addCachedString(str, id);
178− return i;
179− },
180−
181− // 15
182− function dropCachedString(interpreter, mem8, mem32, i) {
183− const id = mem32[i++];
184− interpreter.dropCachedString(id);
185− return i;
186− },
187−
188− // 16
189− function createElementNS(interpreter, mem8, mem32, i) {
190− const tagNameId = mem32[i++];
191− const tagName = interpreter.getCachedString(tagNameId);
192− const nsId = mem32[i++];
193− const ns = interpreter.getCachedString(nsId);
194− interpreter.stack.push(document.createElementNS(ns, tagName));
195− return i;
196− },
197−
198− // 17
199− function saveChildrenToTemporaries(interpreter, mem8, mem32, i) {
200− let temp = mem32[i++];
201− const start = mem32[i++];
202− const end = mem32[i++];
203− const parent = top(interpreter.stack);
204− const children = parent.childNodes;
205− for (let i = start; i < end; i++) {
206− interpreter.temporaries[temp++] = children[i];
207− }
208− return i;
209− },
210−
211− // 18
212− function pushChild(interpreter, mem8, mem32, i) {
213− const parent = top(interpreter.stack);
214− const n = mem32[i++];
215− const child = parent.childNodes[n];
216− interpreter.stack.push(child);
217− return i;
218− },
219−
220− // 19
221− function pushTemporary(interpreter, mem8, mem32, i) {
222− const temp = mem32[i++];
223− interpreter.stack.push(interpreter.temporaries[temp]);
224− return i;
225− },
226−
227− // 20
228− function insertBefore(interpreter, mem8, mem32, i) {
229− const before = interpreter.stack.pop();
230− const after = interpreter.stack.pop();
231− after.parentNode.insertBefore(before, after);
232− interpreter.stack.push(before);
233− return i;
234− },
235−
236− // 21
237− function popPushReverseChild(interpreter, mem8, mem32, i) {
238− const n = mem32[i++];
239− interpreter.stack.pop();
240− const parent = top(interpreter.stack);
241− const children = parent.childNodes;
242− const child = children[children.length - n - 1];
243− interpreter.stack.push(child);
244− return i;
245− },
246−
247− // 22
248− function removeChild(interpreter, mem8, mem32, i) {
249− const n = mem32[i++];
250− const parent = top(interpreter.stack);
251− const child = parent.childNodes[n];
252− child.remove();
253− return i;
254− },
255−
256− // 23
257− function setClass(interpreter, mem8, mem32, i) {
258− const classId = mem32[i++];
259− const className = interpreter.getCachedString(classId);
260− top(interpreter.stack).className = className;
261− return i;
262− },
263−
264− // 24
265− function saveTemplate(interpreter, mem8, mem32, i) {
266− const id = mem32[i++];
267− const template = top(interpreter.stack);
268− interpreter.saveTemplate(id, template.cloneNode(true));
269− return i;
270− },
271−
272− // 25
273− function pushTemplate(interpreter, mem8, mem32, i) {
274− const id = mem32[i++];
275− const template = interpreter.getTemplate(id);
276− interpreter.stack.push(template.cloneNode(true));
277− return i;
278− }
279−];
280−
281−export class ChangeListInterpreter {
282− constructor(container) {
283− this.trampoline = null;
284− this.container = container;
285− this.ranges = [];
286− this.stack = [];
287− this.strings = new Map();
288− this.temporaries = [];
289− this.templates = new Map();
290− }
291−
292− unmount() {
293− this.trampoline.mounted = false;
294−
295− // Null out all of our properties just to ensure that if we mistakenly ever
296− // call a method on this instance again, it will throw.
297− this.trampoline = null;
298− this.container = null;
299− this.ranges = null;
300− this.stack = null;
301− this.strings = null;
302− this.temporaries = null;
303− this.templates = null;
304− }
305−
306− addChangeListRange(start, len) {
307− this.ranges.push(start);
308− this.ranges.push(len);
309− }
310−
311− applyChanges(memory) {
312− if (this.ranges.length == 0) {
313− return;
314− }
315−
316− this.stack.push(this.container.firstChild);
317− const mem8 = new Uint8Array(memory.buffer);
318− const mem32 = new Uint32Array(memory.buffer);
319−
320− for (let i = 0; i < this.ranges.length; i += 2) {
321− const start = this.ranges[i];
322− const len = this.ranges[i + 1];
323− this.applyChangeRange(mem8, mem32, start, len);
324− }
325−
326− this.ranges.length = 0;
327− this.stack.length = 0;
328− this.temporaries.length = 0;
329− }
330−
331− applyChangeRange(mem8, mem32, start, len) {
332− const end = (start + len) / 4;
333− for (let i = start / 4; i < end; ) {
334− const op = mem32[i++];
335− i = OP_TABLE[op](this, mem8, mem32, i);
336− }
337− }
338−
339− addCachedString(str, id) {
340− this.strings.set(id, str);
341− }
342−
343− dropCachedString(id) {
344− this.strings.delete(id);
345− }
346−
347− getCachedString(id) {
348− return this.strings.get(id);
349− }
350−
351− saveTemplate(id, template) {
352− this.templates.set(id, template);
353− }
354−
355− getTemplate(id) {
356− return this.templates.get(id);
357− }
358−
359− initEventsTrampoline(trampoline) {
360− this.trampoline = trampoline;
361− trampoline.mounted = true;
362− this.eventHandler = function(event) {
363− if (!trampoline.mounted) {
364− throw new Error("invocation of listener after VDOM has been unmounted");
365− }
366−
367− // `this` always refers to the element the handler was added to.
368− // Since we're adding the handler to all elements our content wants
369− // to listen for events on, this ensures that we always get the right
370− // values for `a` and `b`.
371− const type = event.type;
372− const a = this[`dodrio-a-${type}`];
373− const b = this[`dodrio-b-${type}`];
374− trampoline(event, a, b);
375− }
376− }
377−}
dodrio/js/strace.jsdeleted−80
@@ -1,80 +0,0 @@
1−export function initStrace() {
2− instrumentAll(Element.prototype);
3− instrumentAll(Document.prototype);
4− instrumentAll(Node.prototype);
5− instrumentAll(EventTarget.prototype);
6−}
7−
8−const counts = new Map;
9−counts.total = 0;
10−
11−let timer = null;
12−
13−function log(ctorName, kind, key) {
14− counts.total += 1;
15−
16− const entry = `${kind} ${ctorName}#${key}`
17− const count = counts.get(entry);
18− if (count === undefined) {
19− counts.set(entry, 1);
20− } else {
21− counts.set(entry, count + 1);
22− }
23−
24− if (timer !== null) {
25− clearTimeout(timer);
26− }
27− timer = setTimeout(dumpAndReset, 100);
28−}
29−
30−function dumpAndReset() {
31− const data = [...counts]
32− .sort((a, b) => b[1] - a[1])
33− .map(a => ({ "DOM Method": a[0], "Count": a[1] }));
34−
35− data.push({ "DOM Method": "<total>", "Count": counts.total });
36−
37− console.table(data, ["DOM Method", "Count"]);
38−
39− counts.clear();
40− counts.total = 0;
41−}
42−
43−function instrument(proto, key, desc) {
44− const ctorName = proto.constructor.name;
45−
46− if (typeof desc.value == "function") {
47− const f = desc.value;
48− desc.value = function (...args) {
49− log(ctorName, "call", key);
50− return f.apply(this, args);
51− };
52− }
53−
54− if (typeof desc.get == "function") {
55− const get = desc.get;
56− desc.get = function (...args) {
57− log(ctorName, "get", key);
58− return get.apply(this, args);
59− };
60− }
61−
62− if (typeof desc.set == "function") {
63− const set = desc.set;
64− desc.set = function (...args) {
65− log(ctorName, "set", key);
66− return set.apply(this, args);
67− };
68− }
69−
70− Object.defineProperty(proto, key, desc);
71−}
72−
73−function instrumentAll(proto) {
74− for (const key of Object.keys(proto)) {
75− const desc = Object.getOwnPropertyDescriptor(proto, key);
76− if (desc.configurable) {
77− instrument(proto, key, desc);
78− }
79− }
80−}
dodrio/publish.shdeleted−11
@@ -1,11 +0,0 @@
1−#!/usr/bin/env bash
2−
3−set -ex
4−
5−cd "$(dirname "$0")"
6−
7−cd crates/js-api
8−cargo publish --no-verify $PUBLISH_FLAGS
9−cd ../..
10−
11−cargo publish $PUBLISH_FLAGS
dodrio/src/builder.rsdeleted−1075
@@ -1,1075 +0,0 @@
1−//! Helpers for building virtual DOM nodes.
2−
3−use crate::{node::NodeKey, Attribute, Listener, Node, RootRender, VdomWeak};
4−use bumpalo::Bump;
5−
6−/// A virtual DOM element builder.
7−///
8−/// Typically constructed with element-specific constructors, eg the `div`
9−/// function for building `<div>` elements or the `button` function for building
10−/// `<button>` elements.
11−#[derive(Clone, Debug)]
12−pub struct ElementBuilder<'a, Listeners, Attributes, Children>
13−where
14− Listeners: 'a + AsRef<[Listener<'a>]>,
15− Attributes: 'a + AsRef<[Attribute<'a>]>,
16− Children: 'a + AsRef<[Node<'a>]>,
17−{
18− bump: &'a Bump,
19− key: NodeKey,
20− tag_name: &'a str,
21− listeners: Listeners,
22− attributes: Attributes,
23− children: Children,
24− namespace: Option<&'a str>,
25−}
26−
27−impl<'a>
28− ElementBuilder<
29− 'a,
30− bumpalo::collections::Vec<'a, Listener<'a>>,
31− bumpalo::collections::Vec<'a, Attribute<'a>>,
32− bumpalo::collections::Vec<'a, Node<'a>>,
33− >
34−{
35− /// Create a new `ElementBuilder` for an element with the given tag name.
36− ///
37− /// In general, only use this constructor if the tag is dynamic (i.e. you
38− /// might build a `<div>` or you might build a `<span>` and you don't know
39− /// until runtime). Prefer using the tag-specific constructors instead:
40− /// `div(bump)` or `span(bump)`, etc.
41− ///
42− /// # Example
43− ///
44− /// ```
45− /// use dodrio::{builder::*, bumpalo::Bump};
46− ///
47− /// let b = Bump::new();
48− ///
49− /// let tag_name = if flip_coin() {
50− /// "div"
51− /// } else {
52− /// "span"
53− /// };
54− ///
55− /// let my_element_builder = ElementBuilder::new(&b, tag_name);
56− /// # fn flip_coin() -> bool { true }
57− /// ```
58− pub fn new<B>(bump: B, tag_name: &'a str) -> Self
59− where
60− B: Into<&'a Bump>,
61− {
62− let bump = bump.into();
63− ElementBuilder {
64− bump,
65− key: NodeKey::NONE,
66− tag_name,
67− listeners: bumpalo::collections::Vec::new_in(bump),
68− attributes: bumpalo::collections::Vec::new_in(bump),
69− children: bumpalo::collections::Vec::new_in(bump),
70− namespace: None,
71− }
72− }
73−}
74−
75−impl<'a, Listeners, Attributes, Children> ElementBuilder<'a, Listeners, Attributes, Children>
76−where
77− Listeners: 'a + AsRef<[Listener<'a>]>,
78− Attributes: 'a + AsRef<[Attribute<'a>]>,
79− Children: 'a + AsRef<[Node<'a>]>,
80−{
81− /// Set the listeners for this element.
82− ///
83− /// You can use this method to customize the backing storage for listeners,
84− /// for example to use a fixed-size array instead of the default
85− /// dynamically-sized `bumpalo::collections::Vec`.
86− ///
87− /// Any listeners already added to the builder will be overridden.
88− ///
89− /// # Example
90− ///
91− /// ```no_run
92− /// use dodrio::{builder::*, bumpalo::Bump};
93− ///
94− /// let b = Bump::new();
95− ///
96− /// // Create a `<div>` with a fixed-size array of two listeners.
97− /// let my_div = div(&b)
98− /// .listeners([
99− /// on(&b, "click", |root, vdom, event| {
100− /// // ...
101− /// }),
102− /// on(&b, "dblclick", |root, vdom, event| {
103− /// // ...
104− /// }),
105− /// ])
106− /// .finish();
107− /// ```
108− #[inline]
109− pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, L, Attributes, Children>
110− where
111− L: 'a + AsRef<[Listener<'a>]>,
112− {
113− ElementBuilder {
114− bump: self.bump,
115− key: self.key,
116− tag_name: self.tag_name,
117− listeners,
118− attributes: self.attributes,
119− children: self.children,
120− namespace: self.namespace,
121− }
122− }
123−
124− /// Set the attributes for this element.
125− ///
126− /// You can use this method to customize the backing storage for attributes,
127− /// for example to use a fixed-size array instead of the default
128− /// dynamically-sized `bumpalo::collections::Vec`.
129− ///
130− /// Any attributes already added to the builder will be overridden.
131− ///
132− /// # Example
133− ///
134− /// ```no_run
135− /// use dodrio::{builder::*, bumpalo::Bump, Attribute};
136− ///
137− /// let b = Bump::new();
138− ///
139− /// // Create a `<div>` with a fixed-size array of two attributes.
140− /// let my_div = div(&b)
141− /// .attributes([
142− /// attr("id", "my-div"),
143− /// attr("class", "notification"),
144− /// ])
145− /// .finish();
146− /// ```
147− #[inline]
148− pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, Listeners, A, Children>
149− where
150− A: 'a + AsRef<[Attribute<'a>]>,
151− {
152− ElementBuilder {
153− bump: self.bump,
154− key: self.key,
155− tag_name: self.tag_name,
156− listeners: self.listeners,
157− attributes,
158− children: self.children,
159− namespace: self.namespace,
160− }
161− }
162−
163− /// Set the children for this element.
164− ///
165− /// You can use this method to customize the backing storage for children,
166− /// for example to use a fixed-size array instead of the default
167− /// dynamically-sized `bumpalo::collections::Vec`.
168− ///
169− /// Any children already added to the builder will be overridden.
170− ///
171− /// # Example
172− ///
173− /// ```no_run
174− /// use dodrio::{builder::*, bumpalo::Bump};
175− ///
176− /// let b = Bump::new();
177− ///
178− /// // Create a `<div>` with a fixed-size array of two `<span>` children.
179− /// let my_div = div(&b)
180− /// .children([
181− /// span(&b).finish(),
182− /// span(&b).finish(),
183− /// ])
184− /// .finish();
185− /// ```
186− #[inline]
187− pub fn children<C>(self, children: C) -> ElementBuilder<'a, Listeners, Attributes, C>
188− where
189− C: 'a + AsRef<[Node<'a>]>,
190− {
191− ElementBuilder {
192− bump: self.bump,
193− key: self.key,
194− tag_name: self.tag_name,
195− listeners: self.listeners,
196− attributes: self.attributes,
197− children,
198− namespace: self.namespace,
199− }
200− }
201−
202− /// Set the namespace for this element.
203− ///
204− /// # Example
205− ///
206− /// ```no_run
207− /// use dodrio::{builder::*, bumpalo::Bump};
208− ///
209− /// let b = Bump::new();
210− ///
211− /// // Create a `<td>` tag with an xhtml namespace
212− /// let my_td = td(&b)
213− /// .namespace(Some("http://www.w3.org/1999/xhtml"))
214− /// .finish();
215− /// ```
216− #[inline]
217− pub fn namespace(self, namespace: Option<&'a str>) -> Self {
218− ElementBuilder {
219− bump: self.bump,
220− key: self.key,
221− tag_name: self.tag_name,
222− listeners: self.listeners,
223− attributes: self.attributes,
224− children: self.children,
225− namespace,
226− }
227− }
228−
229− /// Set this element's key.
230− ///
231− /// When diffing sets of siblings, if an old sibling and new sibling share a
232− /// key, then they will always reuse the same physical DOM node. This is
233− /// important when using CSS animations, web components, third party JS, or
234− /// anything else that makes the diffing implementation observable.
235− ///
236− /// Do not use keys if such a scenario does not apply. Keyed diffing is
237− /// generally more expensive than not, since it is putting greater
238− /// constraints on the diffing algorithm.
239− ///
240− /// # Invariants You Must Uphold
241− ///
242− /// The key may not be `u32::MAX`, which is a reserved key value.
243− ///
244− /// Keys must be unique among siblings.
245− ///
246− /// All sibling nodes must be keyed, or they must all not be keyed. You may
247− /// not mix keyed and unkeyed siblings.
248− ///
249− /// # Example
250− ///
251− /// ```no_run
252− /// use dodrio::{builder::*, bumpalo::Bump};
253− ///
254− /// let b = Bump::new();
255− ///
256− /// let my_li = li(&b)
257− /// .key(1337)
258− /// .finish();
259− /// ```
260− #[inline]
261− pub fn key(mut self, key: u32) -> Self {
262− use std::u32;
263− debug_assert!(key != u32::MAX);
264− self.key = NodeKey(key);
265− self
266− }
267−
268− /// Create the virtual DOM node described by this builder.
269− ///
270− /// # Example
271− ///
272− /// ```no_run
273− /// use dodrio::{builder::*, bumpalo::Bump, Node};
274− ///
275− /// let b = Bump::new();
276− ///
277− /// // Start with a builder...
278− /// let builder: ElementBuilder<_, _, _> = div(&b);
279− ///
280− /// // ...and finish it to create a virtual DOM node!
281− /// let my_div: Node = builder.finish();
282− /// ```
283− #[inline]
284− pub fn finish(self) -> Node<'a> {
285− let children: &'a Children = self.bump.alloc(self.children);
286− let children: &'a [Node<'a>] = children.as_ref();
287−
288− let listeners: &'a Listeners = self.bump.alloc(self.listeners);
289− let listeners: &'a [Listener<'a>] = listeners.as_ref();
290−
291− let attributes: &'a Attributes = self.bump.alloc(self.attributes);
292− let attributes: &'a [Attribute<'a>] = attributes.as_ref();
293−
294− Node::element(
295− self.bump,
296− self.key,
297− self.tag_name,
298− listeners,
299− attributes,
300− children,
301− self.namespace,
302− )
303− }
304−}
305−
306−impl<'a, Attributes, Children>
307− ElementBuilder<'a, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
308−where
309− Attributes: 'a + AsRef<[Attribute<'a>]>,
310− Children: 'a + AsRef<[Node<'a>]>,
311−{
312− /// Add a new event listener to this element.
313− ///
314− /// The `event` string specifies which event will be listened for. The
315− /// `callback` function is the function that will be invoked if the
316− /// specified event occurs.
317− ///
318− /// # Example
319− ///
320− /// ```no_run
321− /// use dodrio::{builder::*, bumpalo::Bump};
322− ///
323− /// let b = Bump::new();
324− ///
325− /// // A button that does something when clicked!
326− /// let my_button = button(&b)
327− /// .on("click", |root, vdom, event| {
328− /// // ...
329− /// })
330− /// .finish();
331− /// ```
332− #[inline]
333− pub fn on<F>(mut self, event: &'a str, callback: F) -> Self
334− where
335− F: 'static + Fn(&mut dyn RootRender, VdomWeak, web_sys::Event),
336− {
337− self.listeners.push(Listener {
338− event,
339− callback: self.bump.alloc(callback),
340− });
341− self
342− }
343−}
344−
345−impl<'a, Listeners, Children>
346− ElementBuilder<'a, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
347−where
348− Listeners: 'a + AsRef<[Listener<'a>]>,
349− Children: 'a + AsRef<[Node<'a>]>,
350−{
351− /// Add a new attribute to this element.
352− ///
353− /// # Example
354− ///
355− /// ```no_run
356− /// use dodrio::{builder::*, bumpalo::Bump};
357− ///
358− /// let b = Bump::new();
359− ///
360− /// // Create the `<div id="my-div"/>` element.
361− /// let my_div = div(&b).attr("id", "my-div").finish();
362− /// ```
363− #[inline]
364− pub fn attr(mut self, name: &'a str, value: &'a str) -> Self {
365− self.attributes.push(Attribute { name, value });
366− self
367− }
368−
369− /// Conditionally add a "boolean-style" attribute to this element.
370− ///
371− /// If the `should_add` parameter is true, then adds an attribute with the
372− /// given `name` and an empty string value. If the `should_add` parameter is
373− /// false, then the attribute is not added.
374− ///
375− /// This method is useful for attributes whose semantics are defined by
376− /// whether or not the attribute is present or not, and whose value is
377− /// ignored. Example attributes like this include:
378− ///
379− /// * `checked`
380− /// * `hidden`
381− /// * `selected`
382− ///
383− /// # Example
384− ///
385− /// ```no_run
386− /// use dodrio::{builder::*, bumpalo::Bump};
387− /// use js_sys::Math;
388− ///
389− /// let b = Bump::new();
390− ///
391− /// // Create the `<div>` that is randomly hidden 50% of the time.
392− /// let my_div = div(&b)
393− /// .bool_attr("hidden", Math::random() >= 0.5)
394− /// .finish();
395− /// ```
396− pub fn bool_attr(mut self, name: &'a str, should_add: bool) -> Self {
397− if should_add {
398− self.attributes.push(Attribute { name, value: "" });
399− }
400− self
401− }
402−}
403−
404−impl<'a, Listeners, Attributes>
405− ElementBuilder<'a, Listeners, Attributes, bumpalo::collections::Vec<'a, Node<'a>>>
406−where
407− Listeners: 'a + AsRef<[Listener<'a>]>,
408− Attributes: 'a + AsRef<[Attribute<'a>]>,
409−{
410− /// Add a new child to this element.
411− ///
412− /// # Example
413− ///
414− /// ```no_run
415− /// use dodrio::{builder::*, bumpalo::Bump};
416− /// use js_sys::Math;
417− ///
418− /// let b = Bump::new();
419− ///
420− /// // Create `<p><span></span></p>`.
421− /// let my_div = p(&b)
422− /// .child(span(&b).finish())
423− /// .finish();
424− /// ```
425− #[inline]
426− pub fn child(mut self, child: Node<'a>) -> Self {
427− self.children.push(child);
428− self
429− }
430−}
431−
432−macro_rules! builder_constructors {
433− ( $(
434− $(#[$attr:meta])*
435− $name:ident;
436− )* ) => {
437− $(
438− $(#[$attr])*
439− #[inline]
440− pub fn $name<'a, B>(
441− bump: B,
442− ) -> ElementBuilder<
443− 'a,
444− bumpalo::collections::Vec<'a, Listener<'a>>,
445− bumpalo::collections::Vec<'a, Attribute<'a>>,
446− bumpalo::collections::Vec<'a, Node<'a>>,
447− >
448− where
449− B: Into<&'a Bump>
450− {
451− ElementBuilder::new(bump, stringify!($name))
452− }
453− )*
454− };
455− ( $(
456− $(#[$attr:meta])*
457− $name:ident <> $namespace:tt;
458− )* ) => {
459− $(
460− $(#[$attr])*
461− #[inline]
462− pub fn $name<'a>(
463− bump: &'a Bump,
464− ) -> ElementBuilder<
465− 'a,
466− bumpalo::collections::Vec<'a, Listener<'a>>,
467− bumpalo::collections::Vec<'a, Attribute<'a>>,
468− bumpalo::collections::Vec<'a, Node<'a>>,
469− > {
470− let builder = ElementBuilder::new(bump, stringify!($name));
471− builder.namespace(Some($namespace))
472− }
473− )*
474− }
475−}
476−
477−// Organized in the same order as
478−// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
479−//
480−// Does not include obsolete elements.
481−builder_constructors! {
482− // Document metadata
483−
484− /// Build a
485− /// [`<base>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)
486− /// element.
487− base;
488− /// Build a
489− /// [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
490− /// element.
491− head;
492− /// Build a
493− /// [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
494− /// element.
495− link;
496− /// Build a
497− /// [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
498− /// element.
499− meta;
500− /// Build a
501− /// [`<style>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)
502− /// element.
503− style;
504− /// Build a
505− /// [`<title>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)
506− /// element.
507− title;
508−
509− // Sectioning root
510−
511− /// Build a
512− /// [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)
513− /// element.
514− body;
515−
516− // Content sectioning
517−
518− /// Build a
519− /// [`<address>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address)
520− /// element.
521− address;
522− /// Build a
523− /// [`<article>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article)
524− /// element.
525− article;
526− /// Build a
527− /// [`<aside>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside)
528− /// element.
529− aside;
530− /// Build a
531− /// [`<footer>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer)
532− /// element.
533− footer;
534− /// Build a
535− /// [`<header>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header)
536− /// element.
537− header;
538− /// Build a
539− /// [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)
540− /// element.
541− h1;
542− /// Build a
543− /// [`<h2>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h2)
544− /// element.
545− h2;
546− /// Build a
547− /// [`<h3>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h3)
548− /// element.
549− h3;
550− /// Build a
551− /// [`<h4>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4)
552− /// element.
553− h4;
554− /// Build a
555− /// [`<h5>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h5)
556− /// element.
557− h5;
558− /// Build a
559− /// [`<h6>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6)
560− /// element.
561− h6;
562− /// Build a
563− /// [`<hgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hgroup)
564− /// element.
565− hgroup;
566− /// Build a
567− /// [`<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main)
568− /// element.
569− main;
570− /// Build a
571− /// [`<nav>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav)
572− /// element.
573− nav;
574− /// Build a
575− /// [`<section>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section)
576− /// element.
577− section;
578−
579− // Text content
580−
581− /// Build a
582− /// [`<blockquote>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote)
583− /// element.
584− blockquote;
585− /// Build a
586− /// [`<dd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd)
587− /// element.
588− dd;
589− /// Build a
590− /// [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)
591− /// element.
592− div;
593− /// Build a
594− /// [`<dl>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl)
595− /// element.
596− dl;
597− /// Build a
598− /// [`<dt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt)
599− /// element.
600− dt;
601− /// Build a
602− /// [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption)
603− /// element.
604− figcaption;
605− /// Build a
606− /// [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure)
607− /// element.
608− figure;
609− /// Build a
610− /// [`<hr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr)
611− /// element.
612− hr;
613− /// Build a
614− /// [`<li>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li)
615− /// element.
616− li;
617− /// Build a
618− /// [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol)
619− /// element.
620− ol;
621− /// Build a
622− /// [`<p>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p)
623− /// element.
624− p;
625− /// Build a
626− /// [`<pre>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre)
627− /// element.
628− pre;
629− /// Build a
630− /// [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul)
631− /// element.
632− ul;
633−
634− // Inline text semantics
635−
636− /// Build a
637− /// [`<a>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
638− /// element.
639− a;
640− /// Build a
641− /// [`<abbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr)
642− /// element.
643− abbr;
644− /// Build a
645− /// [`<b>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b)
646− /// element.
647− b;
648− /// Build a
649− /// [`<bdi>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi)
650− /// element.
651− bdi;
652− /// Build a
653− /// [`<bdo>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo)
654− /// element.
655− bdo;
656− /// Build a
657− /// [`<br>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br)
658− /// element.
659− br;
660− /// Build a
661− /// [`<cite>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite)
662− /// element.
663− cite;
664− /// Build a
665− /// [`<code>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code)
666− /// element.
667− code;
668− /// Build a
669− /// [`<data>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data)
670− /// element.
671− data;
672− /// Build a
673− /// [`<dfn>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn)
674− /// element.
675− dfn;
676− /// Build a
677− /// [`<em>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em)
678− /// element.
679− em;
680− /// Build a
681− /// [`<i>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i)
682− /// element.
683− i;
684− /// Build a
685− /// [`<kbd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd)
686− /// element.
687− kbd;
688− /// Build a
689− /// [`<mark>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark)
690− /// element.
691− mark;
692− /// Build a
693− /// [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)
694− /// element.
695− q;
696− /// Build a
697− /// [`<rb>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rb)
698− /// element.
699− rb;
700− /// Build a
701− /// [`<rp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp)
702− /// element.
703− rp;
704− /// Build a
705− /// [`<rt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt)
706− /// element.
707− rt;
708− /// Build a
709− /// [`<rtc>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rtc)
710− /// element.
711− rtc;
712− /// Build a
713− /// [`<ruby>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)
714− /// element.
715− ruby;
716− /// Build a
717− /// [`<s>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s)
718− /// element.
719− s;
720− /// Build a
721− /// [`<samp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp)
722− /// element.
723− samp;
724− /// Build a
725− /// [`<small>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small)
726− /// element.
727− small;
728− /// Build a
729− /// [`<span>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)
730− /// element.
731− span;
732− /// Build a
733− /// [`<strong>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong)
734− /// element.
735− strong;
736− /// Build a
737− /// [`<sub>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub)
738− /// element.
739− sub;
740− /// Build a
741− /// [`<sup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup)
742− /// element.
743− sup;
744− /// Build a
745− /// [`<time>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time)
746− /// element.
747− time;
748− /// Build a
749− /// [`<u>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u)
750− /// element.
751− u;
752− /// Build a
753− /// [`<var>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var)
754− /// element.
755− var;
756− /// Build a
757− /// [`<wbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr)
758− /// element.
759− wbr;
760−
761− // Image and multimedia
762−
763− /// Build a
764− /// [`<area>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
765− /// element.
766− area;
767− /// Build a
768− /// [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio)
769− /// element.
770− audio;
771− /// Build a
772− /// [`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)
773− /// element.
774− img;
775− /// Build a
776− /// [`<map>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map)
777− /// element.
778− map;
779− /// Build a
780− /// [`<track>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track)
781− /// element.
782− track;
783− /// Build a
784− /// [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)
785− /// element.
786− video;
787−
788− // Embedded content
789−
790− /// Build a
791− /// [`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed)
792− /// element.
793− embed;
794− /// Build a
795− /// [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)
796− /// element.
797− iframe;
798− /// Build a
799− /// [`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object)
800− /// element.
801− object;
802− /// Build a
803− /// [`<param>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param)
804− /// element.
805− param;
806− /// Build a
807− /// [`<picture>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture)
808− /// element.
809− picture;
810− /// Build a
811− /// [`<source>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source)
812− /// element.
813− source;
814−
815− // Scripting
816−
817− /// Build a
818− /// [`<canvas>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas)
819− /// element.
820− canvas;
821− /// Build a
822− /// [`<noscript>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript)
823− /// element.
824− noscript;
825− /// Build a
826− /// [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
827− /// element.
828− script;
829−
830− // Demarcating edits
831−
832− /// Build a
833− /// [`<del>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del)
834− /// element.
835− del;
836− /// Build a
837− /// [`<ins>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins)
838− /// element.
839− ins;
840−
841− // Table content
842−
843− /// Build a
844− /// [`<caption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption)
845− /// element.
846− caption;
847− /// Build a
848− /// [`<col>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/col)
849− /// element.
850− col;
851− /// Build a
852− /// [`<colgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup)
853− /// element.
854− colgroup;
855− /// Build a
856− /// [`<table>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table)
857− /// element.
858− table;
859− /// Build a
860− /// [`<tbody>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody)
861− /// element.
862− tbody;
863− /// Build a
864− /// [`<td>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td)
865− /// element.
866− td;
867− /// Build a
868− /// [`<tfoot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot)
869− /// element.
870− tfoot;
871− /// Build a
872− /// [`<th>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th)
873− /// element.
874− th;
875− /// Build a
876− /// [`<thead>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead)
877− /// element.
878− thead;
879− /// Build a
880− /// [`<tr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr)
881− /// element.
882− tr;
883−
884− // Forms
885−
886− /// Build a
887− /// [`<button>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button)
888− /// element.
889− button;
890− /// Build a
891− /// [`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist)
892− /// element.
893− datalist;
894− /// Build a
895− /// [`<fieldset>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset)
896− /// element.
897− fieldset;
898− /// Build a
899− /// [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
900− /// element.
901− form;
902− /// Build a
903− /// [`<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
904− /// element.
905− input;
906− /// Build a
907− /// [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)
908− /// element.
909− label;
910− /// Build a
911− /// [`<legend>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend)
912− /// element.
913− legend;
914− /// Build a
915− /// [`<meter>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter)
916− /// element.
917− meter;
918− /// Build a
919− /// [`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup)
920− /// element.
921− optgroup;
922− /// Build a
923− /// [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
924− /// element.
925− option;
926− /// Build a
927− /// [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output)
928− /// element.
929− output;
930− /// Build a
931− /// [`<progress>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)
932− /// element.
933− progress;
934− /// Build a
935− /// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
936− /// element.
937− select;
938− /// Build a
939− /// [`<textarea>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)
940− /// element.
941− textarea;
942−
943− // Interactive elements
944−
945− /// Build a
946− /// [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
947− /// element.
948− details;
949− /// Build a
950− /// [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog)
951− /// element.
952− dialog;
953− /// Build a
954− /// [`<menu>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu)
955− /// element.
956− menu;
957− /// Build a
958− /// [`<menuitem>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menuitem)
959− /// element.
960− menuitem;
961− /// Build a
962− /// [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
963− /// element.
964− summary;
965−
966− // Web components
967−
968− /// Build a
969− /// [`<slot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot)
970− /// element.
971− slot;
972− /// Build a
973− /// [`<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)
974− /// element.
975− template;
976−}
977−
978−builder_constructors! {
979− // SVG components
980−
981− /// Build a
982− /// [`<svg>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg)
983− /// element.
984− svg <> "http://www.w3.org/2000/svg" ;
985− /// Build a
986− /// [`<path>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path)
987− /// element.
988− path <> "http://www.w3.org/2000/svg";
989− /// Build a
990− /// [`<circle>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle)
991− /// element.
992− circle <> "http://www.w3.org/2000/svg";
993− /// Build a
994− /// [`<ellipse>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/ellipse)
995− /// element.
996− ellipse <> "http://www.w3.org/2000/svg";
997− /// Build a
998− /// [`<line>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/line)
999− /// element.
1000− line <> "http://www.w3.org/2000/svg";
1001− /// Build a
1002− /// [`<polygon>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon)
1003− /// element.
1004− polygon <> "http://www.w3.org/2000/svg";
1005− /// Build a
1006− /// [`<polyline>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polyline)
1007− /// element.
1008− polyline <> "http://www.w3.org/2000/svg";
1009− /// Build a
1010− /// [`<rect>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect)
1011− /// element.
1012− rect <> "http://www.w3.org/2000/svg";
1013− /// Build a
1014− /// [`<image>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image)
1015− /// element.
1016− image <> "http://www.w3.org/2000/svg";
1017−}
1018−
1019−/// Construct a text node.
1020−///
1021−/// This is `dodrio`'s virtual DOM equivalent of `document.createTextNode`.
1022−///
1023−/// # Example
1024−///
1025−/// ```no_run
1026−/// use dodrio::builder::*;
1027−///
1028−/// let my_text = text("hello, dodrio!");
1029−/// ```
1030−#[inline]
1031−pub fn text<'a>(contents: &'a str) -> Node<'a> {
1032− Node::text(contents)
1033−}
1034−
1035−/// Construct an attribute for an element.
1036−///
1037−/// # Example
1038−///
1039−/// This example creates the `id="my-id"` for some element like `<div
1040−/// id="my-id"/>`.
1041−///
1042−/// ```no_run
1043−/// use dodrio::builder::*;
1044−///
1045−/// let my_id_attr = attr("id", "my-id");
1046−/// ```
1047−pub fn attr<'a>(name: &'a str, value: &'a str) -> Attribute<'a> {
1048− Attribute { name, value }
1049−}
1050−
1051−/// Create an event listener.
1052−///
1053−/// `event` is the type of event to listen for, e.g. `"click"`. The `callback`
1054−/// is the function that will be invoked when the event occurs.
1055−///
1056−/// # Example
1057−///
1058−/// ```no_run
1059−/// use dodrio::{builder::*, bumpalo::Bump};
1060−///
1061−/// let b = Bump::new();
1062−///
1063−/// let listener = on(&b, "click", |root, vdom, event| {
1064−/// // do something when a click happens...
1065−/// });
1066−/// ```
1067−pub fn on<'a, F>(bump: &'a Bump, event: &'a str, callback: F) -> Listener<'a>
1068−where
1069− F: Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static,
1070−{
1071− Listener {
1072− event,
1073− callback: bump.alloc(callback),
1074− }
1075−}
dodrio/src/cached.rsdeleted−229
@@ -1,229 +0,0 @@
1−use crate::{
2− cached_set::CachedSet,
3− node::{CachedNode, NodeKey},
4− Node, Render, RenderContext,
5−};
6−use std::any::TypeId;
7−use std::cell::Cell;
8−use std::ops::{Deref, DerefMut};
9−
10−/// A renderable that supports caching for when rendering is expensive but can
11−/// generate the same DOM tree.
12−///
13−/// # Example
14−///
15−/// The [TodoMVC
16−/// example](https://github.com/fitzgen/dodrio/blob/master/examples/todomvc/src/todo.rs)
17−/// caches individual todo items.
18−#[derive(Clone, Debug, Default)]
19−pub struct Cached<R>
20−where
21− R: Default,
22−{
23− inner: R,
24− cached: Cell<Option<CachedNode>>,
25−}
26−
27−pub_unstable_internal! {
28− #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
29− pub(crate) struct TemplateId(TypeId);
30−}
31−
32−impl<R> Cached<R>
33−where
34− R: Default,
35−{
36− /// Construct a new `Cached<R>` of an inner `R`.
37− ///
38− /// # Example
39− ///
40− /// ```
41− /// use dodrio::{Cached, Node, Render, RenderContext};
42− ///
43− /// #[derive(Default)]
44− /// pub struct Counter {
45− /// count: u32,
46− /// }
47− ///
48− /// impl<'a> Render<'a> for Counter {
49− /// fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
50− /// // ...
51− /// # unimplemented!()
52− /// }
53− /// }
54− ///
55− /// // Create a render-able counter.
56− /// let counter = Counter { count: 0 };
57− ///
58− /// // And cache its rendering!
59− /// let cached_counter = Cached::new(counter);
60− /// ```
61− #[inline]
62− pub fn new(inner: R) -> Cached<R> {
63− let cached = Cell::new(None);
64− Cached { inner, cached }
65− }
66−
67− /// Invalidate the cached rendering.
68− ///
69− /// This method should be called whenever the inner `R` must be re-rendered,
70− /// and the cached `Node` from the last time `R::render` was invoked can no
71− /// longer be re-used.
72− ///
73− /// # Example
74− ///
75− /// The `Cached<Hello>` component must have its cache invalidated whenever
76− /// the `who` string is changed, or else the cached rendering will keep
77− /// displaying greetings to old `who`s.
78− ///
79− /// ```
80− /// use dodrio::{bumpalo, Cached, Node, Render, RenderContext};
81− ///
82− /// /// A component that renders to "<p>Hello, {who}!</p>"
83− /// #[derive(Default)]
84− /// pub struct Hello {
85− /// who: String
86− /// }
87− ///
88− /// impl<'a> Render<'a> for Hello {
89− /// fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
90− /// use dodrio::builder::*;
91− /// let greeting = bumpalo::format!(in cx.bump, "Hello, {}!", self.who);
92− /// p(&cx)
93− /// .children([text(greeting.into_bump_str())])
94− /// .finish()
95− /// }
96− /// }
97− ///
98− /// /// Whenever a `Cached<Hello>`'s `who` is updated, we need to invalidate the
99− /// /// cache so that we don't keep displaying greetings to old `who`s.
100− /// pub fn set_who(hello: &mut Cached<Hello>, who: String) {
101− /// hello.who = who;
102− /// Cached::invalidate(hello);
103− /// }
104− /// ```
105− #[inline]
106− pub fn invalidate(cached: &Self) {
107− cached.cached.set(None);
108− }
109−
110− /// Convert a `Cached<R>` back into a plain `R`.
111− #[inline]
112− pub fn into_inner(cached: Self) -> R {
113− cached.inner
114− }
115−}
116−
117−impl<R> Cached<R>
118−where
119− R: 'static + Default,
120−{
121− pub(crate) fn template_id() -> TemplateId {
122− TemplateId(TypeId::of::<R>())
123− }
124−}
125−
126−impl<R> Deref for Cached<R>
127−where
128− R: Default,
129−{
130− type Target = R;
131−
132− fn deref(&self) -> &R {
133− &self.inner
134− }
135−}
136−
137−impl<R> DerefMut for Cached<R>
138−where
139− R: Default,
140−{
141− fn deref_mut(&mut self) -> &mut R {
142− &mut self.inner
143− }
144−}
145−
146−impl<'a, R> Render<'a> for Cached<R>
147−where
148− R: 'static + Default + for<'b> Render<'b>,
149−{
150− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
151− let template = cx.template::<R>();
152− let cached = match self.cached.get() {
153− // This does-the-cache-contain-this-id check is necessary because
154− // the same `Cached<R>` instance can be rendered into vdom A, which
155− // will save the results into A's cached set and yield id X. Then,
156− // it can be rendered *again* into a second vdom B, and B's cached
157− // set does not have the saved results for id X. If we didn't have
158− // this check, instead of a cache miss, we would have a panic.
159− //
160− // This scenario should basically never happen in the real world,
161− // however. If we ever find that it is worth sharing a cached render
162− // component between multiple vdoms, and want to avoid these
163− // "unnecessary" cache misses, we can do this:
164− //
165− // * Make each `Cached<R>` have an instance id and generation
166− // counter.
167− //
168− // * On invalidation, bump the generation counter.
169− //
170− // * Add a generation member to the `CacheEntry`.
171− //
172− // * Each vdom maintains a map from `Cached<R>` instance id to
173− // `CacheEntry`s.
174− //
175− // * We only re-use the cached results if the `Cached<R>`'s
176− // generation counter matches the entry in the vdom's cached set.
177− //
178− // This is all do-able but is a bit more book keeping than we really
179− // want to do unless it is well motivated.
180− Some(cached)
181− if {
182− let cached_set = cx.cached_set.borrow();
183− cached_set.contains(cached.id)
184− } =>
185− {
186− cached
187− }
188− _ => {
189− let mut key = NodeKey::NONE;
190− let id = CachedSet::insert(cx, false, template, |nested_cx| {
191− let node = self.inner.render(nested_cx);
192− key = node.key();
193− node
194− });
195− let cached = CachedNode { id, key };
196− self.cached.set(Some(cached));
197− cached
198− }
199− };
200−
201− cached.into()
202− }
203−}
204−
205−#[cfg(feature = "serde")]
206−impl<R> serde::Serialize for Cached<R>
207−where
208− R: Default + serde::Serialize,
209−{
210− fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
211− where
212− S: serde::Serializer,
213− {
214− self.inner.serialize(serializer)
215− }
216−}
217−
218−#[cfg(feature = "serde")]
219−impl<'de, R> serde::Deserialize<'de> for Cached<R>
220−where
221− R: Default + serde::Deserialize<'de>,
222−{
223− fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
224− where
225− D: serde::Deserializer<'de>,
226− {
227− R::deserialize(deserializer).map(Cached::new)
228− }
229−}
dodrio/src/cached_set.rsdeleted−186
@@ -1,186 +0,0 @@
1−use crate::{
2− events::EventsRegistry,
3− node::{Node, NodeKind},
4− render_context::RenderContext,
5−};
6−use bumpalo::Bump;
7−use fxhash::{FxHashMap, FxHashSet};
8−use std::sync::atomic::{AtomicUsize, Ordering};
9−use std::u32;
10−use wasm_bindgen::prelude::*;
11−
12−static ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
13−
14−pub_unstable_internal! {
15− #[derive(Debug, Default)]
16− pub(crate) struct CachedSet {
17− items: FxHashMap<CacheId, CacheEntry>,
18− }
19−}
20−
21−pub_unstable_internal! {
22− #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
23− pub(crate) struct CacheId(u32);
24−}
25−
26−#[derive(Debug)]
27−pub(crate) struct CacheEntry {
28− bump: Bump,
29−
30− // Actually a reference into `bump`.
31− node: *const Node<'static>,
32−
33− // All the other cached entries that `node` references (including transitive
34− // edges). By eagerly following transitive edges and recording them the
35− // once, we don't have to repeat the tracing every time we want to garbage
36− // collect cache entries.
37− edges: FxHashSet<CacheId>,
38−
39− // The template for this cached virtual subtree.
40− //
41− // When a cache entry has a template, that means that the `ChangeList` will
42− // lazily build a physical DOM subtree based on the template, and when we
43− // create new versions of this cached result, we clone the template's
44− // physical DOM subtree and then modify it, rather than building the cached
45− // result up from scratch. This is a nice win because we bounce between JS
46− // and DOM methods less often, and get the C++ DOM implementation to do most
47− // of the subtree construction for us.
48− template: Option<CacheId>,
49−
50− // Whether this entry should never be garbage collected. Typically only
51− // templates are pinned.
52− pinned: bool,
53−}
54−
55−impl From<CacheId> for u32 {
56− #[inline]
57− fn from(id: CacheId) -> u32 {
58− id.0
59− }
60−}
61−
62−impl CachedSet {
63− pub(crate) fn new_roots_set(&self) -> FxHashSet<CacheId> {
64− let mut roots = FxHashSet::default();
65− roots.reserve(self.items.len());
66− roots
67− }
68−
69− pub(crate) fn gc(&mut self, registry: &mut EventsRegistry, roots: FxHashSet<CacheId>) {
70− let mut marked = FxHashSet::default();
71− marked.reserve(self.items.len());
72−
73− for root in roots {
74− if marked.insert(root) {
75− let entry = self
76− .items
77− .get(&root)
78− .expect_throw("CachedSet::gc: should have root in cached set");
79−
80− // NB: no need to recurse here because `edges` already contains
81− // transitive edges!
82− marked.extend(entry.edges.iter().cloned());
83− }
84− }
85−
86− self.items.retain(|id, entry| {
87− let keep = entry.pinned || marked.contains(id);
88− if !keep {
89− let node: &Node = unsafe { &*entry.node };
90− registry.remove_subtree(node);
91− }
92− keep
93− });
94− }
95−
96− // Trace all the transitive edges to other cached entries that the given
97− // node has.
98− fn trace(&self, node: &Node) -> FxHashSet<CacheId> {
99− let mut edges = FxHashSet::default();
100− self.trace_recursive(&mut edges, node);
101− edges
102− }
103−
104− fn trace_recursive(&self, edges: &mut FxHashSet<CacheId>, node: &Node) {
105− match &node.kind {
106− NodeKind::Text(_) => return,
107− NodeKind::Cached(c) => {
108− debug_assert!(self.items.contains_key(&c.id));
109− edges.insert(c.id);
110− edges.extend(
111− self.items
112− .get(&c.id)
113− .expect_throw("CachedSet::trace: should have c.id in cached set")
114− .edges
115− .iter()
116− .cloned(),
117− );
118− }
119− NodeKind::Element(el) => {
120− for child in el.children {
121− self.trace_recursive(edges, child);
122− }
123− }
124− }
125− }
126−
127− fn next_id(&mut self) -> CacheId {
128− let next = ID_COUNTER.fetch_add(1, Ordering::AcqRel) as u32;
129− let next = if next == u32::MAX { None } else { Some(next) };
130− CacheId(next.expect_throw("ID_COUNTER overflowed"))
131− }
132−
133− pub(crate) fn insert<F>(
134− cx: &mut RenderContext,
135− pinned: bool,
136− template: Option<CacheId>,
137− f: F,
138− ) -> CacheId
139− where
140− F: for<'a> FnOnce(&mut RenderContext<'a>) -> Node<'a>,
141− {
142− let set = cx.cached_set;
143− let bump = Bump::new();
144− let (node, edges) = {
145− let mut nested_cx = RenderContext::new(&bump, cx.cached_set, cx.templates);
146− let node = f(&mut nested_cx);
147− let node = bump.alloc(node);
148− let edges = {
149− let set = set.borrow();
150− set.trace(node)
151− };
152− (
153− node as *mut Node<'_> as usize as *const Node<'static>,
154− edges,
155− )
156− };
157−
158− let entry = CacheEntry {
159− bump,
160− node,
161− edges,
162− template,
163− pinned,
164− };
165−
166− let mut set = set.borrow_mut();
167− let id = set.next_id();
168− set.items.insert(id, entry);
169− id
170− }
171−
172− /// Does the cached set contain a cached node with the given id?
173− pub fn contains(&self, id: CacheId) -> bool {
174− self.items.contains_key(&id)
175− }
176−
177− /// Get the cached node and its template (if any) for the given cache id.
178− pub fn get(&self, id: CacheId) -> (&Node, Option<CacheId>) {
179− let entry = self
180− .items
181− .get(&id)
182− .expect_throw("CachedSet::get: should have id in set");
183− let node: &Node = unsafe { &*entry.node };
184− (node, entry.template)
185− }
186−}
dodrio/src/change_list/emitter.rsdeleted−292
@@ -1,292 +0,0 @@
1−//! The `InstructionEmitter` is responsible for encoding change list
2−//! instructions and ensuring that each instruction has the correct number of
3−//! immediates. It ensures that the resulting change list instruction stream is
4−//! *syntactically* correct (opcodes have the correct arity of immediate
5−//! arguments, etc), while `ChangeListBuilder` wraps an emitter and additionally
6−//! ensures that the resulting change list program is *semantically* correct
7−//! (doesn't reference cached strings before they've been added to the cache,
8−//! etc).
9−//!
10−//! We encode the instructions directly into a dedicated bump arena. We
11−//! eventually pass the bump arena's chunks to the interpreter in JS, so it is
12−//! critical that nothing other than change list instructions are allocated
13−//! inside this bump, and that the instructions themselves do not contain any
14−//! padding or uninitialized memory. See the documentation for the the
15−//! `Bump::each_allocated_chunk` method for details.
16−
17−use bumpalo::Bump;
18−
19−#[derive(Debug)]
20−pub(crate) struct InstructionEmitter {
21− bump: Bump,
22−}
23−
24−impl InstructionEmitter {
25− /// Construct a new `InstructionEmitter` with its own bump arena.
26− pub fn new() -> InstructionEmitter {
27− let bump = Bump::new();
28− InstructionEmitter { bump }
29− }
30−
31− /// Invoke the given function with each of the allocated instruction
32− /// sequences that this emitter has built up.
33− #[cfg_attr(feature = "xxx-unstable-internal-use-only", allow(dead_code))]
34− pub fn each_instruction_sequence<F>(&mut self, f: F)
35− where
36− F: FnMut(&[u8]),
37− {
38− // Note: the safety invariants required for `each_allocated_chunk` are
39− // maintained by the fact that everything we encode as allocations in
40− // the bump arena are in the form of `u32`s, and therefore we don't have
41− // any uninitialized memory padding in the arena.
42− unsafe {
43− self.bump.each_allocated_chunk(f);
44− }
45− }
46−
47− /// Reset to an empty sequence of instructions.
48− pub fn reset(&mut self) {
49− self.bump.reset();
50− }
51−}
52−
53−macro_rules! define_change_list_instructions {
54− ( $(
55− $( #[$attr:meta] )*
56− $name:ident (
57− $($immediate:ident),*
58− ) = $discriminant:expr,
59− )* ) => {
60− impl InstructionEmitter {
61− $(
62− $( #[$attr] )*
63− #[inline]
64− pub fn $name(&self $(, $immediate: u32)*) {
65− self.bump.alloc_with(|| [$discriminant $(, $immediate )* ]);
66− }
67− )*
68− }
69− }
70−}
71−
72−define_change_list_instructions! {
73− /// Stack: `[... TextNode] -> [... TextNode]`
74− ///
75− /// ```text
76− /// stack.top().textContent = readString(pointer, length)
77− /// ```
78− set_text(pointer, length) = 0,
79−
80− /// Stack: `[... Node] -> [...]`
81− ///
82− /// ```text
83− /// node = stack.pop()
84− /// while (node.nextSibling) {
85− /// node.nextSibling.remove();
86− /// }
87− /// node.remove()
88− /// ```
89− remove_self_and_next_siblings() = 1,
90−
91− /// Stack: `[... Node Node] -> [... Node]`
92− ///
93− /// ```text
94− /// new = stack.pop()
95− /// old = stack.pop()
96− /// old.replaceWith(new)
97− /// stack.push(new)
98− /// ```
99− replace_with() = 2,
100−
101− /// Stack: `[... Node] -> [... Node]`
102− ///
103− /// ```text
104− /// stack.top().setAttribute(getCachedString(attribute_key), getCachedString(value_key))
105− /// ```
106− set_attribute(attribute_key, value_key) = 3,
107−
108− /// Stack: `[... Node] -> [... Node]`
109− ///
110− /// ```text
111− /// stack.top().removeAttribute(getCachedString(attribute_key))
112− /// ```
113− remove_attribute(attribute_key) = 4,
114−
115− /// Stack: `[... Node] -> [... Node Node]`
116− ///
117− /// ```text
118− /// parent = stack.top()
119− /// child = parent.childNodes[parent.childNodes.length - n - 1]
120− /// stack.push(child)
121− /// ```
122− push_reverse_child(n) = 5,
123−
124− /// Stack: `[... Node Node] -> [... Node Node]`
125− ///
126− /// ```text
127− /// stack.pop();
128− /// parent = stack.top();
129− /// child = parent.childNodes[n]
130− /// stack.push(child)
131− /// ```
132− pop_push_child(n) = 6,
133−
134− /// Stack: `[... T] -> [...]`
135− ///
136− /// ```text
137− /// stack.pop()
138− /// ```
139− pop() = 7,
140−
141− /// Stack: `[... Node Node] -> [... Node]`
142− ///
143− /// ```text
144− /// child = stack.pop()
145− /// stack.top().appendChild(child)
146− /// ```
147− append_child() = 8,
148−
149− /// Stack: `[...] -> [... Node]`
150− ///
151− /// ```text
152− /// stack.push(document.createTextNode(readString(pointer, length)))
153− /// ```
154− create_text_node(pointer, length) = 9,
155−
156− /// Stack: `[...] -> [... Node]`
157− ///
158− /// ```text
159− /// stack.push(document.createElement(getCachedString(tag_name_key))
160− /// ```
161− create_element(tag_name_key) = 10,
162−
163− /// Stack: `[... Node] -> [... Node]`
164− ///
165− /// ```text
166− /// event = getCachedString(event_key)
167− /// callback = createProxyToRustCallback(a, b)
168− /// stack.top().addEventListener(event, callback)
169− /// ```
170− new_event_listener(event_key, a, b) = 11,
171−
172− /// Stack: `[... Node] -> [... Node]`
173− ///
174− /// ```text
175− /// event = getCachedString(event_key)
176− /// new_callback = createProxyToRustCallback(a, b);
177− /// stack.top().updateEventlistener(new_callback)
178− /// ```
179− update_event_listener(event_key, a, b) = 12,
180−
181− /// Stack: `[... Node] -> [... Node]`
182− ///
183− /// ```text
184− /// stack.top().removeEventListener(getCachedString(event_key));
185− /// ```
186− remove_event_listener(event_key) = 13,
187−
188− /// Stack: `[...] -> [...]`
189− ///
190− /// ```text
191− /// addCachedString(readString(pointer, length), key);
192− /// ```
193− add_cached_string(pointer, length, key) = 14,
194−
195− /// Stack: `[...] -> [...]`
196− ///
197− /// ```text
198− /// dropCachedString(key);
199− /// ```
200− drop_cached_string(key) = 15,
201−
202− /// Stack: `[...] -> [... Node]`
203− ///
204− /// ```text
205− /// tag_name = getCachedString(tag_name_key)
206− /// namespace = getCachedString(tag_name_key)
207− /// stack.push(document.createElementNS(tag_name, namespace))
208− /// ```
209− create_element_ns(tag_name_key, namespace_key) = 16,
210−
211− /// Stack: `[...] -> [...]`
212− ///
213− /// ```text
214− /// parent = stack.top()
215− /// children = parent.childNodes
216− /// temp = temp_base
217− /// for i in start .. end:
218− /// temporaries[temp] = children[i]
219− /// temp += 1
220− /// ```
221− save_children_to_temporaries(temp_base, start, end) = 17,
222−
223− /// Stack: `[... Node] -> [... Node Node]`
224− ///
225− /// ```text
226− /// parent = stack.top()
227− /// child = parent.childNodes[n]
228− /// stack.push(child)
229− /// ```
230− push_child(n) = 18,
231−
232− /// Stack: `[...] -> [... Node]`
233− ///
234− /// ```text
235− /// stack.push(temporaries[temp])
236− /// ```
237− push_temporary(temp) = 19,
238−
239− /// Stack: `[... Node Node] -> [... Node]`
240− ///
241− /// ```text
242− /// before = stack.pop()
243− /// after = stack.pop()
244− /// after.insertBefore(before)
245− /// stack.push(before)
246− /// ```
247− insert_before() = 20,
248−
249− /// Stack: `[... Node Node] -> [... Node Node]`
250− ///
251− /// ```text
252− /// stack.pop()
253− /// parent = stack.top()
254− /// child = parent.childNodes[parent.childNodes.length - n - 1]
255− /// stack.push(child)
256− /// ```
257− pop_push_reverse_child(n) = 21,
258−
259− /// Stack: `[... Node] -> [... Node]`
260− ///
261− /// ```text
262− /// parent = stack.top()
263− /// child = parent.childNodes[n]
264− /// child.remove()
265− /// ```
266− remove_child(n) = 22,
267−
268− /// Stack: `[... Node] -> [... Node]`
269− ///
270− /// ```text
271− /// class = getCachedString(class)
272− /// node = stack.top()
273− /// node.className = class
274− /// ```
275− set_class(class) = 23,
276−
277− /// Stack: `[... Node] -> [... Node]`
278− ///
279− /// ```text
280− /// template = stack.top()
281− /// saveTemplate(id, template)
282− /// ```
283− save_template(id) = 24,
284−
285− /// Stack: `[...] -> [... Node]`
286− ///
287− /// ```text
288− /// template = getTemplate(id)
289− /// stack.push(template.cloneNode(true))
290− /// ```
291− push_template(id) = 25,
292−}
dodrio/src/change_list/js.rsdeleted−40
@@ -1,40 +0,0 @@
1−cfg_if::cfg_if! {
2− if #[cfg(all(feature = "xxx-unstable-internal-use-only", not(target_arch = "wasm32")))] {
3− #[derive(Clone, Debug)]
4− pub struct ChangeListInterpreter {}
5− impl ChangeListInterpreter {
6− pub fn new(_container: &crate::Element) -> ChangeListInterpreter {
7− ChangeListInterpreter {}
8− }
9− pub fn unmount(&self) {}
10− pub fn add_change_list_range(&self, _start: usize, _len: usize) {}
11− pub fn init_events_trampoline(&self, _trampoline: &crate::EventsTrampoline) {}
12− }
13− } else {
14− use wasm_bindgen::prelude::*;
15−
16− #[wasm_bindgen(module = "/js/change-list-interpreter.js")]
17− extern "C" {
18− #[derive(Clone, Debug)]
19− pub type ChangeListInterpreter;
20−
21− #[wasm_bindgen(constructor)]
22− pub fn new(container: &web_sys::Element) -> ChangeListInterpreter;
23−
24− #[wasm_bindgen(structural, method)]
25− pub fn unmount(this: &ChangeListInterpreter);
26−
27− #[wasm_bindgen(structural, method, js_name = addChangeListRange)]
28− pub fn add_change_list_range(this: &ChangeListInterpreter, start: usize, len: usize);
29−
30− #[wasm_bindgen(structural, method, js_name = applyChanges)]
31− pub fn apply_changes(this: &ChangeListInterpreter, memory: JsValue);
32−
33− #[wasm_bindgen(structural, method, js_name = initEventsTrampoline)]
34− pub fn init_events_trampoline(
35− this: &ChangeListInterpreter,
36− trampoline: &crate::EventsTrampoline,
37− );
38− }
39− }
40−}
dodrio/src/change_list/mod.rsdeleted−353
@@ -1,353 +0,0 @@
1−pub(crate) mod emitter;
2−pub(crate) mod strings;
3−pub(crate) mod traversal;
4−
5−// Note: has to be `pub` because of `wasm-bindgen` visibility restrictions.
6−pub mod js;
7−
8−use self::emitter::InstructionEmitter;
9−use self::strings::{StringKey, StringsCache};
10−use self::traversal::{MoveTo, Traversal};
11−use crate::{cached_set::CacheId, Listener};
12−use fxhash::FxHashSet;
13−
14−#[derive(Debug)]
15−pub(crate) struct ChangeListPersistentState {
16− strings: StringsCache,
17− emitter: InstructionEmitter,
18− traversal: Traversal,
19− interpreter: js::ChangeListInterpreter,
20− templates: FxHashSet<CacheId>,
21−}
22−
23−pub(crate) struct ChangeListBuilder<'a> {
24− state: &'a mut ChangeListPersistentState,
25− next_temporary: u32,
26− forcing_new_listeners: bool,
27−}
28−
29−impl Drop for ChangeListPersistentState {
30− fn drop(&mut self) {
31− self.interpreter.unmount();
32− }
33−}
34−
35−impl ChangeListPersistentState {
36− pub(crate) fn new(container: &crate::Element) -> ChangeListPersistentState {
37− let strings = StringsCache::new();
38− let emitter = InstructionEmitter::new();
39− let traversal = Traversal::new();
40− let interpreter = js::ChangeListInterpreter::new(container);
41− let templates = Default::default();
42− ChangeListPersistentState {
43− strings,
44− emitter,
45− traversal,
46− interpreter,
47− templates,
48− }
49− }
50−
51− pub(crate) fn init_events_trampoline(&mut self, trampoline: &crate::EventsTrampoline) {
52− self.interpreter.init_events_trampoline(trampoline);
53− }
54−
55− pub(crate) fn builder<'a>(&'a mut self) -> ChangeListBuilder<'a> {
56− ChangeListBuilder {
57− state: self,
58− next_temporary: 0,
59− forcing_new_listeners: false,
60− }
61− }
62−}
63−
64−cfg_if::cfg_if! {
65− if #[cfg(all(feature = "xxx-unstable-internal-use-only", not(target_arch = "wasm32")))] {
66− impl ChangeListBuilder<'_> {
67− pub(crate) fn finish(self) {
68− self.state.strings.drop_unused_strings(&self.state.emitter);
69−
70− // Nothing to actually apply the changes to.
71−
72− self.state.emitter.reset();
73− self.state.traversal.reset();
74− }
75− }
76− } else {
77− impl ChangeListBuilder<'_> {
78− pub(crate) fn finish(self) {
79− self.state.strings.drop_unused_strings(&self.state.emitter);
80−
81− // Apply the changes.
82− let interpreter = &self.state.interpreter;
83− self.state.emitter.each_instruction_sequence(|seq| {
84− interpreter.add_change_list_range(seq.as_ptr() as usize, seq.len());
85− });
86− interpreter.apply_changes(wasm_bindgen::memory());
87−
88− self.state.emitter.reset();
89− self.state.traversal.reset();
90− }
91− }
92− }
93−}
94−
95−/// Traversal methods.
96−impl ChangeListBuilder<'_> {
97− pub fn go_down(&mut self) {
98− self.state.traversal.down();
99− }
100−
101− pub fn go_down_to_child(&mut self, index: usize) {
102− self.state.traversal.down();
103− self.state.traversal.sibling(index);
104− }
105−
106− pub fn go_down_to_reverse_child(&mut self, index: usize) {
107− self.state.traversal.down();
108− self.state.traversal.reverse_sibling(index);
109− }
110−
111− pub fn go_up(&mut self) {
112− self.state.traversal.up();
113− }
114−
115− pub fn go_to_sibling(&mut self, index: usize) {
116− self.state.traversal.sibling(index);
117− }
118−
119− pub fn go_to_temp_sibling(&mut self, temp: u32) {
120− self.state.traversal.up();
121− self.state.traversal.down_to_temp(temp);
122− }
123−
124− pub fn go_down_to_temp_child(&mut self, temp: u32) {
125− self.state.traversal.down_to_temp(temp);
126− }
127−
128− pub fn commit_traversal(&mut self) {
129− if self.state.traversal.is_committed() {
130− return;
131− }
132−
133− for mv in self.state.traversal.commit() {
134− match mv {
135− MoveTo::Parent => {
136− debug!("emit: pop");
137− self.state.emitter.pop();
138− }
139− MoveTo::Child(n) => {
140− debug!("emit: push_child({})", n);
141− self.state.emitter.push_child(n);
142− }
143− MoveTo::ReverseChild(n) => {
144− debug!("emit: push_reverse_child({})", n);
145− self.state.emitter.push_reverse_child(n);
146− }
147− MoveTo::Sibling(n) => {
148− debug!("emit: pop_push_child({})", n);
149− self.state.emitter.pop_push_child(n);
150− }
151− MoveTo::ReverseSibling(n) => {
152− debug!("emit: pop_push_reverse_child({})", n);
153− self.state.emitter.pop_push_reverse_child(n);
154− }
155− MoveTo::TempChild(temp) => {
156− debug!("emit: push_temporary({})", temp);
157− self.state.emitter.push_temporary(temp);
158− }
159− }
160− }
161− }
162−
163− pub fn traversal_is_committed(&self) -> bool {
164− self.state.traversal.is_committed()
165− }
166−}
167−
168−impl ChangeListBuilder<'_> {
169− pub fn next_temporary(&self) -> u32 {
170− self.next_temporary
171− }
172−
173− pub fn set_next_temporary(&mut self, next_temporary: u32) {
174− self.next_temporary = next_temporary;
175− }
176−
177− pub fn save_children_to_temporaries(&mut self, start: usize, end: usize) -> u32 {
178− debug_assert!(self.traversal_is_committed());
179− debug_assert!(start < end);
180− let temp_base = self.next_temporary;
181− debug!(
182− "emit: save_children_to_temporaries({}, {}, {})",
183− temp_base, start, end
184− );
185− self.next_temporary = temp_base + (end - start) as u32;
186− self.state
187− .emitter
188− .save_children_to_temporaries(temp_base, start as u32, end as u32);
189− temp_base
190− }
191−
192− pub fn push_temporary(&self, temp: u32) {
193− debug_assert!(self.traversal_is_committed());
194− debug!("emit: push_temporary({})", temp);
195− self.state.emitter.push_temporary(temp);
196− }
197−
198− pub fn remove_child(&self, child: usize) {
199− debug_assert!(self.traversal_is_committed());
200− debug!("emit: remove_child({})", child);
201− self.state.emitter.remove_child(child as u32);
202− }
203−
204− pub fn insert_before(&self) {
205− debug_assert!(self.traversal_is_committed());
206− debug!("emit: insert_before()");
207− self.state.emitter.insert_before();
208− }
209−
210− pub fn ensure_string(&mut self, string: &str) -> StringKey {
211− self.state
212− .strings
213− .ensure_string(string, &self.state.emitter)
214− }
215−
216− pub fn set_text(&self, text: &str) {
217− debug_assert!(self.traversal_is_committed());
218− debug!("emit: set_text({:?})", text);
219− self.state
220− .emitter
221− .set_text(text.as_ptr() as u32, text.len() as u32);
222− }
223−
224− pub fn remove_self_and_next_siblings(&self) {
225− debug_assert!(self.traversal_is_committed());
226− debug!("emit: remove_self_and_next_siblings()");
227− self.state.emitter.remove_self_and_next_siblings();
228− }
229−
230− pub fn replace_with(&self) {
231− debug_assert!(self.traversal_is_committed());
232− debug!("emit: replace_with()");
233− self.state.emitter.replace_with();
234− }
235−
236− pub fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool) {
237− debug_assert!(self.traversal_is_committed());
238− if name == "class" && !is_namespaced {
239− let class_id = self.ensure_string(value);
240− debug!("emit: set_class({:?})", value);
241− self.state.emitter.set_class(class_id.into());
242− } else {
243− let name_id = self.ensure_string(name);
244− let value_id = self.ensure_string(value);
245− debug!("emit: set_attribute({:?}, {:?})", name, value);
246− self.state
247− .emitter
248− .set_attribute(name_id.into(), value_id.into());
249− }
250− }
251−
252− pub fn remove_attribute(&mut self, name: &str) {
253− debug_assert!(self.traversal_is_committed());
254− debug!("emit: remove_attribute({:?})", name);
255− let name_id = self.ensure_string(name);
256− self.state.emitter.remove_attribute(name_id.into());
257− }
258−
259− pub fn append_child(&self) {
260− debug_assert!(self.traversal_is_committed());
261− debug!("emit: append_child()");
262− self.state.emitter.append_child();
263− }
264−
265− pub fn create_text_node(&self, text: &str) {
266− debug_assert!(self.traversal_is_committed());
267− debug!("emit: create_text_node({:?})", text);
268− self.state
269− .emitter
270− .create_text_node(text.as_ptr() as u32, text.len() as u32);
271− }
272−
273− pub fn create_element(&mut self, tag_name: &str) {
274− debug_assert!(self.traversal_is_committed());
275− debug!("emit: create_element({:?})", tag_name);
276− let tag_name_id = self.ensure_string(tag_name);
277− self.state.emitter.create_element(tag_name_id.into());
278− }
279−
280− pub fn create_element_ns(&mut self, tag_name: &str, ns: &str) {
281− debug_assert!(self.traversal_is_committed());
282− debug!("emit: create_element_ns({:?}, {:?})", tag_name, ns);
283− let tag_name_id = self.ensure_string(tag_name);
284− let ns_id = self.ensure_string(ns);
285− self.state
286− .emitter
287− .create_element_ns(tag_name_id.into(), ns_id.into());
288− }
289−
290− pub fn push_force_new_listeners(&mut self) -> bool {
291− let old = self.forcing_new_listeners;
292− self.forcing_new_listeners = true;
293− old
294− }
295−
296− pub fn pop_force_new_listeners(&mut self, previous: bool) {
297− debug_assert!(self.forcing_new_listeners);
298− self.forcing_new_listeners = previous;
299− }
300−
301− pub fn new_event_listener(&mut self, listener: &Listener) {
302− debug_assert!(self.traversal_is_committed());
303− debug!("emit: new_event_listener({:?})", listener);
304− let (a, b) = listener.get_callback_parts();
305− debug_assert!(a != 0);
306− let event_id = self.ensure_string(listener.event);
307− self.state.emitter.new_event_listener(event_id.into(), a, b);
308− }
309−
310− pub fn update_event_listener(&mut self, listener: &Listener) {
311− debug_assert!(self.traversal_is_committed());
312−
313− if self.forcing_new_listeners {
314− self.new_event_listener(listener);
315− return;
316− }
317−
318− debug!("emit: update_event_listener({:?})", listener);
319− let (a, b) = listener.get_callback_parts();
320− debug_assert!(a != 0);
321− let event_id = self.ensure_string(listener.event);
322− self.state
323− .emitter
324− .update_event_listener(event_id.into(), a, b);
325− }
326−
327− pub fn remove_event_listener(&mut self, event: &str) {
328− debug_assert!(self.traversal_is_committed());
329− debug!("emit: remove_event_listener({:?})", event);
330− let event_id = self.ensure_string(event);
331− self.state.emitter.remove_event_listener(event_id.into());
332− }
333−
334− #[inline]
335− pub fn has_template(&mut self, id: CacheId) -> bool {
336− self.state.templates.contains(&id)
337− }
338−
339− pub fn save_template(&mut self, id: CacheId) {
340− debug_assert!(self.traversal_is_committed());
341− debug_assert!(!self.has_template(id));
342− debug!("emit: save_template({:?})", id);
343− self.state.templates.insert(id);
344− self.state.emitter.save_template(id.into());
345− }
346−
347− pub fn push_template(&mut self, id: CacheId) {
348− debug_assert!(self.traversal_is_committed());
349− debug_assert!(self.has_template(id));
350− debug!("emit: push_template({:?})", id);
351− self.state.emitter.push_template(id.into());
352− }
353−}
dodrio/src/change_list/strings.rsdeleted−66
@@ -1,66 +0,0 @@
1−use crate::change_list::emitter::InstructionEmitter;
2−use fxhash::FxHashMap;
3−
4−#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5−pub struct StringKey(u32);
6−
7−impl From<StringKey> for u32 {
8− #[inline]
9− fn from(key: StringKey) -> u32 {
10− key.0
11− }
12−}
13−
14−#[derive(Debug)]
15−struct StringsCacheEntry {
16− key: StringKey,
17− used: bool,
18−}
19−
20−#[derive(Debug, Default)]
21−pub(crate) struct StringsCache {
22− entries: FxHashMap<String, StringsCacheEntry>,
23− next_string_key: u32,
24−}
25−
26−impl StringsCache {
27− /// Create a new, empty strings cache.
28− pub fn new() -> StringsCache {
29− Default::default()
30− }
31−
32− /// Ensure that the given string is cached, and get its key.
33− pub fn ensure_string(&mut self, string: &str, emitter: &InstructionEmitter) -> StringKey {
34− if let Some(entry) = self.entries.get_mut(string) {
35− entry.used = true;
36− entry.key
37− } else {
38− let key = StringKey(self.next_string_key);
39− self.next_string_key += 1;
40− let entry = StringsCacheEntry { key, used: true };
41− self.entries.insert(string.to_string(), entry);
42− emitter.add_cached_string(string.as_ptr() as u32, string.len() as u32, key.into());
43− key
44− }
45− }
46−
47− pub fn drop_unused_strings(&mut self, emitter: &InstructionEmitter) {
48− self.entries.retain(|string, entry| {
49− if entry.used {
50− // Since this entry was used during while rendering this frame,
51− // it is likely going to be used for the next frame as well. So
52− // we keep it in the cache so that we don't have to repopulate
53− // and resync the cache next frame (assuming it is reused), but
54− // we set the `used` flag to false so that if it is not used
55− // next frame, it will be cleaned up.
56− entry.used = false;
57− true
58− } else {
59− let key = entry.key.into();
60− debug!("emit: drop_cached_string({}) = {:?}", key, string);
61− emitter.drop_cached_string(key);
62− false
63− }
64− });
65− }
66−}
dodrio/src/change_list/traversal.rsdeleted−344
@@ -1,344 +0,0 @@
1−//! Keeps track of where we are moving in a DOM tree, and shortens traversal
2−//! paths between mutations to their minimal number of operations.
3−
4−#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5−pub enum MoveTo {
6− /// Move from the current node up to its parent.
7− Parent,
8−
9− /// Move to the current node's n^th child.
10− Child(u32),
11−
12− /// Move to the current node's n^th from last child.
13− ReverseChild(u32),
14−
15− /// Move to the n^th sibling. Not relative from the current
16− /// location. Absolute indexed within all of the current siblings.
17− Sibling(u32),
18−
19− /// Move to the n^th from last sibling. Not relative from the current
20− /// location. Absolute indexed within all of the current siblings.
21− ReverseSibling(u32),
22−
23− /// Move down to the given saved temporary child.
24− TempChild(u32),
25−}
26−
27−#[derive(Debug)]
28−pub struct Traversal {
29− uncommitted: Vec<MoveTo>,
30−}
31−
32−impl Traversal {
33− /// Construct a new `Traversal` with its internal storage backed by the
34− /// given bump arena.
35− pub fn new() -> Traversal {
36− Traversal {
37− uncommitted: Vec::with_capacity(32),
38− }
39− }
40−
41− /// Move the traversal up in the tree.
42− pub fn up(&mut self) {
43− match self.uncommitted.last() {
44− Some(MoveTo::Sibling(_)) | Some(MoveTo::ReverseSibling(_)) => {
45− self.uncommitted.pop();
46− self.uncommitted.push(MoveTo::Parent);
47− }
48− Some(MoveTo::TempChild(_)) | Some(MoveTo::Child(_)) | Some(MoveTo::ReverseChild(_)) => {
49− self.uncommitted.pop();
50− // And we're back at the parent.
51− }
52− _ => {
53− self.uncommitted.push(MoveTo::Parent);
54− }
55− }
56− }
57−
58− /// Move the traversal down in the tree to the first child of the current
59− /// node.
60− pub fn down(&mut self) {
61− if let Some(&MoveTo::Parent) = self.uncommitted.last() {
62− self.uncommitted.pop();
63− self.sibling(0);
64− } else {
65− self.uncommitted.push(MoveTo::Child(0));
66− }
67− }
68−
69− /// Move the traversal to the n^th sibling.
70− pub fn sibling(&mut self, index: usize) {
71− let index = index as u32;
72− match self.uncommitted.last_mut() {
73− Some(MoveTo::Sibling(ref mut n)) | Some(MoveTo::Child(ref mut n)) => {
74− *n = index;
75− }
76− Some(MoveTo::ReverseSibling(_)) => {
77− self.uncommitted.pop();
78− self.uncommitted.push(MoveTo::Sibling(index));
79− }
80− Some(MoveTo::TempChild(_)) | Some(MoveTo::ReverseChild(_)) => {
81− self.uncommitted.pop();
82− self.uncommitted.push(MoveTo::Child(index))
83− }
84− _ => {
85− self.uncommitted.push(MoveTo::Sibling(index));
86− }
87− }
88− }
89−
90− /// Move the the n^th from last sibling.
91− pub fn reverse_sibling(&mut self, index: usize) {
92− let index = index as u32;
93− match self.uncommitted.last_mut() {
94− Some(MoveTo::ReverseSibling(ref mut n)) | Some(MoveTo::ReverseChild(ref mut n)) => {
95− *n = index;
96− }
97− Some(MoveTo::Sibling(_)) => {
98− self.uncommitted.pop();
99− self.uncommitted.push(MoveTo::ReverseSibling(index));
100− }
101− Some(MoveTo::TempChild(_)) | Some(MoveTo::Child(_)) => {
102− self.uncommitted.pop();
103− self.uncommitted.push(MoveTo::ReverseChild(index))
104− }
105− _ => {
106− self.uncommitted.push(MoveTo::ReverseSibling(index));
107− }
108− }
109− }
110−
111− /// Go to the given saved temporary.
112− pub fn down_to_temp(&mut self, temp: u32) {
113− match self.uncommitted.last() {
114− Some(MoveTo::Sibling(_)) | Some(MoveTo::ReverseSibling(_)) => {
115− self.uncommitted.pop();
116− }
117− Some(MoveTo::Parent)
118− | Some(MoveTo::TempChild(_))
119− | Some(MoveTo::Child(_))
120− | Some(MoveTo::ReverseChild(_))
121− | None => {
122− // Can't remove moves to parents since we rely on their stack
123− // pops.
124− }
125− }
126− self.uncommitted.push(MoveTo::TempChild(temp));
127− }
128−
129− /// Are all the traversal's moves committed? That is, are there no moves
130− /// that have *not* been committed yet?
131− #[inline]
132− pub fn is_committed(&self) -> bool {
133− self.uncommitted.is_empty()
134− }
135−
136− /// Commit this traversals moves and return the optimized path from the last
137− /// commit.
138− #[inline]
139− pub fn commit(&mut self) -> Moves {
140− Moves {
141− inner: self.uncommitted.drain(..),
142− }
143− }
144−
145− #[inline]
146− pub fn reset(&mut self) {
147− self.uncommitted.clear();
148− }
149−}
150−
151−pub struct Moves<'a> {
152− inner: std::vec::Drain<'a, MoveTo>,
153−}
154−
155−impl Iterator for Moves<'_> {
156− type Item = MoveTo;
157−
158− #[inline]
159− fn next(&mut self) -> Option<MoveTo> {
160− self.inner.next()
161− }
162−}
163−
164−#[cfg(test)]
165−mod tests {
166− use super::*;
167−
168− #[test]
169− fn test_traversal() {
170− fn t<F>(f: F) -> Box<dyn FnMut(&mut Traversal)>
171− where
172− F: 'static + FnMut(&mut Traversal),
173− {
174− Box::new(f) as _
175− }
176−
177− for (mut traverse, expected_moves) in vec![
178− (
179− t(|t| {
180− t.down();
181− }),
182− vec![MoveTo::Child(0)],
183− ),
184− (
185− t(|t| {
186− t.up();
187− }),
188− vec![MoveTo::Parent],
189− ),
190− (
191− t(|t| {
192− t.sibling(42);
193− }),
194− vec![MoveTo::Sibling(42)],
195− ),
196− (
197− t(|t| {
198− t.down();
199− t.up();
200− }),
201− vec![],
202− ),
203− (
204− t(|t| {
205− t.down();
206− t.sibling(2);
207− t.up();
208− }),
209− vec![],
210− ),
211− (
212− t(|t| {
213− t.down();
214− t.sibling(3);
215− }),
216− vec![MoveTo::Child(3)],
217− ),
218− (
219− t(|t| {
220− t.down();
221− t.sibling(4);
222− t.sibling(8);
223− }),
224− vec![MoveTo::Child(8)],
225− ),
226− (
227− t(|t| {
228− t.sibling(1);
229− t.sibling(1);
230− }),
231− vec![MoveTo::Sibling(1)],
232− ),
233− (
234− t(|t| {
235− t.reverse_sibling(3);
236− }),
237− vec![MoveTo::ReverseSibling(3)],
238− ),
239− (
240− t(|t| {
241− t.down();
242− t.reverse_sibling(3);
243− }),
244− vec![MoveTo::ReverseChild(3)],
245− ),
246− (
247− t(|t| {
248− t.down();
249− t.reverse_sibling(3);
250− t.up();
251− }),
252− vec![],
253− ),
254− (
255− t(|t| {
256− t.down();
257− t.reverse_sibling(3);
258− t.reverse_sibling(6);
259− }),
260− vec![MoveTo::ReverseChild(6)],
261− ),
262− (
263− t(|t| {
264− t.up();
265− t.reverse_sibling(3);
266− t.reverse_sibling(6);
267− }),
268− vec![MoveTo::Parent, MoveTo::ReverseSibling(6)],
269− ),
270− (
271− t(|t| {
272− t.up();
273− t.sibling(3);
274− t.sibling(6);
275− }),
276− vec![MoveTo::Parent, MoveTo::Sibling(6)],
277− ),
278− (
279− t(|t| {
280− t.sibling(3);
281− t.sibling(6);
282− t.up();
283− }),
284− vec![MoveTo::Parent],
285− ),
286− (
287− t(|t| {
288− t.reverse_sibling(3);
289− t.reverse_sibling(6);
290− t.up();
291− }),
292− vec![MoveTo::Parent],
293− ),
294− (
295− t(|t| {
296− t.down();
297− t.down_to_temp(3);
298− }),
299− vec![MoveTo::Child(0), MoveTo::TempChild(3)],
300− ),
301− (
302− t(|t| {
303− t.down_to_temp(3);
304− t.sibling(5);
305− }),
306− vec![MoveTo::Child(5)],
307− ),
308− (
309− t(|t| {
310− t.down_to_temp(3);
311− t.reverse_sibling(5);
312− }),
313− vec![MoveTo::ReverseChild(5)],
314− ),
315− (
316− t(|t| {
317− t.down_to_temp(3);
318− t.up();
319− }),
320− vec![],
321− ),
322− (
323− t(|t| {
324− t.sibling(2);
325− t.up();
326− t.down_to_temp(3);
327− }),
328− vec![MoveTo::Parent, MoveTo::TempChild(3)],
329− ),
330− (
331− t(|t| {
332− t.up();
333− t.down_to_temp(3);
334− }),
335− vec![MoveTo::Parent, MoveTo::TempChild(3)],
336− ),
337− ] {
338− let mut traversal = Traversal::new();
339− traverse(&mut traversal);
340− let actual_moves: Vec<_> = traversal.commit().collect();
341− assert_eq!(actual_moves, expected_moves);
342− }
343− }
344−}
dodrio/src/diff.rsdeleted−1125
@@ -1,1125 +0,0 @@
1−use crate::{
2− cached_set::{CacheId, CachedSet},
3− change_list::ChangeListBuilder,
4− events::EventsRegistry,
5− node::{Attribute, ElementNode, Listener, Node, NodeKind, TextNode},
6−};
7−use fxhash::{FxHashMap, FxHashSet};
8−use std::cmp::Ordering;
9−use std::u32;
10−use wasm_bindgen::UnwrapThrowExt;
11−
12−// Diff the `old` node with the `new` node. Emits instructions to modify a
13−// physical DOM node that reflects `old` into something that reflects `new`.
14−//
15−// Upon entry to this function, the physical DOM node must be on the top of the
16−// change list stack:
17−//
18−// [... node]
19−//
20−// The change list stack is in the same state when this function exits.
21−pub(crate) fn diff(
22− cached_set: &CachedSet,
23− change_list: &mut ChangeListBuilder,
24− registry: &mut EventsRegistry,
25− old: &Node,
26− new: &Node,
27− cached_roots: &mut FxHashSet<CacheId>,
28−) {
29− match (&new.kind, &old.kind) {
30− (
31− &NodeKind::Text(TextNode { text: new_text }),
32− &NodeKind::Text(TextNode { text: old_text }),
33− ) => {
34− if new_text != old_text {
35− change_list.commit_traversal();
36− change_list.set_text(new_text);
37− }
38− }
39−
40− (&NodeKind::Text(_), &NodeKind::Element(_)) => {
41− change_list.commit_traversal();
42− create(cached_set, change_list, registry, new, cached_roots);
43− registry.remove_subtree(&old);
44− change_list.replace_with();
45− }
46−
47− (&NodeKind::Element(_), &NodeKind::Text(_)) => {
48− change_list.commit_traversal();
49− create(cached_set, change_list, registry, new, cached_roots);
50− // Note: text nodes cannot have event listeners, so we don't need to
51− // remove the old node's listeners from our registry her.
52− change_list.replace_with();
53− }
54−
55− (
56− &NodeKind::Element(ElementNode {
57− key: _,
58− tag_name: new_tag_name,
59− listeners: new_listeners,
60− attributes: new_attributes,
61− children: new_children,
62− namespace: new_namespace,
63− }),
64− &NodeKind::Element(ElementNode {
65− key: _,
66− tag_name: old_tag_name,
67− listeners: old_listeners,
68− attributes: old_attributes,
69− children: old_children,
70− namespace: old_namespace,
71− }),
72− ) => {
73− if new_tag_name != old_tag_name || new_namespace != old_namespace {
74− change_list.commit_traversal();
75− create(cached_set, change_list, registry, new, cached_roots);
76− registry.remove_subtree(&old);
77− change_list.replace_with();
78− return;
79− }
80− diff_listeners(change_list, registry, old_listeners, new_listeners);
81− diff_attributes(change_list, old_attributes, new_attributes, new_namespace.is_some());
82− diff_children(
83− cached_set,
84− change_list,
85− registry,
86− old_children,
87− new_children,
88− cached_roots,
89− );
90− }
91−
92− // Both the new and old nodes are cached.
93− (&NodeKind::Cached(ref new), &NodeKind::Cached(ref old)) => {
94− cached_roots.insert(new.id);
95−
96− if new.id == old.id {
97− // This is the same cached node, so nothing has changed!
98− return;
99− }
100−
101− let (new, new_template) = cached_set.get(new.id);
102− let (old, old_template) = cached_set.get(old.id);
103− if new_template == old_template {
104− // If they are both using the same template, then just diff the
105− // subtrees.
106− diff(cached_set, change_list, registry, old, new, cached_roots);
107− } else {
108− // Otherwise, they are probably different enough that
109− // re-constructing the subtree from scratch should be faster.
110− // This doubly holds true if we have a new template.
111− change_list.commit_traversal();
112− create_and_replace(
113− cached_set,
114− change_list,
115− registry,
116− new_template,
117− old,
118− new,
119− cached_roots,
120− );
121− }
122− }
123−
124− // New cached node when the old node was not cached. In this scenario,
125− // we assume that they are pretty different, and it isn't worth diffing
126− // the subtrees, so we just create the new cached node afresh.
127− (&NodeKind::Cached(ref c), _) => {
128− change_list.commit_traversal();
129− cached_roots.insert(c.id);
130− let (new, new_template) = cached_set.get(c.id);
131− create_and_replace(
132− cached_set,
133− change_list,
134− registry,
135− new_template,
136− old,
137− new,
138− cached_roots,
139− );
140− }
141−
142− // Old cached node and new non-cached node. Again, assume that they are
143− // probably pretty different and create the new non-cached node afresh.
144− (_, &NodeKind::Cached(_)) => {
145− change_list.commit_traversal();
146− create(cached_set, change_list, registry, new, cached_roots);
147− registry.remove_subtree(&old);
148− change_list.replace_with();
149− }
150− }
151−}
152−
153−// Diff event listeners between `old` and `new`.
154−//
155−// The listeners' node must be on top of the change list stack:
156−//
157−// [... node]
158−//
159−// The change list stack is left unchanged.
160−fn diff_listeners(
161− change_list: &mut ChangeListBuilder,
162− registry: &mut EventsRegistry,
163− old: &[Listener],
164− new: &[Listener],
165−) {
166− if !old.is_empty() || !new.is_empty() {
167− change_list.commit_traversal();
168− }
169−
170− 'outer1: for new_l in new {
171− unsafe {
172− // Safety relies on removing `new_l` from the registry manually at
173− // the end of its lifetime. This happens below in the `'outer2`
174− // loop, and elsewhere in diffing when removing old dom trees.
175− registry.add(new_l);
176− }
177−
178− for old_l in old {
179− if new_l.event == old_l.event {
180− change_list.update_event_listener(new_l);
181− continue 'outer1;
182− }
183− }
184−
185− change_list.new_event_listener(new_l);
186− }
187−
188− 'outer2: for old_l in old {
189− registry.remove(old_l);
190−
191− for new_l in new {
192− if new_l.event == old_l.event {
193− continue 'outer2;
194− }
195− }
196− change_list.remove_event_listener(old_l.event);
197− }
198−}
199−
200−// Diff a node's attributes.
201−//
202−// The attributes' node must be on top of the change list stack:
203−//
204−// [... node]
205−//
206−// The change list stack is left unchanged.
207−fn diff_attributes(change_list: &mut ChangeListBuilder, old: &[Attribute], new: &[Attribute], is_namespaced: bool) {
208− // Do O(n^2) passes to add/update and remove attributes, since
209− // there are almost always very few attributes.
210− 'outer: for new_attr in new {
211− if new_attr.is_volatile() {
212− change_list.commit_traversal();
213− change_list.set_attribute(new_attr.name, new_attr.value, is_namespaced);
214− } else {
215− for old_attr in old {
216− if old_attr.name == new_attr.name {
217− if old_attr.value != new_attr.value {
218− change_list.commit_traversal();
219− change_list.set_attribute(new_attr.name, new_attr.value, is_namespaced);
220− }
221− continue 'outer;
222− }
223− }
224−
225− change_list.commit_traversal();
226− change_list.set_attribute(new_attr.name, new_attr.value, is_namespaced);
227− }
228− }
229−
230− 'outer2: for old_attr in old {
231− for new_attr in new {
232− if old_attr.name == new_attr.name {
233− continue 'outer2;
234− }
235− }
236−
237− change_list.commit_traversal();
238− change_list.remove_attribute(old_attr.name);
239− }
240−}
241−
242−// Diff the given set of old and new children.
243−//
244−// The parent must be on top of the change list stack when this function is
245−// entered:
246−//
247−// [... parent]
248−//
249−// the change list stack is in the same state when this function returns.
250−fn diff_children(
251− cached_set: &CachedSet,
252− change_list: &mut ChangeListBuilder,
253− registry: &mut EventsRegistry,
254− old: &[Node],
255− new: &[Node],
256− cached_roots: &mut FxHashSet<CacheId>,
257−) {
258− if new.is_empty() {
259− if !old.is_empty() {
260− change_list.commit_traversal();
261− remove_all_children(change_list, registry, old);
262− }
263− return;
264− }
265−
266− if new.len() == 1 {
267− match (old.first(), &new[0]) {
268− (
269− Some(&Node {
270− kind: NodeKind::Text(TextNode { text: old_text }),
271− }),
272− &Node {
273− kind: NodeKind::Text(TextNode { text: new_text }),
274− },
275− ) if old_text == new_text => {
276− // Don't take this fast path...
277− }
278− (
279− _,
280− &Node {
281− kind: NodeKind::Text(TextNode { text }),
282− },
283− ) => {
284− change_list.commit_traversal();
285− change_list.set_text(text);
286− for o in old {
287− registry.remove_subtree(o);
288− }
289− return;
290− }
291− (_, _) => {}
292− }
293− }
294−
295− if old.is_empty() {
296− if !new.is_empty() {
297− change_list.commit_traversal();
298− create_and_append_children(cached_set, change_list, registry, new, cached_roots);
299− }
300− return;
301− }
302−
303− let new_is_keyed = new[0].key().is_some();
304− let old_is_keyed = old[0].key().is_some();
305−
306− debug_assert!(
307− new.iter().all(|n| n.key().is_some() == new_is_keyed),
308− "all siblings must be keyed or all siblings must be non-keyed"
309− );
310− debug_assert!(
311− old.iter().all(|o| o.key().is_some() == old_is_keyed),
312− "all siblings must be keyed or all siblings must be non-keyed"
313− );
314−
315− if new_is_keyed && old_is_keyed {
316− let t = change_list.next_temporary();
317− diff_keyed_children(cached_set, change_list, registry, old, new, cached_roots);
318− change_list.set_next_temporary(t);
319− } else {
320− diff_non_keyed_children(cached_set, change_list, registry, old, new, cached_roots);
321− }
322−}
323−
324−// Diffing "keyed" children.
325−//
326−// With keyed children, we care about whether we delete, move, or create nodes
327−// versus mutate existing nodes in place. Presumably there is some sort of CSS
328−// transition animation that makes the virtual DOM diffing algorithm
329−// observable. By specifying keys for nodes, we know which virtual DOM nodes
330−// must reuse (or not reuse) the same physical DOM nodes.
331−//
332−// This is loosely based on Inferno's keyed patching implementation. However, we
333−// have to modify the algorithm since we are compiling the diff down into change
334−// list instructions that will be executed later, rather than applying the
335−// changes to the DOM directly as we compare virtual DOMs.
336−//
337−// https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
338−//
339−// When entering this function, the parent must be on top of the change list
340−// stack:
341−//
342−// [... parent]
343−//
344−// Upon exiting, the change list stack is in the same state.
345−fn diff_keyed_children(
346− cached_set: &CachedSet,
347− change_list: &mut ChangeListBuilder,
348− registry: &mut EventsRegistry,
349− old: &[Node],
350− new: &[Node],
351− cached_roots: &mut FxHashSet<CacheId>,
352−) {
353− if cfg!(debug_assertions) {
354− let mut keys = FxHashSet::default();
355− let mut assert_unique_keys = |children: &[Node]| {
356− keys.clear();
357− for child in children {
358− let key = child.key();
359− debug_assert!(
360− key.is_some(),
361− "if any sibling is keyed, all siblings must be keyed"
362− );
363− keys.insert(key);
364− }
365− debug_assert_eq!(
366− children.len(),
367− keys.len(),
368− "keyed siblings must each have a unique key"
369− );
370− };
371− assert_unique_keys(old);
372− assert_unique_keys(new);
373− }
374−
375− // First up, we diff all the nodes with the same key at the beginning of the
376− // children.
377− //
378− // `shared_prefix_count` is the count of how many nodes at the start of
379− // `new` and `old` share the same keys.
380− let shared_prefix_count =
381− match diff_keyed_prefix(cached_set, change_list, registry, old, new, cached_roots) {
382− KeyedPrefixResult::Finished => return,
383− KeyedPrefixResult::MoreWorkToDo(count) => count,
384− };
385−
386− // Next, we find out how many of the nodes at the end of the children have
387− // the same key. We do _not_ diff them yet, since we want to emit the change
388− // list instructions such that they can be applied in a single pass over the
389− // DOM. Instead, we just save this information for later.
390− //
391− // `shared_suffix_count` is the count of how many nodes at the end of `new`
392− // and `old` share the same keys.
393− let shared_suffix_count = old[shared_prefix_count..]
394− .iter()
395− .rev()
396− .zip(new[shared_prefix_count..].iter().rev())
397− .take_while(|&(old, new)| old.key() == new.key())
398− .count();
399−
400− let old_shared_suffix_start = old.len() - shared_suffix_count;
401− let new_shared_suffix_start = new.len() - shared_suffix_count;
402−
403− // Ok, we now hopefully have a smaller range of children in the middle
404− // within which to re-order nodes with the same keys, remove old nodes with
405− // now-unused keys, and create new nodes with fresh keys.
406− diff_keyed_middle(
407− cached_set,
408− change_list,
409− registry,
410− &old[shared_prefix_count..old_shared_suffix_start],
411− &new[shared_prefix_count..new_shared_suffix_start],
412− cached_roots,
413− shared_prefix_count,
414− shared_suffix_count,
415− old_shared_suffix_start,
416− );
417−
418− // Finally, diff the nodes at the end of `old` and `new` that share keys.
419− let old_suffix = &old[old_shared_suffix_start..];
420− let new_suffix = &new[new_shared_suffix_start..];
421− debug_assert_eq!(old_suffix.len(), new_suffix.len());
422− if !old_suffix.is_empty() {
423− diff_keyed_suffix(
424− cached_set,
425− change_list,
426− registry,
427− old_suffix,
428− new_suffix,
429− cached_roots,
430− new_shared_suffix_start,
431− );
432− }
433−}
434−
435−enum KeyedPrefixResult {
436− // Fast path: we finished diffing all the children just by looking at the
437− // prefix of shared keys!
438− Finished,
439− // There is more diffing work to do. Here is a count of how many children at
440− // the beginning of `new` and `old` we already processed.
441− MoreWorkToDo(usize),
442−}
443−
444−// Diff the prefix of children in `new` and `old` that share the same keys in
445−// the same order.
446−//
447−// Upon entry of this function, the change list stack must be:
448−//
449−// [... parent]
450−//
451−// Upon exit, the change list stack is the same.
452−fn diff_keyed_prefix(
453− cached_set: &CachedSet,
454− change_list: &mut ChangeListBuilder,
455− registry: &mut EventsRegistry,
456− old: &[Node],
457− new: &[Node],
458− cached_roots: &mut FxHashSet<CacheId>,
459−) -> KeyedPrefixResult {
460− change_list.go_down();
461− let mut shared_prefix_count = 0;
462−
463− for (i, (old, new)) in old.iter().zip(new.iter()).enumerate() {
464− if old.key() != new.key() {
465− break;
466− }
467−
468− change_list.go_to_sibling(i);
469− diff(cached_set, change_list, registry, old, new, cached_roots);
470− shared_prefix_count += 1;
471− }
472−
473− // If that was all of the old children, then create and append the remaining
474− // new children and we're finished.
475− if shared_prefix_count == old.len() {
476− change_list.go_up();
477− change_list.commit_traversal();
478− create_and_append_children(
479− cached_set,
480− change_list,
481− registry,
482− &new[shared_prefix_count..],
483− cached_roots,
484− );
485− return KeyedPrefixResult::Finished;
486− }
487−
488− // And if that was all of the new children, then remove all of the remaining
489− // old children and we're finished.
490− if shared_prefix_count == new.len() {
491− change_list.go_to_sibling(shared_prefix_count);
492− change_list.commit_traversal();
493− remove_self_and_next_siblings(change_list, registry, &old[shared_prefix_count..]);
494− return KeyedPrefixResult::Finished;
495− }
496−
497− change_list.go_up();
498− KeyedPrefixResult::MoreWorkToDo(shared_prefix_count)
499−}
500−
501−// The most-general, expensive code path for keyed children diffing.
502−//
503−// We find the longest subsequence within `old` of children that are relatively
504−// ordered the same way in `new` (via finding a longest-increasing-subsequence
505−// of the old child's index within `new`). The children that are elements of
506−// this subsequence will remain in place, minimizing the number of DOM moves we
507−// will have to do.
508−//
509−// Upon entry to this function, the change list stack must be:
510−//
511−// [... parent]
512−//
513−// Upon exit from this function, it will be restored to that same state.
514−fn diff_keyed_middle(
515− cached_set: &CachedSet,
516− change_list: &mut ChangeListBuilder,
517− registry: &mut EventsRegistry,
518− old: &[Node],
519− mut new: &[Node],
520− cached_roots: &mut FxHashSet<CacheId>,
521− shared_prefix_count: usize,
522− shared_suffix_count: usize,
523− old_shared_suffix_start: usize,
524−) {
525− // Should have already diffed the shared-key prefixes and suffixes.
526− debug_assert_ne!(new.first().map(|n| n.key()), old.first().map(|o| o.key()));
527− debug_assert_ne!(new.last().map(|n| n.key()), old.last().map(|o| o.key()));
528−
529− // The algorithm below relies upon using `u32::MAX` as a sentinel
530− // value, so if we have that many new nodes, it won't work. This
531− // check is a bit academic (hence only enabled in debug), since
532− // wasm32 doesn't have enough address space to hold that many nodes
533− // in memory.
534− debug_assert!(new.len() < u32::MAX as usize);
535−
536− // Map from each `old` node's key to its index within `old`.
537− let mut old_key_to_old_index = FxHashMap::default();
538− old_key_to_old_index.reserve(old.len());
539− old_key_to_old_index.extend(old.iter().enumerate().map(|(i, o)| (o.key(), i)));
540−
541− // The set of shared keys between `new` and `old`.
542− let mut shared_keys = FxHashSet::default();
543− // Map from each index in `new` to the index of the node in `old` that
544− // has the same key.
545− let mut new_index_to_old_index = Vec::with_capacity(new.len());
546− new_index_to_old_index.extend(new.iter().map(|n| {
547− let key = n.key();
548− if let Some(&i) = old_key_to_old_index.get(&key) {
549− shared_keys.insert(key);
550− i
551− } else {
552− u32::MAX as usize
553− }
554− }));
555−
556− // If none of the old keys are reused by the new children, then we
557− // remove all the remaining old children and create the new children
558− // afresh.
559− if shared_suffix_count == 0 && shared_keys.is_empty() {
560− if shared_prefix_count == 0 {
561− change_list.commit_traversal();
562− remove_all_children(change_list, registry, old);
563− } else {
564− change_list.go_down_to_child(shared_prefix_count);
565− change_list.commit_traversal();
566− remove_self_and_next_siblings(change_list, registry, &old[shared_prefix_count..]);
567− }
568− create_and_append_children(cached_set, change_list, registry, new, cached_roots);
569− return;
570− }
571−
572− // Save each of the old children whose keys are reused in the new
573− // children.
574− let mut old_index_to_temp = vec![u32::MAX; old.len()];
575− let mut start = 0;
576− loop {
577− let end = (start..old.len())
578− .find(|&i| {
579− let key = old[i].key();
580− !shared_keys.contains(&key)
581− })
582− .unwrap_or(old.len());
583−
584− if end - start > 0 {
585− change_list.commit_traversal();
586− let mut t = change_list.save_children_to_temporaries(
587− shared_prefix_count + start,
588− shared_prefix_count + end,
589− );
590− for i in start..end {
591− old_index_to_temp[i] = t;
592− t += 1;
593− }
594− }
595−
596− debug_assert!(end <= old.len());
597− if end == old.len() {
598− break;
599− } else {
600− start = end + 1;
601− }
602− }
603−
604− // Remove any old children whose keys were not reused in the new
605− // children. Remove from the end first so that we don't mess up indices.
606− let mut removed_count = 0;
607− for (i, old_child) in old.iter().enumerate().rev() {
608− if !shared_keys.contains(&old_child.key()) {
609− registry.remove_subtree(old_child);
610− change_list.commit_traversal();
611− change_list.remove_child(i + shared_prefix_count);
612− removed_count += 1;
613− }
614− }
615−
616− // If there aren't any more new children, then we are done!
617− if new.is_empty() {
618− return;
619− }
620−
621− // The longest increasing subsequence within `new_index_to_old_index`. This
622− // is the longest sequence on DOM nodes in `old` that are relatively ordered
623− // correctly within `new`. We will leave these nodes in place in the DOM,
624− // and only move nodes that are not part of the LIS. This results in the
625− // maximum number of DOM nodes left in place, AKA the minimum number of DOM
626− // nodes moved.
627− let mut new_index_is_in_lis = FxHashSet::default();
628− new_index_is_in_lis.reserve(new_index_to_old_index.len());
629− let mut predecessors = vec![0; new_index_to_old_index.len()];
630− let mut starts = vec![0; new_index_to_old_index.len()];
631− longest_increasing_subsequence::lis_with(
632− &new_index_to_old_index,
633− &mut new_index_is_in_lis,
634− |a, b| a < b,
635− &mut predecessors,
636− &mut starts,
637− );
638−
639− // Now we will iterate from the end of the new children back to the
640− // beginning, diffing old children we are reusing and if they aren't in the
641− // LIS moving them to their new destination, or creating new children. Note
642− // that iterating in reverse order lets us use `Node.prototype.insertBefore`
643− // to move/insert children.
644− //
645− // But first, we ensure that we have a child on the change list stack that
646− // we can `insertBefore`. We handle this once before looping over `new`
647− // children, so that we don't have to keep checking on every loop iteration.
648− if shared_suffix_count > 0 {
649− // There is a shared suffix after these middle children. We will be
650− // inserting before that shared suffix, so add the first child of that
651− // shared suffix to the change list stack.
652− //
653− // [... parent]
654− change_list.go_down_to_child(old_shared_suffix_start - removed_count);
655− // [... parent first_child_of_shared_suffix]
656− } else {
657− // There is no shared suffix coming after these middle children.
658− // Therefore we have to process the last child in `new` and move it to
659− // the end of the parent's children if it isn't already there.
660− let last_index = new.len() - 1;
661− let last = new.last().unwrap_throw();
662− new = &new[..new.len() - 1];
663− if shared_keys.contains(&last.key()) {
664− let old_index = new_index_to_old_index[last_index];
665− let temp = old_index_to_temp[old_index];
666− // [... parent]
667− change_list.go_down_to_temp_child(temp);
668− // [... parent last]
669− diff(
670− cached_set,
671− change_list,
672− registry,
673− &old[old_index],
674− last,
675− cached_roots,
676− );
677− if new_index_is_in_lis.contains(&last_index) {
678− // Don't move it, since it is already where it needs to be.
679− } else {
680− change_list.commit_traversal();
681− // [... parent last]
682− change_list.append_child();
683− // [... parent]
684− change_list.go_down_to_temp_child(temp);
685− // [... parent last]
686− }
687− } else {
688− change_list.commit_traversal();
689− // [... parent]
690− create(cached_set, change_list, registry, last, cached_roots);
691− // [... parent last]
692− change_list.append_child();
693− // [... parent]
694− change_list.go_down_to_reverse_child(0);
695− // [... parent last]
696− }
697− }
698−
699− for (new_index, new_child) in new.iter().enumerate().rev() {
700− let old_index = new_index_to_old_index[new_index];
701− if old_index == u32::MAX as usize {
702− debug_assert!(!shared_keys.contains(&new_child.key()));
703− change_list.commit_traversal();
704− // [... parent successor]
705− create(cached_set, change_list, registry, new_child, cached_roots);
706− // [... parent successor new_child]
707− change_list.insert_before();
708− // [... parent new_child]
709− } else {
710− debug_assert!(shared_keys.contains(&new_child.key()));
711− let temp = old_index_to_temp[old_index];
712− debug_assert_ne!(temp, u32::MAX);
713−
714− if new_index_is_in_lis.contains(&new_index) {
715− // [... parent successor]
716− change_list.go_to_temp_sibling(temp);
717− // [... parent new_child]
718− } else {
719− change_list.commit_traversal();
720− // [... parent successor]
721− change_list.push_temporary(temp);
722− // [... parent successor new_child]
723− change_list.insert_before();
724− // [... parent new_child]
725− }
726−
727− diff(
728− cached_set,
729− change_list,
730− registry,
731− &old[old_index],
732− new_child,
733− cached_roots,
734− );
735− }
736− }
737−
738− // [... parent child]
739− change_list.go_up();
740− // [... parent]
741−}
742−
743−// Diff the suffix of keyed children that share the same keys in the same order.
744−//
745−// The parent must be on the change list stack when we enter this function:
746−//
747−// [... parent]
748−//
749−// When this function exits, the change list stack remains the same.
750−fn diff_keyed_suffix(
751− cached_set: &CachedSet,
752− change_list: &mut ChangeListBuilder,
753− registry: &mut EventsRegistry,
754− old: &[Node],
755− new: &[Node],
756− cached_roots: &mut FxHashSet<CacheId>,
757− new_shared_suffix_start: usize,
758−) {
759− debug_assert_eq!(old.len(), new.len());
760− debug_assert!(!old.is_empty());
761−
762− // [... parent]
763− change_list.go_down();
764− // [... parent new_child]
765−
766− for (i, (old_child, new_child)) in old.iter().zip(new.iter()).enumerate() {
767− change_list.go_to_sibling(new_shared_suffix_start + i);
768− diff(
769− cached_set,
770− change_list,
771− registry,
772− old_child,
773− new_child,
774− cached_roots,
775− );
776− }
777−
778− // [... parent]
779− change_list.go_up();
780−}
781−
782−// Diff children that are not keyed.
783−//
784−// The parent must be on the top of the change list stack when entering this
785−// function:
786−//
787−// [... parent]
788−//
789−// the change list stack is in the same state when this function returns.
790−fn diff_non_keyed_children(
791− cached_set: &CachedSet,
792− change_list: &mut ChangeListBuilder,
793− registry: &mut EventsRegistry,
794− old: &[Node],
795− new: &[Node],
796− cached_roots: &mut FxHashSet<CacheId>,
797−) {
798− // Handled these cases in `diff_children` before calling this function.
799− debug_assert!(!new.is_empty());
800− debug_assert!(!old.is_empty());
801−
802− // [... parent]
803− change_list.go_down();
804− // [... parent child]
805−
806− for (i, (new_child, old_child)) in new.iter().zip(old.iter()).enumerate() {
807− // [... parent prev_child]
808− change_list.go_to_sibling(i);
809− // [... parent this_child]
810− diff(
811− cached_set,
812− change_list,
813− registry,
814− old_child,
815− new_child,
816− cached_roots,
817− );
818− }
819−
820− match old.len().cmp(&new.len()) {
821− Ordering::Greater => {
822− // [... parent prev_child]
823− change_list.go_to_sibling(new.len());
824− // [... parent first_child_to_remove]
825− change_list.commit_traversal();
826− remove_self_and_next_siblings(change_list, registry, &old[new.len()..]);
827− // [... parent]
828− }
829− Ordering::Less => {
830− // [... parent last_child]
831− change_list.go_up();
832− // [... parent]
833− change_list.commit_traversal();
834− create_and_append_children(
835− cached_set,
836− change_list,
837− registry,
838− &new[old.len()..],
839− cached_roots,
840− );
841− }
842− Ordering::Equal => {
843− // [... parent child]
844− change_list.go_up();
845− // [... parent]
846− }
847− }
848−}
849−
850−// Create the given children and append them to the parent node.
851−//
852−// The parent node must currently be on top of the change list stack:
853−//
854−// [... parent]
855−//
856−// When this function returns, the change list stack is in the same state.
857−fn create_and_append_children(
858− cached_set: &CachedSet,
859− change_list: &mut ChangeListBuilder,
860− registry: &mut EventsRegistry,
861− new: &[Node],
862− cached_roots: &mut FxHashSet<CacheId>,
863−) {
864− debug_assert!(change_list.traversal_is_committed());
865− for child in new {
866− create(cached_set, change_list, registry, child, cached_roots);
867− change_list.append_child();
868− }
869−}
870−
871−// Remove all of a node's children.
872−//
873−// The change list stack must have this shape upon entry to this function:
874−//
875−// [... parent]
876−//
877−// When this function returns, the change list stack is in the same state.
878−fn remove_all_children(
879− change_list: &mut ChangeListBuilder,
880− registry: &mut EventsRegistry,
881− old: &[Node],
882−) {
883− debug_assert!(change_list.traversal_is_committed());
884− for child in old {
885− registry.remove_subtree(child);
886− }
887− // Fast way to remove all children: set the node's textContent to an empty
888− // string.
889− change_list.set_text("");
890−}
891−
892−// Remove the current child and all of its following siblings.
893−//
894−// The change list stack must have this shape upon entry to this function:
895−//
896−// [... parent child]
897−//
898−// After the function returns, the child is no longer on the change list stack:
899−//
900−// [... parent]
901−fn remove_self_and_next_siblings(
902− change_list: &mut ChangeListBuilder,
903− registry: &mut EventsRegistry,
904− old: &[Node],
905−) {
906− debug_assert!(change_list.traversal_is_committed());
907− for child in old {
908− registry.remove_subtree(child);
909− }
910− change_list.remove_self_and_next_siblings();
911−}
912−
913−// Emit instructions to create the given virtual node.
914−//
915−// The change list stack may have any shape upon entering this function:
916−//
917−// [...]
918−//
919−// When this function returns, the new node is on top of the change list stack:
920−//
921−// [... node]
922−fn create(
923− cached_set: &CachedSet,
924− change_list: &mut ChangeListBuilder,
925− registry: &mut EventsRegistry,
926− node: &Node,
927− cached_roots: &mut FxHashSet<CacheId>,
928−) {
929− debug_assert!(change_list.traversal_is_committed());
930− match node.kind {
931− NodeKind::Text(TextNode { text }) => {
932− change_list.create_text_node(text);
933− }
934− NodeKind::Element(&ElementNode {
935− key: _,
936− tag_name,
937− listeners,
938− attributes,
939− children,
940− namespace,
941− }) => {
942− if let Some(namespace) = namespace {
943− change_list.create_element_ns(tag_name, namespace);
944− } else {
945− change_list.create_element(tag_name);
946− }
947−
948− for l in listeners {
949− unsafe {
950− registry.add(l);
951− }
952− change_list.new_event_listener(l);
953− }
954−
955− for attr in attributes {
956− change_list.set_attribute(&attr.name, &attr.value, namespace.is_some());
957− }
958−
959− // Fast path: if there is a single text child, it is faster to
960− // create-and-append the text node all at once via setting the
961− // parent's `textContent` in a single change list instruction than
962− // to emit three instructions to (1) create a text node, (2) set its
963− // text content, and finally (3) append the text node to this
964− // parent.
965− if children.len() == 1 {
966− if let Node {
967− kind: NodeKind::Text(TextNode { text }),
968− } = children[0]
969− {
970− change_list.set_text(text);
971− return;
972− }
973− }
974−
975− for child in children {
976− create(cached_set, change_list, registry, child, cached_roots);
977− change_list.append_child();
978− }
979− }
980− NodeKind::Cached(ref c) => {
981− cached_roots.insert(c.id);
982− let (node, template) = cached_set.get(c.id);
983− if let Some(template) = template {
984− create_with_template(
985− cached_set,
986− change_list,
987− registry,
988− template,
989− node,
990− cached_roots,
991− );
992− } else {
993− create(cached_set, change_list, registry, node, cached_roots);
994− }
995− }
996− }
997−}
998−
999−// Get or create the template.
1000−//
1001−// Upon entering this function the change list stack may be in any shape:
1002−//
1003−// [...]
1004−//
1005−// When this function returns, it leaves a freshly cloned copy of the template
1006−// on the top of the change list stack:
1007−//
1008−// [... template]
1009−#[inline]
1010−fn get_or_create_template<'a>(
1011− cached_set: &'a CachedSet,
1012− change_list: &mut ChangeListBuilder,
1013− registry: &mut EventsRegistry,
1014− cached_roots: &mut FxHashSet<CacheId>,
1015− template_id: CacheId,
1016−) -> (&'a Node<'a>, bool) {
1017− let (template, template_template) = cached_set.get(template_id);
1018− debug_assert!(
1019− template_template.is_none(),
1020− "templates should not be templated themselves"
1021− );
1022−
1023− // If we haven't already created and saved the physical DOM subtree for this
1024− // template, do that now.
1025− if change_list.has_template(template_id) {
1026− // Clone the template and push it onto the stack.
1027− //
1028− // [...]
1029− change_list.push_template(template_id);
1030− // [... template]
1031−
1032− (template, true)
1033− } else {
1034− // [...]
1035− create(cached_set, change_list, registry, template, cached_roots);
1036− // [... template]
1037− change_list.save_template(template_id);
1038− // [... template]
1039−
1040− (template, false)
1041− }
1042−}
1043−
1044−fn create_and_replace(
1045− cached_set: &CachedSet,
1046− change_list: &mut ChangeListBuilder,
1047− registry: &mut EventsRegistry,
1048− new_template: Option<CacheId>,
1049− old: &Node,
1050− new: &Node,
1051− cached_roots: &mut FxHashSet<CacheId>,
1052−) {
1053− debug_assert!(change_list.traversal_is_committed());
1054−
1055− if let Some(template_id) = new_template {
1056− let (template, needs_listeners) =
1057− get_or_create_template(cached_set, change_list, registry, cached_roots, template_id);
1058− change_list.replace_with();
1059−
1060− let mut old_forcing = None;
1061− if needs_listeners {
1062− old_forcing = Some(change_list.push_force_new_listeners());
1063− }
1064−
1065− diff(
1066− cached_set,
1067− change_list,
1068− registry,
1069− template,
1070− new,
1071− cached_roots,
1072− );
1073−
1074− if let Some(old) = old_forcing {
1075− change_list.pop_force_new_listeners(old);
1076− }
1077−
1078− change_list.commit_traversal();
1079− } else {
1080− create(cached_set, change_list, registry, new, cached_roots);
1081− change_list.replace_with();
1082− }
1083− registry.remove_subtree(old);
1084−}
1085−
1086−fn create_with_template(
1087− cached_set: &CachedSet,
1088− change_list: &mut ChangeListBuilder,
1089− registry: &mut EventsRegistry,
1090− template_id: CacheId,
1091− node: &Node,
1092− cached_roots: &mut FxHashSet<CacheId>,
1093−) {
1094− debug_assert!(change_list.traversal_is_committed());
1095−
1096− // [...]
1097− let (template, needs_listeners) =
1098− get_or_create_template(cached_set, change_list, registry, cached_roots, template_id);
1099− // [... template]
1100−
1101− // Now diff the node with its template.
1102− //
1103− // We must force adding new listeners instead of updating existing ones,
1104− // since listeners don't get cloned in `cloneNode`.
1105− let mut old_forcing = None;
1106− if needs_listeners {
1107− old_forcing = Some(change_list.push_force_new_listeners());
1108− }
1109−
1110− diff(
1111− cached_set,
1112− change_list,
1113− registry,
1114− template,
1115− node,
1116− cached_roots,
1117− );
1118−
1119− if let Some(old) = old_forcing {
1120− change_list.pop_force_new_listeners(old);
1121− }
1122−
1123− // Make sure that we come back up to the level we were at originally.
1124− change_list.commit_traversal();
1125−}
dodrio/src/events.rsdeleted−138
@@ -1,138 +0,0 @@
1−use crate::{node::Node, vdom::VdomInner, Listener};
2−use std::cell::RefCell;
3−use std::rc::{Rc, Weak};
4−
5−cfg_if::cfg_if! {
6− if #[cfg(all(feature = "xxx-unstable-internal-use-only", not(target_arch = "wasm32")))] {
7− #[derive(Debug)]
8− pub(crate) struct EventsRegistry {}
9− impl EventsRegistry {
10− pub(crate) fn new(_vdom: Weak<VdomInner>) -> (
11− Rc<RefCell<EventsRegistry>>,
12− crate::EventsTrampoline,
13− ) {
14− (Rc::new(RefCell::new(EventsRegistry {})), ())
15− }
16− pub(crate) fn remove(&mut self, _listener: &Listener) {}
17− pub(crate) fn remove_subtree(&mut self, _node: &Node) {}
18− pub(crate) unsafe fn add<'a>(&mut self, _listener: &'a Listener<'a>) {}
19− pub(crate) fn clear_active_listeners(&mut self) {}
20− }
21− } else {
22− use crate::{
23− node::{ElementNode, ListenerCallback, NodeKind},
24− vdom::VdomWeak,
25− };
26− use fxhash::FxHashMap;
27− use std::fmt;
28− use std::mem;
29− use wasm_bindgen::closure::Closure;
30− use wasm_bindgen::prelude::*;
31−
32− /// The events registry manages event listeners for a virtual DOM.
33− ///
34− /// The events registry is persistent across virtual DOM rendering and double
35− /// buffering.
36− pub(crate) struct EventsRegistry {
37− vdom: Weak<VdomInner>,
38− active: FxHashMap<(u32, u32), ListenerCallback<'static>>,
39− }
40−
41− impl fmt::Debug for EventsRegistry {
42− fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43− f.debug_struct("EventsRegistry")
44− .field("active", &self.active.keys().collect::<Vec<_>>())
45− .finish()
46− }
47− }
48−
49− impl EventsRegistry {
50− /// Construct a new events registry and JS function trampoline that weakly
51− /// holds the new registry and can be used by JS to invoke listeners on the
52− /// Rust side.
53− pub(crate) fn new(
54− vdom: Weak<VdomInner>,
55− ) -> (
56− Rc<RefCell<EventsRegistry>>,
57− crate::EventsTrampoline,
58− ) {
59− let registry = Rc::new(RefCell::new(EventsRegistry {
60− vdom,
61− active: FxHashMap::default(),
62− }));
63−
64− let weak_registry = Rc::downgrade(&registry);
65− let closure = Closure::wrap(Box::new(move |event, a, b| {
66− debug_assert!(a != 0);
67−
68− // if the VdomInnerExclusive is keeping this closure alive, then the
69− // VdomInnerExclusive should also be keeping the registry alive
70− let registry = weak_registry.upgrade().unwrap_throw();
71− let registry = registry.borrow();
72−
73− match registry.active.get(&(a, b)) {
74− None => warn!(
75− "EventsRegistry closure invoked with unknown listener parts: \
76− (0x{:x}, 0x{:x})",
77− a, b
78− ),
79− Some(callback) => {
80− let vdom = registry.vdom.upgrade().expect_throw(
81− "if the registry is still around, then the vdom should still be around",
82− );
83− let vdom_weak = VdomWeak::new(&vdom);
84− let mut vdom = vdom.exclusive.borrow_mut();
85− let component = vdom.component_raw_mut();
86− callback(component, vdom_weak, event);
87− }
88− }
89− }) as Box<dyn Fn(web_sys::Event, u32, u32)>);
90−
91− (registry, closure)
92− }
93−
94− pub(crate) fn remove(&mut self, listener: &Listener) {
95− let id = listener.get_callback_parts();
96− debug_assert!(id.0 != 0);
97− self.active.remove(&id);
98− }
99−
100− pub(crate) fn remove_subtree(&mut self, node: &Node) {
101− match node.kind {
102− NodeKind::Cached(_) | NodeKind::Text(_) => {},
103− NodeKind::Element(&ElementNode {listeners, children, ..}) => {
104− for l in listeners {
105− self.remove(l);
106− }
107− for child in children {
108− self.remove_subtree(child)
109− }
110− }
111− }
112− }
113−
114− /// Add an event listener to the registry, exposing to JS.
115− ///
116− /// # Unsafety
117− ///
118− /// The listener's lifetime is extended to `'static` and it is the
119− /// caller's responsibility to ensure that the listener is not kept
120− /// in the registry after it is dropped. This is maintained during
121− /// diffing.
122− pub(crate) unsafe fn add<'a>(&mut self, listener: &'a Listener<'a>) {
123− let id = listener.get_callback_parts();
124− debug_assert!(id.0 != 0);
125−
126− let callback =
127− mem::transmute::<ListenerCallback<'a>, ListenerCallback<'static>>(listener.callback);
128− let old = self.active.insert(id, callback);
129− debug_assert!(old.is_none());
130− }
131−
132− /// Clear all event listeners from the registry.
133− pub(crate) fn clear_active_listeners(&mut self) {
134− self.active.clear();
135− }
136− }
137− }
138−}
dodrio/src/lib.rsdeleted−135
@@ -1,135 +0,0 @@
1−//! The `dodrio` virtual DOM.
2−//!
3−//! ## Example
4−//!
5−//! ```no_run
6−//! use dodrio::{bumpalo, Attribute, Node, Render, RenderContext};
7−//! use wasm_bindgen::UnwrapThrowExt;
8−//!
9−//! /// A component that greets someone.
10−//! pub struct Hello<'who> {
11−//! who: &'who str,
12−//! }
13−//!
14−//! impl<'who> Hello<'who> {
15−//! /// Construct a new `Hello` component that greets the given `who`.
16−//! pub fn new(who: &str) -> Hello {
17−//! Hello { who }
18−//! }
19−//! }
20−//!
21−//! impl<'a, 'who> Render<'a> for Hello<'who> {
22−//! fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
23−//! use dodrio::builder::*;
24−//!
25−//! let id = bumpalo::format!(in cx.bump, "hello-{}", self.who);
26−//! let who = bumpalo::collections::String::from_str_in(self.who, cx.bump).into_bump_str();
27−//!
28−//! div(&cx)
29−//! .attr("id", id.into_bump_str())
30−//! .on("click", |root, _vdom, _event| {
31−//! let hello = root.unwrap_mut::<Hello>();
32−//! web_sys::window()
33−//! .expect_throw("should have a `Window` on the Web")
34−//! .alert_with_message(hello.who);
35−//! })
36−//! .children([
37−//! text("Hello, "),
38−//! strong(&cx)
39−//! .children([
40−//! text(who),
41−//! text("!"),
42−//! ])
43−//! .finish(),
44−//! ])
45−//! .finish()
46−//! }
47−//! }
48−//! ```
49−#![deny(missing_docs, missing_debug_implementations)]
50−
51−// Re-export the `bumpalo` crate.
52−pub use bumpalo;
53−
54−cfg_if::cfg_if! {
55− if #[cfg(feature = "log")] {
56− #[macro_use]
57− extern crate log;
58− } else {
59− #[macro_use]
60− mod logging;
61− }
62−}
63−
64−// A macro to expose items unstably, only for internal/testing usage.
65−cfg_if::cfg_if! {
66− if #[cfg(feature = "xxx-unstable-internal-use-only")] {
67− macro_rules! pub_unstable_internal {
68− ( $(#[$attr:meta])* pub(crate) $( $thing:tt )* ) => {
69− #[doc(hidden)]
70− $( #[$attr] )*
71− pub $( $thing )*
72− }
73− }
74− } else {
75− macro_rules! pub_unstable_internal {
76− ( $(#[$attr:meta])* pub(crate) $( $thing:tt )* ) => {
77− $( #[$attr] )*
78− pub(crate) $( $thing )*
79− }
80− }
81− }
82−}
83−
84−// Only `pub` so that the wasm-bindgen bindings work.
85−#[doc(hidden)]
86−pub mod change_list;
87−
88−mod cached;
89−mod cached_set;
90−mod diff;
91−mod events;
92−mod node;
93−mod render;
94−mod render_context;
95−mod strace;
96−mod vdom;
97−
98−pub mod builder;
99−
100−// Re-export items at the top level.
101−pub use self::cached::Cached;
102−pub use self::node::{Attribute, Listener, Node, NodeKey};
103−pub use self::render::{Render, RootRender};
104−pub use self::render_context::RenderContext;
105−pub use self::vdom::{Vdom, VdomWeak};
106−
107−cfg_if::cfg_if! {
108− if #[cfg(all(target_arch = "wasm32", not(feature = "xxx-unstable-internal-use-only")))] {
109− use wasm_bindgen::__rt::WasmRefCell as RefCell;
110− } else {
111− use std::cell::RefCell;
112− }
113−}
114−
115−// Polyfill some Web stuff for benchmarking...
116−cfg_if::cfg_if! {
117− if #[cfg(all(feature = "xxx-unstable-internal-use-only", not(target_arch = "wasm32")))] {
118− /// An element node in the physical DOM.
119− pub type Element = ();
120−
121− pub(crate) type EventsTrampoline = ();
122− } else {
123− /// An element node in the physical DOM.
124− pub type Element = web_sys::Element;
125−
126− pub(crate) type EventsTrampoline = wasm_bindgen::closure::Closure<dyn Fn(web_sys::Event, u32, u32)>;
127− }
128−}
129−
130−cfg_if::cfg_if! {
131− if #[cfg(feature = "xxx-unstable-internal-use-only")] {
132− pub use self::cached_set::{CachedSet};
133− pub use self::node::{ElementNode, NodeKind, TextNode};
134− }
135−}
dodrio/src/logging.rsdeleted−65
@@ -1,65 +0,0 @@
1−#[allow(unused_macros)]
2−macro_rules! debug {
3− ( $( $e:expr ),* $(,)* ) => {
4− if false {
5− $(
6− let _ = $e;
7− )*
8− }
9− }
10−}
11−
12−#[allow(unused_macros)]
13−macro_rules! error {
14− ( $( $e:expr ),* $(,)* ) => {
15− if false {
16− $(
17− let _ = $e;
18− )*
19− }
20− }
21−}
22−
23−#[allow(unused_macros)]
24−macro_rules! info {
25− ( $( $e:expr ),* $(,)* ) => {
26− if false {
27− $(
28− let _ = $e;
29− )*
30− }
31− }
32−}
33−
34−#[allow(unused_macros)]
35−macro_rules! log {
36− ( $( $e:expr ),* $(,)* ) => {
37− if false {
38− $(
39− let _ = $e;
40− )*
41− }
42− }
43−}
44−
45−#[allow(unused_macros)]
46−macro_rules! trace {
47− ( $( $e:expr ),* $(,)* ) => {
48− if false {
49− $(
50− let _ = $e;
51− )*
52− }
53− }
54−}
55−
56−#[allow(unused_macros)]
57−macro_rules! warn {
58− ( $( $e:expr ),* $(,)* ) => {
59− if false {
60− $(
61− let _ = $e;
62− )*
63− }
64− }
65−}
dodrio/src/node.rsdeleted−272
@@ -1,272 +0,0 @@
1−use crate::{cached_set::CacheId, RootRender, VdomWeak};
2−use bumpalo::Bump;
3−use std::fmt;
4−use std::iter;
5−use std::mem;
6−use std::u32;
7−
8−/// A virtual DOM node.
9−#[derive(Debug, Clone)]
10−pub struct Node<'a> {
11− #[cfg(feature = "xxx-unstable-internal-use-only")]
12− #[doc(hidden)]
13− pub kind: NodeKind<'a>,
14−
15− #[cfg(not(feature = "xxx-unstable-internal-use-only"))]
16− pub(crate) kind: NodeKind<'a>,
17−}
18−
19−pub_unstable_internal! {
20− /// A node is either a text node or an element.
21− #[derive(Debug, Clone)]
22− pub(crate) enum NodeKind<'a> {
23− /// A text node.
24− Text(TextNode<'a>),
25−
26− /// An element potentially with attributes and children.
27− Element(&'a ElementNode<'a>),
28−
29− /// A node in the vdom's `CachedSet`. This allows us to avoid
30− /// re-rendering and re-diffing subtrees.
31− Cached(CachedNode),
32− }
33−}
34−
35−pub_unstable_internal! {
36− /// Text nodes are just a string of text. They cannot have attributes or
37− /// children.
38− #[derive(Debug, Clone)]
39− pub(crate) struct TextNode<'a> {
40− pub text: &'a str,
41− }
42−}
43−
44−pub_unstable_internal! {
45− /// Elements have a tag name, zero or more attributes, and zero or more
46− /// children.
47− #[derive(Debug, Clone)]
48− pub(crate) struct ElementNode<'a> {
49− pub key: NodeKey,
50− pub tag_name: &'a str,
51− pub listeners: &'a [Listener<'a>],
52− pub attributes: &'a [Attribute<'a>],
53− pub children: &'a [Node<'a>],
54− pub namespace: Option<&'a str>,
55− }
56−}
57−
58−pub_unstable_internal! {
59− /// A cached node exists in an arena that is internal to the `Vdom`. It
60− /// allows us to avoid both re-rendering a sub-tree and re-diffing
61− /// it. `CachedNode`s cannot contain cycles, since they can only contain
62− /// other `CachedNode`s that already exist when they are being constructed.
63− #[derive(Debug, Clone, Copy)]
64− pub(crate) struct CachedNode {
65− pub id: CacheId,
66− // Always a copy of the inner cached node's key.
67− pub key: NodeKey,
68− }
69−}
70−
71−/// The key for keyed children.
72−///
73−/// Keys must be unique among siblings.
74−///
75−/// If any sibling is keyed, then they all must be keyed.
76−#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
77−pub struct NodeKey(pub(crate) u32);
78−
79−impl Default for NodeKey {
80− fn default() -> NodeKey {
81− NodeKey::NONE
82− }
83−}
84−
85−impl NodeKey {
86− /// The default, lack of a key.
87− pub const NONE: NodeKey = NodeKey(u32::MAX);
88−
89− /// Is this key `NodeKey::NONE`?
90− #[inline]
91− pub fn is_none(&self) -> bool {
92− *self == Self::NONE
93− }
94−
95− /// Is this key not `NodeKey::NONE`?
96− #[inline]
97− pub fn is_some(&self) -> bool {
98− !self.is_none()
99− }
100−
101− /// Create a new `NodeKey`.
102− ///
103− /// `key` must not be `u32::MAX`.
104− #[inline]
105− pub fn new(key: u32) -> Self {
106− debug_assert_ne!(key, u32::MAX);
107− NodeKey(key)
108− }
109−}
110−
111−/// An event listener callback function.
112−///
113−/// It takes three parameters:
114−///
115−/// 1. The virtual DOM's root rendering component.
116−/// 2. A capability to scheduler virtual DOM re-rendering.
117−/// 3. The event that occurred.
118−pub(crate) type ListenerCallback<'a> = &'a (dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event));
119−
120−/// An event listener.
121−pub struct Listener<'a> {
122− /// The type of event to listen for.
123− pub event: &'a str,
124− /// The callback to invoke when the event happens.
125− pub callback: ListenerCallback<'a>,
126−}
127−
128−/// An attribute on a DOM node, such as `id="my-thing"` or
129−/// `href="https://example.com"`.
130−#[derive(Clone, Debug)]
131−pub struct Attribute<'a> {
132− /// attribute name
133− pub name: &'a str,
134− /// attribute value
135− pub value: &'a str,
136−}
137−
138−impl<'a> From<CachedNode> for Node<'a> {
139− #[inline]
140− fn from(c: CachedNode) -> Self {
141− Node {
142− kind: NodeKind::Cached(c),
143− }
144− }
145−}
146−
147−impl fmt::Debug for Listener<'_> {
148− fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149− let (a, b) = self.get_callback_parts();
150− let a = a as *mut u32;
151− let b = b as *mut u32;
152− f.debug_struct("Listener")
153− .field("event", &self.event)
154− .field("callback", &(a, b))
155− .finish()
156− }
157−}
158−
159−impl<'a> Attribute<'a> {
160− /// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
161− #[inline]
162− pub fn name(&self) -> &'a str {
163− self.name
164− }
165−
166− /// The attribute value, such as `"my-thing"` in `<div id="my-thing" />`.
167− #[inline]
168− pub fn value(&self) -> &'a str {
169− self.value
170− }
171−
172− /// Certain attributes are considered "volatile" and can change via user
173− /// input that we can't see when diffing against the old virtual DOM. For
174− /// these attributes, we want to always re-set the attribute on the physical
175− /// DOM node, even if the old and new virtual DOM nodes have the same value.
176− #[inline]
177− pub(crate) fn is_volatile(&self) -> bool {
178− match self.name {
179− "value" | "checked" | "selected" => true,
180− _ => false,
181− }
182− }
183−}
184−
185−impl<'a> Node<'a> {
186− /// Low-level constructor for making a new `Node` of type element with given
187− /// parts.
188− ///
189− /// This is primarily intended for JSX and templating proc-macros to compile
190− /// down into. If you are building nodes by-hand, prefer using the
191− /// `dodrio::builder::*` APIs.
192− #[inline]
193− pub fn element(
194− bump: &'a Bump,
195− key: NodeKey,
196− tag_name: &'a str,
197− listeners: &'a [Listener<'a>],
198− attributes: &'a [Attribute<'a>],
199− children: &'a [Node<'a>],
200− namespace: Option<&'a str>,
201− ) -> Node<'a> {
202− let element = bump.alloc_with(|| ElementNode {
203− key,
204− tag_name,
205− listeners,
206− attributes,
207− children,
208− namespace,
209− });
210−
211− Node {
212− kind: NodeKind::Element(element),
213− }
214− }
215−
216− /// Construct a new text node with the given text.
217− #[inline]
218− pub fn text(text: &'a str) -> Node<'a> {
219− Node {
220− kind: NodeKind::Text(TextNode { text }),
221− }
222− }
223−
224− #[inline]
225− pub(crate) fn key(&self) -> NodeKey {
226− match &self.kind {
227− NodeKind::Text(_) => NodeKey::NONE,
228− NodeKind::Element(e) => e.key,
229− NodeKind::Cached(c) => c.key,
230− }
231− }
232−}
233−
234−/// A node can become an iterator that yields the node itself once.
235−///
236−/// This implementation of `IntoIterator` mostly exists to improve the
237−/// `typed-html` ergonomics, where the macro invokes `.into_iter()` on the child
238−/// contents of a tag. By implementing `IntoIterator` here, we avoid having to
239−/// do nasty shenanigans like `<p>vec![$contents]</p>` instead of plain old
240−/// `<p>$contents</p>`.
241−impl<'a> IntoIterator for Node<'a> {
242− type Item = Node<'a>;
243− type IntoIter = iter::Once<Self::Item>;
244−
245− fn into_iter(self) -> Self::IntoIter {
246− iter::once(self)
247− }
248−}
249−
250−union CallbackFatPtr<'a> {
251− callback: ListenerCallback<'a>,
252− parts: (u32, u32),
253−}
254−
255−impl Listener<'_> {
256− #[inline]
257− pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
258− assert_eq!(
259− mem::size_of::<ListenerCallback>(),
260− mem::size_of::<CallbackFatPtr>()
261− );
262−
263− unsafe {
264− let fat = CallbackFatPtr {
265− callback: self.callback,
266− };
267− let (a, b) = fat.parts;
268− debug_assert!(a != 0);
269− (a, b)
270− }
271− }
272−}
dodrio/src/render.rsdeleted−168
@@ -1,168 +0,0 @@
1−use crate::{Node, RenderContext};
2−use std::any::Any;
3−use std::rc::Rc;
4−use wasm_bindgen::UnwrapThrowExt;
5−
6−/// A trait for any component that can be rendered to HTML.
7−///
8−/// Takes a shared reference to `self` and generates the virtual DOM that
9−/// represents its rendered HTML.
10−///
11−/// ## `Bump` Allocation
12−///
13−/// `Render` implementations can use the `Bump` inside the provided
14−/// `RenderContext` for very fast allocation for anything that needs to be
15−/// temporarily allocated during rendering.
16−///
17−/// ## Example
18−///
19−/// ```no_run
20−/// use dodrio::{Node, Render, RenderContext};
21−///
22−/// pub struct MyComponent;
23−///
24−/// impl<'a> Render<'a> for MyComponent {
25−/// fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
26−/// use dodrio::builder::*;
27−///
28−/// p(&cx)
29−/// .children([
30−/// text("This is "),
31−/// strong(&cx).children([text("my component")]).finish(),
32−/// text(" rendered!"),
33−/// ])
34−/// .finish()
35−/// }
36−/// }
37−/// ```
38−pub trait Render<'a> {
39− /// Render `self` as a virtual DOM. Use the given context's `Bump` for
40− /// temporary allocations.
41− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a>;
42−}
43−
44−impl<'a, 'r, R> Render<'a> for &'r R
45−where
46− R: Render<'a>,
47−{
48− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
49− (**self).render(cx)
50− }
51−}
52−
53−impl<'a, R> Render<'a> for Rc<R>
54−where
55− R: Render<'a>,
56−{
57− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
58− (**self).render(cx)
59− }
60−}
61−
62−/// A `RootRender` is a render component that can be the root rendering component
63−/// mounted to a virtual DOM.
64−///
65−/// In addition to rendering, it must also be `'static` so that it can be owned
66−/// by the virtual DOM and `Any` so that it can be downcast to its concrete type
67−/// by event listener callbacks.
68−///
69−/// You do not need to implement this trait by hand: there is a blanket
70−/// implementation for all `Render` types that fulfill the `RootRender`
71−/// requirements.
72−pub trait RootRender: Any + for<'a> Render<'a> {
73− /// Get this `&RootRender` trait object as an `&Any` trait object reference.
74− fn as_any(&self) -> &dyn Any;
75−
76− /// Get this `&mut RootRender` trait object as an `&mut Any` trait object
77− /// reference.
78− fn as_any_mut(&mut self) -> &mut dyn Any;
79−}
80−
81−impl<T> RootRender for T
82−where
83− T: Any + for<'a> Render<'a>,
84−{
85− fn as_any(&self) -> &dyn Any {
86− self
87− }
88−
89− fn as_any_mut(&mut self) -> &mut dyn Any {
90− self
91− }
92−}
93−
94−impl dyn RootRender {
95− /// Downcast this shared `&dyn RootRender` trait object reference to its
96− /// underlying concrete type.
97− ///
98− /// # Panics
99− ///
100− /// Panics if this virtual DOM's root rendering component is not an `R`
101− /// instance.
102− pub fn unwrap_ref<R>(&self) -> &R
103− where
104− R: RootRender,
105− {
106− self.as_any()
107− .downcast_ref::<R>()
108− .expect_throw("bad `RootRender::unwrap_ref` call")
109− }
110−
111− /// Downcast this exclusive `&mut dyn RootRender` trait object reference to
112− /// its underlying concrete type.
113− ///
114− /// # Panics
115− ///
116− /// Panics if this virtual DOM's root rendering component is not an `R`
117− /// instance.
118− pub fn unwrap_mut<R>(&mut self) -> &mut R
119− where
120− R: RootRender,
121− {
122− self.as_any_mut()
123− .downcast_mut::<R>()
124− .expect_throw("bad `RootRender::unwrap_ref` call")
125− }
126−}
127−
128−#[cfg(test)]
129−mod tests {
130− #[test]
131− fn render_is_object_safe() {
132− #[allow(dead_code)]
133− fn takes_dyn_render(_: &dyn super::Render) {}
134− }
135−
136− #[test]
137− fn root_render_is_object_safe() {
138− #[allow(dead_code)]
139− fn takes_dyn_render(_: &dyn super::RootRender) {}
140− }
141−
142− #[test]
143− fn render_bump_scoped_child() {
144− use crate::{builder::*, bumpalo::collections::String, Node, Render, RenderContext};
145−
146− struct Child<'a> {
147− name: &'a str,
148− }
149−
150− impl<'a> Render<'a> for Child<'a> {
151− fn render(&self, _cx: &mut RenderContext<'a>) -> Node<'a> {
152− text(self.name)
153− }
154− }
155−
156− struct Parent;
157−
158− impl<'a> Render<'a> for Parent {
159− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
160− let child_name = String::from_str_in("child", cx.bump).into_bump_str();
161−
162− div(&cx)
163− .children([Child { name: child_name }.render(cx)])
164− .finish()
165− }
166− }
167− }
168−}
dodrio/src/render_context.rsdeleted−104
@@ -1,104 +0,0 @@
1−use crate::{
2− cached::{Cached, TemplateId},
3− cached_set::{CacheId, CachedSet},
4− Node, Render,
5−};
6−use bumpalo::Bump;
7−use fxhash::FxHashMap;
8−use std::fmt;
9−
10−/// Common context available to all `Render` implementations.
11−///
12−/// Notably, the `RenderContext` gives access to the bump arena that the virtual
13−/// DOM should be allocated within. This is available via the `bump` field.
14−pub struct RenderContext<'a> {
15− /// The underlying bump arena that virtual DOMs are rendered into.
16− ///
17− /// ## Example
18− ///
19− /// ```
20− /// use dodrio::RenderContext;
21− ///
22− /// // Given a rendering context, allocate an i32 inside its bump arena.
23− /// fn foo<'a>(cx: &mut RenderContext<'a>) -> &'a mut i32 {
24− /// cx.bump.alloc(42)
25− /// }
26− /// ```
27− pub bump: &'a Bump,
28−
29− pub(crate) cached_set: &'a crate::RefCell<CachedSet>,
30−
31− pub(crate) templates: &'a mut FxHashMap<TemplateId, Option<CacheId>>,
32−
33− // Prevent exhaustive matching on the rendering context, so we can always
34− // add more members in a semver-compatible way.
35− _non_exhaustive: (),
36−}
37−
38−impl fmt::Debug for RenderContext<'_> {
39− fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40− f.debug_struct("RenderContext")
41− .field("bump", &self.bump)
42− .finish()
43− }
44−}
45−
46−impl<'a> RenderContext<'a> {
47− pub_unstable_internal! {
48− pub(crate) fn new(
49− bump: &'a Bump,
50− cached_set: &'a crate::RefCell<CachedSet>,
51− templates: &'a mut FxHashMap<TemplateId, Option<CacheId>>
52− ) -> Self {
53− RenderContext {
54− bump,
55− cached_set,
56− templates,
57− _non_exhaustive: (),
58− }
59− }
60− }
61−
62− pub(crate) fn cache<F>(&mut self, pinned: bool, template: Option<CacheId>, f: F) -> CacheId
63− where
64− F: for<'b> FnOnce(&mut RenderContext<'b>) -> Node<'b>,
65− {
66− CachedSet::insert(self, pinned, template, f)
67− }
68−
69− /// Get or create the cached template for `Cached<R>`.
70− pub(crate) fn template<R>(&mut self) -> Option<CacheId>
71− where
72− R: 'static + Default + for<'b> Render<'b>,
73− {
74− let template_id = Cached::<R>::template_id();
75− if let Some(cache_id) = self.templates.get(&template_id).cloned() {
76− return cache_id;
77− }
78−
79− // Prevent re-entrancy from infinite looping. Any attempts to get `R`'s
80− // template while constructing the template will simply fail to use the
81− // templated fast path.
82− self.templates.insert(template_id, None);
83−
84− // Render the default `R` and save that as the template for all
85− // `Cached<R>`s.
86− let cache_id = self.cache(true, None, |nested_cx| R::default().render(nested_cx));
87− self.templates.insert(template_id, Some(cache_id));
88− Some(cache_id)
89− }
90−}
91−
92−impl<'a, 'b> From<&'b RenderContext<'a>> for &'a Bump {
93− #[inline]
94− fn from(cx: &'b RenderContext<'a>) -> &'a Bump {
95− cx.bump
96− }
97−}
98−
99−impl<'a, 'b, 'c> From<&'c &'b mut RenderContext<'a>> for &'a Bump {
100− #[inline]
101− fn from(cx: &'c &'b mut RenderContext<'a>) -> &'a Bump {
102− cx.bump
103− }
104−}
dodrio/src/strace.rsdeleted−22
@@ -1,22 +0,0 @@
1−cfg_if::cfg_if! {
2− if #[cfg(feature = "xxx-unstable-strace")] {
3− use wasm_bindgen::prelude::*;
4−
5− #[wasm_bindgen(module = "/js/strace.js")]
6− extern "C" {
7− #[wasm_bindgen(js_name = initStrace)]
8− fn really_init_strace();
9− }
10−
11− pub fn init_strace() {
12− use std::sync::Once;
13− static STRACE: Once = Once::new();
14− STRACE.call_once(|| {
15− really_init_strace();
16− });
17− }
18− } else {
19− #[inline]
20− pub fn init_strace() {}
21− }
22−}
dodrio/src/vdom.rsdeleted−476
@@ -1,476 +0,0 @@
1−use super::change_list::ChangeListPersistentState;
2−use super::RootRender;
3−use crate::cached::TemplateId;
4−use crate::cached_set::{CacheId, CachedSet};
5−use crate::events::EventsRegistry;
6−use crate::node::{Node, NodeKey};
7−use crate::RenderContext;
8−use bumpalo::Bump;
9−use fxhash::FxHashMap;
10−use std::cell::Cell;
11−use std::cell::RefCell;
12−use std::fmt;
13−use std::future::Future;
14−use std::mem;
15−use std::mem::ManuallyDrop;
16−use std::rc::{Rc, Weak};
17−use wasm_bindgen::prelude::*;
18−use wasm_bindgen::JsCast;
19−use wasm_bindgen_futures::JsFuture;
20−
21−/// A strong handle to a mounted virtual DOM.
22−///
23−/// When this handle is dropped, the virtual DOM is unmounted and its listeners
24−/// removed. To keep it mounted forever, use the `Vdom::forget` method.
25−#[must_use = "A `Vdom` will only keep rendering and listening to events while it has not been \
26− dropped. If you want a `Vdom` to run forever, call `Vdom::forget`."]
27−#[derive(Debug)]
28−pub struct Vdom {
29− inner: Rc<VdomInner>,
30−}
31−
32−/// A weak handle to a virtual DOM.
33−///
34−/// Does not prevent the virtual DOM from being unmounted: only keeping the
35−/// original `Vdom` alive guarantees that.
36−///
37−/// A `VdomWeak` also gives you the capability to scheduling re-rendering (say
38−/// after mutating the render component state).
39−#[derive(Clone, Debug)]
40−pub struct VdomWeak {
41− inner: Weak<VdomInner>,
42−}
43−
44−#[derive(Debug)]
45−pub(crate) struct VdomInner {
46− pub(crate) shared: VdomInnerShared,
47− pub(crate) exclusive: RefCell<VdomInnerExclusive>,
48−}
49−
50−pub(crate) struct VdomInnerShared {
51− pub(crate) render_scheduled: Cell<Option<js_sys::Promise>>,
52−}
53−
54−pub(crate) struct VdomInnerExclusive {
55− // Always `Some` except just before we drop. Just an option so that
56− // `unmount` can take the component out but we can still have a Drop
57− // implementation.
58− component: Option<Box<dyn RootRender>>,
59−
60− dom_buffers: Option<[Bump; 2]>,
61− change_list: ManuallyDrop<ChangeListPersistentState>,
62− container: crate::Element,
63− events_registry: Option<Rc<RefCell<EventsRegistry>>>,
64− events_trampoline: Option<crate::EventsTrampoline>,
65− cached_set: crate::RefCell<CachedSet>,
66− templates: FxHashMap<TemplateId, Option<CacheId>>,
67−
68− // Actually a reference into `self.dom_buffers[0]` or if `self.component` is
69− // caching renders, into `self.component`'s bump.
70− current_root: Option<Node<'static>>,
71−}
72−
73−unsafe fn extend_node_lifetime<'a>(node: Node<'a>) -> Node<'static> {
74− mem::transmute(node)
75−}
76−
77−impl fmt::Debug for VdomInnerShared {
78− fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79− let render_scheduled = Cell::new(None);
80− self.render_scheduled.swap(&render_scheduled);
81− let render_scheduled = render_scheduled.into_inner();
82− let r = f
83− .debug_struct("VdomInnerShared")
84− .field("render_scheduled", &render_scheduled)
85− .finish();
86− self.render_scheduled.set(render_scheduled);
87− r
88− }
89−}
90−
91−impl fmt::Debug for VdomInnerExclusive {
92− fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93− f.debug_struct("VdomInnerExclusive")
94− .field("component", &"..")
95− .field("dom_buffers", &self.dom_buffers)
96− .field("change_list", &self.change_list)
97− .field("container", &self.container)
98− .field("events_registry", &self.events_registry)
99− .field("events_trampoline", &"..")
100− .field("current_root", &self.current_root)
101− .finish()
102− }
103−}
104−
105−impl Drop for VdomInnerExclusive {
106− fn drop(&mut self) {
107− debug!("Dropping VdomInnerExclusive");
108−
109− // Make sure that we clean up our JS listeners and all that before we
110− // empty the container.
111− unsafe {
112− ManuallyDrop::drop(&mut self.change_list);
113− }
114−
115− let registry = self.events_registry.take().unwrap_throw();
116− let mut registry = registry.borrow_mut();
117− registry.clear_active_listeners();
118−
119− empty_container(&self.container);
120− }
121−}
122−
123−cfg_if::cfg_if! {
124− if #[cfg(all(feature = "xxx-unstable-internal-use-only", not(target_arch = "wasm32")))] {
125− fn empty_container(_container: &crate::Element) {}
126− fn initialize_container(_container: &crate::Element) {}
127− } else {
128− fn empty_container(container: &crate::Element) {
129− container.set_inner_html("");
130− }
131−
132− fn initialize_container(container: &crate::Element) {
133− empty_container(container);
134−
135− // Create the dummy `<div/>` child in the container.
136− let window = web_sys::window().expect_throw("should have access to the Window");
137− let document = window
138− .document()
139− .expect("should have access to the Document");
140− container
141− .append_child(
142− document
143− .create_element("div")
144− .expect("should create element OK")
145− .as_ref(),
146− )
147− .expect("should append child OK");
148− }
149− }
150−}
151−
152−impl Vdom {
153− /// Mount a new `Vdom` in the given container element with the given root
154− /// rendering component.
155− ///
156− /// This will box the given component into trait object.
157− pub fn new<R>(container: &crate::Element, component: R) -> Vdom
158− where
159− R: RootRender,
160− {
161− Self::with_boxed_root_render(container, Box::new(component) as Box<dyn RootRender>)
162− }
163−
164− /// Construct a `Vdom` with the already-boxed-as-a-trait-object root
165− /// rendering component.
166− pub fn with_boxed_root_render(
167− container: &crate::Element,
168− component: Box<dyn RootRender>,
169− ) -> Vdom {
170− crate::strace::init_strace();
171−
172− let dom_buffers = [Bump::new(), Bump::new()];
173− let change_list = ManuallyDrop::new(ChangeListPersistentState::new(container));
174−
175− // Create a dummy `<div/>` in our container.
176− initialize_container(container);
177− let current_root =
178− Node::element(&dom_buffers[0], NodeKey::NONE, "div", &[], &[], &[], None);
179− let current_root = Some(unsafe { extend_node_lifetime(current_root) });
180−
181− let container = container.clone();
182− let inner = Rc::new(VdomInner {
183− shared: VdomInnerShared {
184− render_scheduled: Cell::new(None),
185− },
186− exclusive: RefCell::new(VdomInnerExclusive {
187− component: Some(component),
188− dom_buffers: Some(dom_buffers),
189− change_list,
190− container,
191− current_root,
192− events_registry: None,
193− events_trampoline: None,
194− cached_set: crate::RefCell::new(Default::default()),
195− templates: Default::default(),
196− }),
197− });
198−
199− let (events_registry, events_trampoline) = EventsRegistry::new(Rc::downgrade(&inner));
200−
201− {
202− let mut inner = inner.exclusive.borrow_mut();
203− inner.events_registry = Some(events_registry);
204− inner.change_list.init_events_trampoline(&events_trampoline);
205− debug_assert!(inner.events_trampoline.is_none());
206− inner.events_trampoline = Some(events_trampoline);
207−
208− // Diff and apply the `contents` against our dummy `<div/>`.
209− inner.render();
210− }
211−
212− Vdom { inner }
213− }
214−
215− /// Immediately re-render and diff. Only for internal testing and
216− /// benchmarking purposes.
217− #[cfg(feature = "xxx-unstable-internal-use-only")]
218− pub fn immediately_render_and_diff<R>(&self, component: R)
219− where
220− R: RootRender,
221− {
222− let mut exclusive = self.inner.exclusive.borrow_mut();
223− let component = Box::new(component) as Box<dyn RootRender>;
224− exclusive.component = Some(component);
225− exclusive.render();
226− }
227−
228− /// Run this virtual DOM and its listeners forever and never unmount it.
229− #[inline]
230− pub fn forget(self) {
231− mem::forget(self);
232− }
233−
234− /// Get a weak handle to this virtual DOM.
235− #[inline]
236− pub fn weak(&self) -> VdomWeak {
237− VdomWeak::new(&self.inner)
238− }
239−
240− /// Unmount this virtual DOM, unregister its event listeners, and return its
241− /// root render component.
242− #[inline]
243− pub fn unmount(self) -> Box<dyn RootRender> {
244− Rc::try_unwrap(self.inner.clone())
245− .map_err(|_| ())
246− .unwrap_throw()
247− .exclusive
248− .into_inner()
249− .component
250− .take()
251− .unwrap_throw()
252− }
253−}
254−
255−impl VdomInnerExclusive {
256− /// Get an exclusive reference to the underlying render component as a raw
257− /// trait object.
258− #[inline]
259− pub(crate) fn component_raw_mut(&mut self) -> &mut dyn RootRender {
260− &mut **self.component.as_mut().unwrap_throw()
261− }
262−
263− /// Re-render this virtual dom's current component.
264− pub(crate) fn render(&mut self) {
265− unsafe {
266− let events_registry = self.events_registry.take().unwrap();
267− {
268− let mut registry = events_registry.borrow_mut();
269−
270− // Reset the inactive bump arena's pointer.
271− let mut dom_buffers = self.dom_buffers.take().unwrap_throw();
272− dom_buffers[1].reset();
273−
274− // Render the new current contents into the inactive bump arena.
275− let mut cx =
276− RenderContext::new(&dom_buffers[1], &self.cached_set, &mut self.templates);
277− let new_contents = self.component.as_ref().unwrap_throw().render(&mut cx);
278− let new_contents = extend_node_lifetime(new_contents);
279−
280− // Diff the old contents with the new contents.
281− let old_contents = self.current_root.take().unwrap();
282− let mut cache_roots;
283− {
284− let cached_set = self.cached_set.borrow();
285− cache_roots = cached_set.new_roots_set();
286− let mut change_list = self.change_list.builder();
287− crate::diff::diff(
288− &cached_set,
289− &mut change_list,
290− &mut registry,
291− &old_contents,
292− &new_contents,
293− &mut cache_roots,
294− );
295−
296− // Tell JS to apply our diff-generated changes to the physical DOM!
297− change_list.finish();
298− }
299−
300− {
301− // Clean up unused cached renders.
302− let mut cached_set = self.cached_set.borrow_mut();
303− cached_set.gc(&mut registry, cache_roots);
304− }
305−
306− // Swap the buffers to make the bump arena with the new contents the
307− // active arena, and the old one into the inactive arena.
308− self.swap_buffers(dom_buffers);
309− self.set_current_root(new_contents);
310− }
311−
312− self.events_registry = Some(events_registry);
313− }
314− }
315−
316− fn swap_buffers(&mut self, mut dom_buffers: [Bump; 2]) {
317− debug_assert!(self.dom_buffers.is_none());
318− let (first, second) = dom_buffers.as_mut().split_at_mut(1);
319− mem::swap(&mut first[0], &mut second[0]);
320− self.dom_buffers = Some(dom_buffers);
321− }
322−
323− unsafe fn set_current_root(&mut self, current: Node<'static>) {
324− debug_assert!(self.current_root.is_none());
325− self.current_root = Some(current);
326− }
327−}
328−
329−fn request_animation_frame(f: &Closure<dyn FnMut()>) {
330− web_sys::window()
331− .expect_throw("should have a window")
332− .request_animation_frame(f.as_ref().unchecked_ref())
333− .expect_throw("should register `requestAnimationFrame` OK");
334−}
335−
336−fn with_animation_frame<F>(mut f: F)
337−where
338− F: 'static + FnMut(),
339−{
340− let g = Rc::new(RefCell::new(None));
341− let h = g.clone();
342−
343− let f = Closure::wrap(Box::new(move || {
344− *g.borrow_mut() = None;
345− f();
346− }) as Box<dyn FnMut()>);
347− request_animation_frame(&f);
348−
349− *h.borrow_mut() = Some(f);
350−}
351−
352−/// An operation failed because the virtual DOM was already dropped and
353−/// unmounted.
354−#[derive(Debug)]
355−pub struct VdomDroppedError {}
356−
357−impl fmt::Display for VdomDroppedError {
358− fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
359− write!(f, "The virtual DOM was dropped.")
360− }
361−}
362−
363−impl std::error::Error for VdomDroppedError {}
364−
365−impl VdomWeak {
366− /// Construct a new weak handle to the given virtual DOM.
367− #[inline]
368− pub(crate) fn new(inner: &Rc<VdomInner>) -> VdomWeak {
369− VdomWeak {
370− inner: Rc::downgrade(inner),
371− }
372− }
373−
374− /// Replace the root rendering component with the new `root`.
375− ///
376− /// Returns a future that resolves to the *old* root component.
377− pub async fn set_component(
378− self,
379− root: Box<dyn RootRender>,
380− ) -> Result<Box<dyn RootRender + 'static>, VdomDroppedError> {
381− let inner = self.inner.upgrade().ok_or(VdomDroppedError {})?;
382−
383− // Wait for a new tick of the micro-task queue
384− let _ = JsFuture::from(js_sys::Promise::resolve(&JsValue::null())).await;
385−
386− let old = {
387− let mut exclusive = inner.exclusive.borrow_mut();
388− mem::replace(&mut *exclusive.component.as_mut().unwrap_throw(), root)
389− };
390−
391− VdomWeak::new(&inner).render().await?;
392−
393− Ok(old)
394− }
395−
396− /// Execute `f` with a reference to this virtual DOM's root rendering
397− /// component.
398− ///
399− /// To ensure exclusive access to the root rendering component, the
400− /// invocation takes place on a new tick of the micro-task queue.
401− pub async fn with_component<F, T>(&self, f: F) -> Result<T, VdomDroppedError>
402− where
403− F: 'static + FnOnce(&mut dyn RootRender) -> T,
404− {
405− let inner = self.inner.upgrade().ok_or(VdomDroppedError {})?;
406−
407− // Wait for a new tick of the micro-task queue
408− let _ = JsFuture::from(js_sys::Promise::resolve(&JsValue::null())).await;
409−
410− let mut exclusive = inner.exclusive.borrow_mut();
411−
412− Ok(f(exclusive.component_raw_mut()))
413− }
414−
415− /// Schedule a render to occur during the next animation frame.
416− ///
417− /// If you want a future that resolves after the render has finished, use
418− /// `render` instead.
419− pub fn schedule_render(&self) {
420− debug!("VdomWeak::schedule_render");
421−
422− let future = self.render();
423−
424− wasm_bindgen_futures::spawn_local(async move {
425− let _ = future.await;
426− });
427− }
428−
429− /// Schedule a render to occur during the next animation frame and return a
430− /// future that will complete once the render has finished.
431− ///
432− /// If you don't want to do more things after the render completes, then use
433− /// `schedule_render` instead of `render`.
434− pub fn render(&self) -> impl Future<Output = Result<(), VdomDroppedError>> {
435− let inner = self.inner.upgrade();
436−
437− async move {
438− let inner = inner.ok_or(VdomDroppedError {})?;
439−
440− let promise = inner.shared.render_scheduled.take().unwrap_or_else(|| {
441− js_sys::Promise::new(&mut |resolve, reject| {
442− let vdom = VdomWeak {
443− inner: Rc::downgrade(&inner),
444− };
445−
446− with_animation_frame(move || match vdom.inner.upgrade() {
447− None => {
448− warn!("VdomWeak::render: vdom unmounted before we could render");
449− let r = reject.call0(&JsValue::null());
450− debug_assert!(r.is_ok());
451− }
452− Some(inner) => {
453− let mut exclusive = inner.exclusive.borrow_mut();
454− exclusive.render();
455−
456− // We did the render, so take the promise away
457− // and let future `render` calls request new
458− // animation frames.
459− let _ = inner.shared.render_scheduled.take();
460−
461− let r = resolve.call0(&JsValue::null());
462− debug_assert!(r.is_ok());
463− }
464− });
465− })
466− });
467−
468− inner.shared.render_scheduled.set(Some(promise.clone()));
469−
470− JsFuture::from(promise)
471− .await
472− .map(drop)
473− .map_err(|_| VdomDroppedError {})
474− }
475− }
476−}
dodrio/tests/web/cached.jsdeleted−11
@@ -1,11 +0,0 @@
1−let cloneNodeCount = 0;
2−
3−const origCloneNode = Node.prototype.cloneNode;
4−Node.prototype.cloneNode = function (...args) {
5− cloneNodeCount += 1;
6− return origCloneNode.apply(this, args);
7−};
8−
9−export function getCloneNodeCount() {
10− return cloneNodeCount;
11−}
dodrio/tests/web/cached.rsdeleted−289
@@ -1,289 +0,0 @@
1−use super::{assert_rendered, before_after, create_element, RenderFn};
2−use dodrio::{builder::*, bumpalo, Cached, Node, Render, RenderContext, RootRender, Vdom};
3−use std::cell::Cell;
4−use std::rc::Rc;
5−use wasm_bindgen::prelude::*;
6−use wasm_bindgen_test::*;
7−
8−#[derive(Default)]
9−pub struct CountRenders {
10− render_count: Cell<usize>,
11−}
12−
13−impl CountRenders {
14− pub fn new() -> CountRenders {
15− CountRenders {
16− render_count: Cell::new(0),
17− }
18− }
19−}
20−
21−impl<'a> Render<'a> for CountRenders {
22− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
23− let count = self.render_count.get() + 1;
24− self.render_count.set(count);
25−
26− let s = bumpalo::format!(in cx.bump, "{}", count);
27− text(s.into_bump_str())
28− }
29−}
30−
31−#[wasm_bindgen_test]
32−async fn uses_cached_render() {
33− use dodrio::builder::*;
34−
35− let cached = Cached::new(CountRenders::new());
36−
37− let container0 = create_element("div");
38− let container1 = container0.clone();
39− let container2 = container0.clone();
40− let container3 = container0.clone();
41− let container4 = container0.clone();
42−
43− let vdom0 = Rc::new(Vdom::new(&container0, cached));
44− let vdom1 = vdom0.clone();
45− let vdom2 = vdom0.clone();
46− let vdom3 = vdom0.clone();
47− let vdom4 = vdom0.clone();
48− let vdom5 = vdom0.clone();
49− let vdom6 = vdom0.clone();
50− let vdom7 = vdom0.clone();
51−
52− // We render, populate the cache, and get "1".
53− vdom0.weak().render().await.unwrap();
54− vdom0
55− .weak()
56− .with_component(move |comp| {
57− let comp = comp.unwrap_mut::<Cached<CountRenders>>();
58− assert_eq!(comp.render_count.get(), 1);
59− assert_rendered(&container1, &RenderFn(|_| text("1")));
60− })
61− .await
62− .unwrap();
63−
64− // We re-render, re-use the cached node, and get "1" again.
65− vdom1.weak().render().await.unwrap();
66− vdom2
67− .weak()
68− .with_component(move |comp| {
69− let comp = comp.unwrap_mut::<Cached<CountRenders>>();
70− assert_eq!(comp.render_count.get(), 1);
71− assert_rendered(&container2, &RenderFn(|_| text("1")));
72− })
73− .await
74− .unwrap();
75−
76− // We invalidate the cache, re-render, re-populate the cache, and should now have "2".
77− vdom3
78− .weak()
79− .with_component(move |comp| {
80− let comp = comp.unwrap_mut::<Cached<CountRenders>>();
81− Cached::invalidate(comp);
82− })
83− .await
84− .unwrap();
85− vdom4.weak().render().await.unwrap();
86− vdom5
87− .weak()
88− .with_component(move |comp| {
89− let comp = comp.unwrap_mut::<Cached<CountRenders>>();
90− assert_eq!(comp.render_count.get(), 2);
91− assert_rendered(&container3, &RenderFn(|_| text("2")));
92− })
93− .await
94− .unwrap();
95−
96− // We re-render, re-use the cached node, and get "2" again.
97− vdom6.weak().render().await.unwrap();
98− vdom7
99− .weak()
100− .with_component(move |comp| {
101− let comp = comp.unwrap_mut::<Cached<CountRenders>>();
102− assert_eq!(comp.render_count.get(), 2);
103− assert_rendered(&container4, &RenderFn(|_| text("2")));
104− })
105− .await
106− .unwrap();
107−}
108−
109−#[wasm_bindgen(module = "/tests/web/cached.js")]
110−extern "C" {
111− #[wasm_bindgen(js_name = getCloneNodeCount)]
112− fn get_clone_node_count() -> u32;
113−}
114−
115−#[wasm_bindgen_test]
116−async fn creating_cached_nodes_uses_clone_node() {
117− use dodrio::builder::*;
118−
119− let container = create_element("div");
120−
121− // The initial vdom render should populate the cache and save a template.
122− let vdom0 = Rc::new(Vdom::new(&container, Cached::new(CountRenders::new())));
123− let vdom1 = vdom0.clone();
124− let _vdom2 = vdom0.clone();
125−
126− let clone_node_count_before = get_clone_node_count();
127−
128− // Render something entirely different...
129− vdom0
130− .weak()
131− .set_component(Box::new(RenderFn(|_| text("hi"))) as Box<dyn RootRender>)
132− .await
133− .unwrap();
134−
135− // Re-render our cached node. Since we are creating it from scratch,
136− // and already have a template from the earlier render, we should
137− // call `cloneNode` here.
138− vdom1
139− .weak()
140− .set_component(Box::new(Cached::new(CountRenders::new())) as Box<dyn RootRender>)
141− .await
142− .unwrap();
143−
144− let clone_node_count_after = get_clone_node_count();
145− assert_eq!(clone_node_count_after, clone_node_count_before + 1);
146−}
147−
148−struct Id(&'static str);
149−
150−impl Default for Id {
151− fn default() -> Id {
152− Id("")
153− }
154−}
155−
156−impl<'a> Render<'a> for Id {
157− fn render(&self, _cx: &mut RenderContext<'a>) -> Node<'a> {
158− text(self.0)
159− }
160−}
161−
162−thread_local! {
163− static WARM_CHEESE: Rc<Cached<Id>> = Rc::new(Cached::new(Id("cheese")));
164− static WARM_CHEESIER: Rc<Cached<Id>> = Rc::new(Cached::new(Id("cheesier")));
165−}
166−
167−fn warm_cheese<'a>(cx: &mut RenderContext<'a>) -> Node<'a> {
168− WARM_CHEESE.with(|c| {
169− let _ = c.render(cx);
170− c.render(cx)
171− })
172−}
173−
174−fn warm_cheesier<'a>(cx: &mut RenderContext<'a>) -> Node<'a> {
175− WARM_CHEESIER.with(|c| {
176− let _ = c.render(cx);
177− c.render(cx)
178− })
179−}
180−
181−before_after! {
182− cold_cache_and_not_cached {
183− before(cx) {
184− Cached::new(Id("cheese")).render(cx)
185− }
186− after(cx) {
187− Id("ravioli").render(cx)
188− }
189− }
190−
191− not_cached_and_cold_cache {
192− before(cx) {
193− Id("ravioli").render(cx)
194− }
195− after(cx) {
196− Cached::new(Id("cheese")).render(cx)
197− }
198− }
199−
200− cold_cache_and_cold_cache_same {
201− before(cx) {
202− Cached::new(Id("cheese")).render(cx)
203− }
204− after(cx) {
205− Cached::new(Id("cheese")).render(cx)
206− }
207− }
208−
209− cold_cache_and_cold_cache_different {
210− before(cx) {
211− Cached::new(Id("cheese")).render(cx)
212− }
213− after(cx) {
214− Cached::new(Id("ravioli")).render(cx)
215− }
216− }
217−
218− warm_cache_and_not_cached {
219− before(cx) {
220− warm_cheese(cx)
221− }
222− after(cx) {
223− Id("ravioli").render(cx)
224− }
225− }
226−
227− not_cached_and_warm_cache {
228− before(cx) {
229− Id("ravioli").render(cx)
230− }
231− after(cx) {
232− warm_cheese(cx)
233− }
234− }
235−
236− warm_cache_and_warm_cache_same {
237− before(cx) {
238− warm_cheese(cx)
239− }
240− after(cx) {
241− warm_cheese(cx)
242− }
243− }
244−
245− warm_cache_and_warm_cache_different {
246− before(cx) {
247− warm_cheese(cx)
248− }
249− after(cx) {
250− warm_cheesier(cx)
251− }
252− }
253−
254− cold_cache_and_warm_cache_same {
255− before(cx) {
256− Cached::new(Id("cheese")).render(cx)
257− }
258− after(cx) {
259− warm_cheese(cx)
260− }
261− }
262−
263− cold_cache_and_warm_cache_different {
264− before(cx) {
265− Cached::new(Id("cheese")).render(cx)
266− }
267− after(cx) {
268− warm_cheesier(cx)
269− }
270− }
271−
272− warm_cache_and_cold_cache_same {
273− before(cx) {
274− warm_cheese(cx)
275− }
276− after(cx) {
277− Cached::new(Id("cheese")).render(cx)
278− }
279− }
280−
281− warm_cache_and_cold_cache_different {
282− before(cx) {
283− warm_cheesier(cx)
284− }
285− after(cx) {
286− Cached::new(Id("cheese")).render(cx)
287− }
288− }
289−}
dodrio/tests/web/events.rsdeleted−250
@@ -1,250 +0,0 @@
1−use super::create_element;
2−use dodrio::{Node, Render, RenderContext, Vdom};
3−use futures::future::{select, Either};
4−use std::cell::{Cell, RefCell};
5−use std::rc::Rc;
6−use wasm_bindgen::prelude::*;
7−use wasm_bindgen::JsCast;
8−use wasm_bindgen_test::*;
9−
10−struct EventContainer {
11− event: &'static str,
12− on_event: Box<dyn FnMut()>,
13−}
14−
15−impl EventContainer {
16− fn new<F>(event: &'static str, on_event: F) -> EventContainer
17− where
18− F: 'static + FnMut(),
19− {
20− EventContainer {
21− event,
22− on_event: Box::new(on_event),
23− }
24− }
25−}
26−
27−impl<'a> Render<'a> for EventContainer {
28− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
29− use dodrio::builder::*;
30− div(&cx)
31− .attr("id", "target")
32− .on(self.event, |root, _scheduler, _event| {
33− (root.unwrap_mut::<EventContainer>().on_event)();
34− })
35− .finish()
36− }
37−}
38−
39−fn target(container: &web_sys::Element) -> web_sys::HtmlElement {
40− container
41− .query_selector("#target")
42− .expect_throw("should querySelector OK")
43− .expect_throw("should find `#target` in container")
44− .unchecked_into()
45−}
46−
47−#[wasm_bindgen_test]
48−async fn click() {
49− let container = create_element("div");
50−
51− let (sender, receiver) = futures::channel::oneshot::channel();
52− let mut sender = Some(sender);
53−
54− let _vdom = Vdom::new(
55− &container,
56− EventContainer::new("click", move || {
57− sender
58− .take()
59− .expect_throw("should only call listener once")
60− .send(())
61− .expect_throw("should not have dropped the receiver");
62− }),
63− );
64−
65− target(&container).click();
66−
67− receiver.await.unwrap();
68−}
69−
70−#[wasm_bindgen_test]
71−async fn updated_listener_is_called() {
72− let container = create_element("div");
73−
74− let (first_sender, first_receiver) = futures::channel::oneshot::channel();
75− let mut first_sender = Some(first_sender);
76−
77− let vdom = Vdom::new(
78− &container,
79− EventContainer::new("click", move || {
80− first_sender
81− .take()
82− .expect_throw("should only call listener once")
83− .send("first")
84− .expect_throw("should not have dropped the receiver");
85− }),
86− );
87−
88− let (second_sender, second_receiver) = futures::channel::oneshot::channel();
89− let mut second_sender = Some(second_sender);
90−
91− vdom.weak()
92− .set_component(Box::new(EventContainer::new("click", move || {
93− second_sender
94− .take()
95− .expect_throw("should only call listener once")
96− .send("second")
97− .expect_throw("should not have dropped the receiver");
98− })))
99− .await
100− .unwrap();
101−
102− target(&container).click();
103−
104− match select(first_receiver, second_receiver).await {
105− Either::Left((Ok(_), _)) => panic!(),
106− Either::Left((Err(_), second)) => assert_eq!(second.await, Ok("second")),
107− Either::Right((Ok(msg), _)) => assert_eq!(msg, "second"),
108− Either::Right((Err(_), _)) => panic!(),
109− }
110−}
111−
112−struct ListensOnlyOnFirstRender {
113− count: Cell<usize>,
114− callback: Box<dyn FnMut()>,
115−}
116−
117−impl ListensOnlyOnFirstRender {
118− fn new<F>(callback: F) -> ListensOnlyOnFirstRender
119− where
120− F: 'static + FnMut(),
121− {
122− ListensOnlyOnFirstRender {
123− count: Cell::new(0),
124− callback: Box::new(callback),
125− }
126− }
127−}
128−
129−impl<'a> Render<'a> for ListensOnlyOnFirstRender {
130− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
131− use dodrio::builder::*;
132−
133− let count = self.count.get();
134− self.count.set(count + 1);
135−
136− let mut elem = div(&cx).attr("id", "target");
137− if count == 0 {
138− elem = elem.on("click", |root, _scheduler, _event| {
139− (root.unwrap_mut::<ListensOnlyOnFirstRender>().callback)();
140− });
141− }
142− elem.finish()
143− }
144−}
145−
146−#[wasm_bindgen_test]
147−async fn removed_listener_is_not_called() {
148− let container = create_element("div");
149−
150− let (outer_sender, outer_receiver) = futures::channel::oneshot::channel();
151− let outer_sender = Rc::new(RefCell::new(Some(outer_sender)));
152− let outer_listener = Closure::wrap(Box::new(move |_| {
153− outer_sender
154− .borrow_mut()
155− .take()
156− .expect_throw("should only invoke outer_listener once")
157− .send("outer")
158− .expect_throw("should not have dropped receiver");
159− }) as Box<dyn FnMut(web_sys::Event)>);
160−
161− container
162− .add_event_listener_with_callback("click", outer_listener.as_ref().unchecked_ref())
163− .unwrap();
164−
165− let (vdom_sender, vdom_receiver) = futures::channel::oneshot::channel();
166− let mut vdom_sender = Some(vdom_sender);
167−
168− // Render a vdom with a listener into container.
169− let vdom = Vdom::new(
170− &container,
171− ListensOnlyOnFirstRender::new(move || {
172− vdom_sender
173− .take()
174− .expect_throw("should not invoke vdom listener more than once")
175− .send("inner")
176− .expect_throw("should not have dropped the receiver");
177− }),
178− );
179−
180− // Re-render, so we aren't listening anymore.
181− vdom.weak().render().await.unwrap();
182−
183− target(&container).click();
184−
185− // We should get our container's event handler fired, and not the unmounted
186− // vdom's event handler.
187− match select(outer_receiver, vdom_receiver).await {
188− Either::Left((Ok(msg), _)) => assert_eq!(msg, ("outer")),
189− Either::Left((Err(_), _)) => panic!(),
190− Either::Right((Ok(_), _)) => panic!(),
191− Either::Right((Err(_), outer)) => assert_eq!(outer.await, Ok("outer")),
192− }
193−}
194−
195−#[wasm_bindgen_test]
196−async fn event_does_not_fire_after_unmounting() {
197− let container = create_element("div");
198−
199− let (outer_sender, outer_receiver) = futures::channel::oneshot::channel();
200− let outer_sender = Rc::new(RefCell::new(Some(outer_sender)));
201− let outer_listener = Closure::wrap(Box::new(move |_| {
202− outer_sender
203− .borrow_mut()
204− .take()
205− .expect_throw("should only invoke outer_listener once")
206− .send("outer")
207− .expect_throw("should not have dropped receiver");
208− }) as Box<dyn FnMut(web_sys::Event)>);
209−
210− container
211− .add_event_listener_with_callback("click", outer_listener.as_ref().unchecked_ref())
212− .unwrap();
213−
214− let (vdom_sender, vdom_receiver) = futures::channel::oneshot::channel();
215− let mut vdom_sender = Some(vdom_sender);
216−
217− // Render a vdom with a listener into container.
218− let vdom = Vdom::new(
219− &container,
220− EventContainer::new("click", move || {
221− vdom_sender
222− .take()
223− .expect_throw("should not invoke vdom listener more than once")
224− .send("vdom")
225− .expect_throw("should not have dropped the receiver");
226− }),
227− );
228−
229− // Grab a reference to the target before dropping clears the DOM.
230− let target = target(&container);
231−
232− // Unmount the vdom.
233− drop(vdom);
234−
235− // The target is no longer a child of the container, so re-append it so that
236− // the click will bubble.
237− container.append_child(&target).unwrap();
238−
239− // Send a click to the target.
240− target.click();
241−
242− // We should get our container's event handler fired, and not the unmounted
243− // vdom's event handler.
244− match select(outer_receiver, vdom_receiver).await {
245− Either::Left((Ok(msg), _)) => assert_eq!(msg, ("outer")),
246− Either::Left((Err(_), _)) => panic!(),
247− Either::Right((Ok(_), _)) => panic!(),
248− Either::Right((Err(_), outer)) => assert_eq!(outer.await, Ok("outer")),
249− }
250−}
dodrio/tests/web/js_api.rsdeleted−153
@@ -1,153 +0,0 @@
1−use super::{assert_rendered, create_element, RenderFn};
2−use dodrio::{builder::*, Node, Render, RenderContext, Vdom};
3−use dodrio_js_api::JsRender;
4−use futures::channel::oneshot;
5−use js_sys::Object;
6−use std::rc::Rc;
7−use wasm_bindgen::prelude::*;
8−use wasm_bindgen::JsCast;
9−use wasm_bindgen_test::*;
10−
11−pub struct WrapJs {
12− inner: JsRender,
13−}
14−
15−impl WrapJs {
16− pub fn new() -> (WrapJs, oneshot::Receiver<()>, Closure<dyn FnMut()>) {
17− let (sender, receiver) = oneshot::channel();
18−
19− let on_after_render = Closure::wrap(Box::new({
20− let mut sender = Some(sender);
21− move || {
22− sender
23− .take()
24− .expect_throw("on_after_render should only be called once")
25− .send(())
26− .expect_throw("receiver should not have been dropped");
27− }
28− }) as Box<dyn FnMut()>);
29−
30− let component = WrapJs {
31− inner: JsRender::new(JsComponent::new(&on_after_render)),
32− };
33−
34− (component, receiver, on_after_render)
35− }
36−}
37−
38−impl<'a> Render<'a> for WrapJs {
39− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
40− div(&cx)
41− .attr("class", "wrap-js")
42− .children([self.inner.render(cx)])
43− .finish()
44− }
45−}
46−
47−#[wasm_bindgen]
48−extern "C" {
49− #[wasm_bindgen(extends = Object)]
50− #[derive(Clone, Debug)]
51− type JsComponent;
52−
53− #[wasm_bindgen(constructor)]
54− fn new(on_after_render: &Closure<dyn FnMut()>) -> JsComponent;
55−}
56−
57−fn eval_js_rendering_component() {
58− use std::sync::Once;
59− static EVAL: Once = Once::new();
60− EVAL.call_once(|| {
61− super::init_logging();
62− js_sys::eval(
63− r#"
64− window.JsComponent = class JsComponent {
65− constructor(onAfterRender) {
66− this.count = 0;
67− this.onAfterRender = onAfterRender;
68− }
69−
70− render() {
71− return {
72− tagName: "span",
73− attributes: [{ name: "class", value: "js-component" }],
74− listeners: [{ on: "click", callback: this.onClick.bind(this) }],
75− children: [
76− "Here is some plain text",
77− {
78− tagName: "b",
79− children: ["...and here is some bold text"]
80− },
81− String(this.count),
82− ]
83− };
84− }
85−
86− async onClick(vdom, event) {
87− this.count++;
88− await vdom.render();
89− this.onAfterRender();
90− }
91− };
92− "#,
93− )
94− .expect_throw("should eval JS component OK");
95− });
96−}
97−
98−#[wasm_bindgen_test]
99−async fn can_use_js_rendering_components() {
100− eval_js_rendering_component();
101−
102− let container = create_element("div");
103− let (component, receiver, _closure) = WrapJs::new();
104− let _vdom = Rc::new(Vdom::new(&container, component));
105−
106− assert_rendered(
107− &container,
108− &RenderFn(|cx| {
109− div(&cx)
110− .attr("class", "wrap-js")
111− .children([span(&cx)
112− .attr("class", "js-component")
113− .children([
114− text("Here is some plain text"),
115− b(&cx)
116− .children([text("...and here is some bold text")])
117− .finish(),
118− text("0"),
119− ])
120− .finish()])
121− .finish()
122− }),
123− );
124−
125− container
126− .query_selector(".js-component")
127− .expect_throw("should querySelector OK")
128− .expect_throw("should find `.js-component` element OK")
129− .dyn_into::<web_sys::HtmlElement>()
130− .expect_throw("should be an `HTMLElement` object")
131− .click();
132−
133− receiver.await.unwrap();
134−
135− assert_rendered(
136− &container,
137− &RenderFn(|cx| {
138− div(&cx)
139− .attr("class", "wrap-js")
140− .children([span(&cx)
141− .attr("class", "js-component")
142− .children([
143− text("Here is some plain text"),
144− b(&cx)
145− .children([text("...and here is some bold text")])
146− .finish(),
147− text("1"),
148− ])
149− .finish()])
150− .finish()
151− }),
152− );
153−}
dodrio/tests/web/keyed.jsdeleted−25
@@ -1,25 +0,0 @@
1−export function saveKeyedElements(container) {
2− const saved = new Map;
3− const children = container.querySelectorAll(".keyed");
4− for (const child of children) {
5− saved.set(child.id, child);
6− }
7− return saved;
8−}
9−
10−function assertEq(a, b, msg) {
11− if (a !== b) {
12− throw new Error(`assertEq failed: ${a} !== ${b}; ${msg}`);
13− }
14−}
15−
16−export function checkKeyedElements(container, saved) {
17− const children = container.querySelectorAll(".keyed");
18− for (const child of children) {
19− console.log(`checking child=${child.outerHTML}`);
20− const original = saved.get(child.id);
21− if (original) {
22− assertEq(original, child, `did not preserve child with key=${child.id}`);
23− }
24− }
25−}
dodrio/tests/web/keyed.rsdeleted−269
@@ -1,269 +0,0 @@
1−use crate::{assert_rendered, create_element};
2−use dodrio::{builder::*, bumpalo, Node, Render, RenderContext, Vdom};
3−use log::*;
4−use std::rc::Rc;
5−use wasm_bindgen::prelude::*;
6−use wasm_bindgen_test::*;
7−
8−struct Keyed(u16);
9−
10−impl<'a> Render<'a> for Keyed {
11− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
12− let key = bumpalo::format!(in cx.bump, "{}", self.0).into_bump_str();
13− div(&cx)
14− .attr("class", "keyed")
15− .attr("id", key)
16− .key(self.0 as u32)
17− .finish()
18− }
19−}
20−
21−fn keyed<'a, Keys>(cx: &mut RenderContext<'a>, keys: Keys) -> Node<'a>
22−where
23− Keys: AsRef<[u16]>,
24−{
25− let mut parent = div(&cx).attr("class", "parent");
26−
27− for &k in keys.as_ref() {
28− parent = parent.child(Keyed(k).render(cx));
29− }
30−
31− parent.finish()
32−}
33−
34−async fn assert_keyed<Before, After>(before: Before, after: After) -> Result<(), JsValue>
35−where
36− Before: 'static + for<'a> Render<'a>,
37− After: 'static + for<'a> Render<'a>,
38−{
39− #[wasm_bindgen(module = "/tests/web/keyed.js")]
40− extern "C" {
41− #[wasm_bindgen(js_name = saveKeyedElements)]
42− fn save_keyed_elements(container: &web_sys::Element) -> JsValue;
43−
44− #[wasm_bindgen(js_name = checkKeyedElements)]
45− fn check_keyed_elements(container: &web_sys::Element, saved: JsValue);
46− }
47−
48− let container = create_element("div");
49−
50− let before = Rc::new(before);
51− let after = Rc::new(after);
52−
53− debug!("====== Rendering the *before* DOM into the physical DOM ======");
54− let vdom1 = Rc::new(Vdom::new(&container, before.clone()));
55− let _vdom2 = vdom1.clone();
56− let saved = save_keyed_elements(&container);
57−
58− debug!("====== Checking the *before* DOM against the physical DOM ======");
59− assert_rendered(&container, &before);
60−
61− debug!("====== Rendering the *after* DOM into the physical DOM ======");
62− let weak = vdom1.weak();
63− weak.set_component(Box::new(after.clone()))
64− .await
65− .map_err(|e| e.to_string())?;
66−
67− debug!("====== Checking the *after* DOM against the physical DOM ======");
68− assert_rendered(&container, &after);
69− check_keyed_elements(&container, saved);
70−
71− Ok(())
72−}
73−
74−macro_rules! keyed_tests {
75− ( $(
76− $name:ident {
77− before($before_cx:ident) {
78− $( $before:tt )*
79− }
80− after($after_cx:ident) {
81− $( $after:tt )*
82− }
83− }
84− )* ) => {
85− $(
86− #[wasm_bindgen_test]
87− async fn $name() {
88− use crate::RenderFn;
89− log::debug!("############### {} ###############", stringify!($name));
90− assert_keyed(
91− RenderFn(|$before_cx| { $( $before )* }),
92− RenderFn(|$after_cx| { $( $after )* }),
93− )
94− .await
95− .unwrap()
96− }
97− )*
98− }
99−}
100−
101−keyed_tests! {
102− same_order {
103− before(cx) {
104− keyed(cx, [1, 2, 3])
105− }
106− after(cx) {
107− keyed(cx, [1, 2, 3])
108− }
109− }
110−
111− same_order_append {
112− before(cx) {
113− keyed(cx, [1])
114− }
115− after(cx) {
116− keyed(cx, [1, 2])
117− }
118− }
119−
120− same_order_delete {
121− before(cx) {
122− keyed(cx, [1, 2, 3])
123− }
124− after(cx) {
125− keyed(cx, [1, 2])
126− }
127− }
128−
129− same_suffix {
130− before(cx) {
131− keyed(cx, [1, 2, 3])
132− }
133− after(cx) {
134− keyed(cx, [4, 2, 3])
135− }
136− }
137−
138− same_prefix_and_suffix_reorder_middle {
139− before(cx) {
140− keyed(cx, [1, 2, 3, 4])
141− }
142− after(cx) {
143− keyed(cx, [1, 3, 2, 4])
144− }
145− }
146−
147− same_prefix_and_suffix_new_middle {
148− before(cx) {
149− keyed(cx, [2, 3, 4, 5])
150− }
151− after(cx) {
152− keyed(cx, [2, 7, 8, 5])
153− }
154− }
155−
156− reverse_order {
157− before(cx) {
158− keyed(cx, [1, 2, 3])
159− }
160− after(cx) {
161− keyed(cx, [3, 2, 1])
162− }
163− }
164−
165− no_shared_keys {
166− before(cx) {
167− keyed(cx, [1, 2])
168− }
169− after(cx) {
170− keyed(cx, [3, 4])
171− }
172− }
173−
174− new_keys_in_middle {
175− before(cx) {
176− keyed(cx, [1, 2])
177− }
178− after(cx) {
179− keyed(cx, [1, 3, 4, 2])
180− }
181− }
182−
183− new_keys_at_start_and_end {
184− before(cx) {
185− keyed(cx, [1, 2, 3])
186− }
187− after(cx) {
188− keyed(cx, [4, 1, 2, 3, 5])
189− }
190− }
191−
192− delete_prefix {
193− before(cx) {
194− keyed(cx, [1, 2, 3])
195− }
196− after(cx) {
197− keyed(cx, [2, 3])
198− }
199− }
200−
201− delete_suffix {
202− before(cx) {
203− keyed(cx, [1, 2, 3])
204− }
205− after(cx) {
206− keyed(cx, [1, 2])
207− }
208− }
209−
210− delete_middle {
211− before(cx) {
212− keyed(cx, [1, 2, 3])
213− }
214− after(cx) {
215− keyed(cx, [1, 3])
216− }
217− }
218−
219− nested_keyed_children {
220− before(cx) {
221− ul(&cx)
222− .children([
223− li(&cx)
224− .key(1)
225− .children([
226− keyed(cx, [2, 3, 4])
227− ])
228− .finish(),
229− li(&cx)
230− .key(5)
231− .children([
232− keyed(cx, [6, 7, 8])
233− ])
234− .finish(),
235− li(&cx)
236− .key(9)
237− .children([
238− keyed(cx, [10, 11, 12])
239− ])
240− .finish(),
241− ])
242− .finish()
243− }
244− after(cx) {
245− ul(&cx)
246− .children([
247− li(&cx)
248− .key(9)
249− .children([
250− keyed(cx, [12, 11, 10])
251− ])
252− .finish(),
253− li(&cx)
254− .key(5)
255− .children([
256− keyed(cx, [8, 7, 6])
257− ])
258− .finish(),
259− li(&cx)
260− .key(1)
261− .children([
262− keyed(cx, [4, 3, 2])
263− ])
264− .finish(),
265− ])
266− .finish()
267− }
268− }
269−}
dodrio/tests/web/main.rsdeleted−226
@@ -1,226 +0,0 @@
1−//! Test suite for the Web and headless browsers.
2−
3−#![cfg(all(feature = "xxx-unstable-internal-use-only", target_arch = "wasm32"))]
4−
5−use bumpalo::Bump;
6−use dodrio::{
7− Attribute, CachedSet, ElementNode, Node, NodeKind, Render, RenderContext, TextNode, Vdom,
8−};
9−use fxhash::FxHashMap;
10−use log::*;
11−use std::cell::RefCell;
12−use std::rc::Rc;
13−use wasm_bindgen::prelude::*;
14−use wasm_bindgen::JsCast;
15−use wasm_bindgen_test::*;
16−
17−wasm_bindgen_test_configure!(run_in_browser);
18−
19−pub mod cached;
20−pub mod events;
21−pub mod js_api;
22−pub mod keyed;
23−pub mod render;
24−
25−pub fn window() -> web_sys::Window {
26− web_sys::window().expect("no global `window` exists")
27−}
28−
29−pub fn document() -> web_sys::Document {
30− window()
31− .document()
32− .expect("should have a document on window")
33−}
34−
35−pub fn create_element(tag: &str) -> web_sys::Element {
36− init_logging();
37− document()
38− .create_element(tag)
39− .expect("should create element OK")
40−}
41−
42−/// Ensure that logs go to the devtools console.
43−pub fn init_logging() {
44− use std::sync::Once;
45− static START: Once = Once::new();
46− START.call_once(|| {
47− console_log::init_with_level(Level::Trace).expect("could not initialize console_log");
48− });
49−}
50−
51−/// Assert that the `container` contains the physical DOM tree that matches
52−/// `r`'s rendered virtual DOM.
53−pub fn assert_rendered<R: for<'a> Render<'a>>(container: &web_sys::Element, r: &R) {
54− init_logging();
55−
56− let cached_set = &RefCell::new(CachedSet::default());
57− let bump = &Bump::new();
58− let templates = &mut FxHashMap::default();
59− let cx = &mut RenderContext::new(bump, cached_set, templates);
60− let node = r.render(cx);
61− let child = container
62− .first_child()
63− .expect("container does not have anything rendered into it?");
64−
65− let cached_set = cached_set.borrow();
66− check_node(&cached_set, &child, &node);
67−
68− fn stringify_actual_node(n: &web_sys::Node) -> String {
69− if let Some(el) = n.dyn_ref::<web_sys::Element>() {
70− el.outer_html()
71− } else {
72− format!("#text({:?})", n.text_content())
73− }
74− }
75−
76− fn check_node(cached_set: &CachedSet, actual: &web_sys::Node, expected: &Node) {
77− debug!("check_render:");
78− debug!(" actual = {}", stringify_actual_node(&actual));
79− debug!(" expected = {:#?}", expected);
80− match expected.kind {
81− NodeKind::Text(TextNode { text }) => {
82− assert_eq!(
83− actual.node_name().to_uppercase(),
84− "#TEXT",
85− "actual.node_name() == #TEXT"
86− );
87− assert_eq!(
88− actual.text_content().unwrap_or_default(),
89− text,
90− "actual.text_content() == expected.text()"
91− );
92− }
93− NodeKind::Element(&ElementNode {
94− tag_name,
95− attributes,
96− children,
97− namespace,
98− ..
99− }) => {
100− assert_eq!(
101− actual.node_name().to_uppercase(),
102− tag_name.to_uppercase(),
103− "actual.node_name() == expected.tag_name()"
104− );
105− let actual = actual
106− .dyn_ref::<web_sys::Element>()
107− .expect("`actual` should be an `Element`");
108− check_attributes(actual.attributes(), attributes);
109− check_children(cached_set, actual.child_nodes(), children);
110− if let Some(namespace) = namespace {
111− assert_eq!(actual.namespace_uri(), Some(namespace.into()))
112− }
113− }
114− NodeKind::Cached(ref c) => {
115− let (expected, _template) = cached_set.get(c.id);
116− check_node(cached_set, actual, &expected);
117− }
118− }
119− }
120−
121− fn check_attributes(actual: web_sys::NamedNodeMap, expected: &[Attribute]) {
122− assert_eq!(
123− actual.length(),
124− expected.len() as u32,
125− "actual's number of attributes == expected's number of attributes"
126− );
127− for attr in expected {
128− let actual_attr = actual
129− .get_named_item(attr.name())
130− .expect(&format!("should have attribute \"{}\"", attr.name()));
131− assert_eq!(
132− actual_attr.value(),
133− attr.value(),
134− "actual attr value == expected attr value for attr \"{}\"",
135− attr.name()
136− );
137− }
138− }
139−
140− fn check_children(cached_set: &CachedSet, actual: web_sys::NodeList, expected: &[Node]) {
141− assert_eq!(
142− actual.length(),
143− expected.len() as u32,
144− "actual children length == expected children length"
145− );
146− for (i, child) in expected.iter().enumerate() {
147− let actual_child = actual.item(i as u32).unwrap();
148− check_node(cached_set, &actual_child, child);
149− }
150− }
151−}
152−
153−/// Use the function `F` to render.
154−pub struct RenderFn<F>(F)
155−where
156− F: for<'a> Fn(&mut RenderContext<'a>) -> Node<'a>;
157−
158−impl<'a, F> Render<'a> for RenderFn<F>
159−where
160− F: for<'b> Fn(&mut RenderContext<'b>) -> Node<'b>,
161−{
162− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
163− (self.0)(cx)
164− }
165−}
166−
167−/// Assert that if we start by rendering the `before` virtual DOM tree into a
168−/// physical DOM tree, and then diff it with the `after` virtual DOM tree, then
169−/// the physical DOM tree correctly matches `after`.
170−pub async fn assert_before_after<R, S>(before: R, after: S) -> Result<(), JsValue>
171−where
172− R: 'static + for<'a> Render<'a>,
173− S: 'static + for<'a> Render<'a>,
174−{
175− let container = create_element("div");
176−
177− let before = Rc::new(before);
178− let after = Rc::new(after);
179−
180− debug!("====== Rendering the *before* DOM into the physical DOM ======");
181− let vdom1 = Rc::new(Vdom::new(&container, before.clone()));
182− let _vdom2 = vdom1.clone();
183−
184− debug!("====== Checking the *before* DOM against the physical DOM ======");
185− assert_rendered(&container, &before);
186−
187− debug!("====== Rendering the *after* DOM into the physical DOM ======");
188− let weak = vdom1.weak();
189− weak.set_component(Box::new(after.clone()))
190− .await
191− .map_err(|e| e.to_string())?;
192−
193− debug!("====== Checking the *after* DOM against the physical DOM ======");
194− assert_rendered(&container, &after);
195−
196− Ok(())
197−}
198−
199−/// A helper macro for declaring a bunch of `assert_before_after` tests.
200−#[macro_export]
201−macro_rules! before_after {
202− ( $(
203− $name:ident {
204− before($before_bump:ident) {
205− $( $before:tt )*
206− }
207− after($after_bump:ident) {
208− $( $after:tt )*
209− }
210− }
211− )* ) => {
212− $(
213− #[wasm_bindgen_test]
214− async fn $name() {
215− use crate::{assert_before_after, RenderFn};
216− log::debug!("############### {} ###############", stringify!($name));
217− assert_before_after(
218− RenderFn(|$before_bump| { $( $before )* }),
219− RenderFn(|$after_bump| { $( $after )* })
220− )
221− .await
222− .unwrap()
223− }
224− )*
225− }
226−}
dodrio/tests/web/render.rsdeleted−343
@@ -1,343 +0,0 @@
1−use super::{assert_rendered, before_after, create_element, RenderFn};
2−use dodrio::{builder::*, bumpalo::collections::String, Node, Render, RenderContext, Vdom};
3−use std::rc::Rc;
4−use wasm_bindgen::{ JsCast};
5−use wasm_bindgen_test::*;
6−
7−#[wasm_bindgen_test]
8−fn render_initial_text() {
9− let hello = Rc::new(RenderFn(|_cx| text("hello")));
10−
11− let container = create_element("div");
12− let _vdom = Vdom::new(&container, hello.clone());
13− assert_rendered(&container, &hello);
14−}
15−
16−#[wasm_bindgen_test]
17−fn render_initial_node() {
18− let hello = Rc::new(RenderFn(|cx| {
19− div(&cx)
20− .attr("id", "hello-world")
21− .children([text("Hello "), span(&cx).child(text("World!")).finish()])
22− .finish()
23− }));
24−
25− let container = create_element("div");
26− let _vdom = Vdom::new(&container, hello.clone());
27− assert_rendered(&container, &hello);
28−}
29−
30−#[wasm_bindgen_test]
31−fn container_is_emptied_upon_drop() {
32− let container = create_element("div");
33− let vdom = Vdom::new(&container, RenderFn(|_cx| text("blah")));
34− drop(vdom);
35− assert!(container.first_child().is_none());
36−}
37−
38−/// Renders a child with a lifetime scoped to the RenderContext bump arena.
39−#[wasm_bindgen_test]
40−fn render_bump_scoped_node() {
41− struct Child<'a> {
42− name: &'a str,
43− }
44−
45− impl<'a> Render<'a> for Child<'a> {
46− fn render(&self, _cx: &mut RenderContext<'a>) -> Node<'a> {
47− text(self.name)
48− }
49− }
50−
51− struct Parent;
52−
53− impl<'a> Render<'a> for Parent {
54− fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
55− let child_name = String::from_str_in("child", cx.bump).into_bump_str();
56−
57− div(&cx)
58− .children([Child { name: child_name }.render(cx)])
59− .finish()
60− }
61− }
62−
63− let parent = Rc::new(RenderFn(|cx| {
64− Parent.render(cx)
65− }));
66−
67− let container = create_element("div");
68− let _vdom = Vdom::new(&container, parent.clone());
69−
70− assert_rendered(&container, &parent);
71−}
72−
73−/// Originally, dodrio would use the className property for SVGs.
74−///
75−/// This is problematic because when SVG elements are created, the className is flagged as a read
76−/// only property, so setting it causes an exception to be thrown. Here's an example of how this
77−/// happens:
78−///
79−/// let elem = web_sys::window()
80−/// .unwrap()
81−/// .document()
82−/// .unwrap()
83−/// .create_element_ns(Some("http://www.w3.org/2000/svg"), "svg")
84−/// .unwrap();
85−///
86−/// elem.set_class_name("does-not-work");
87−///
88−/// -----------------------------------------------------------------------------------------------
89−///
90−/// wasm-bindgen: imported JS function that was not marked as `catch` threw an error:
91−/// setting getter-only property "className"
92−///
93−/// -----------------------------------------------------------------------------------------------
94−///
95−/// Now, dodrio passes the 'class' attribute of all namespaced elements into set_attribute. This
96−/// satisfies the restrictions on SVG and keeps the optimized path for non-namespaced elements
97−#[wasm_bindgen_test(async)]
98−async fn test_svg_set_class() {
99− let container = create_element("div");
100−
101−
102− let valid_svg = Rc::new(RenderFn(|cx| {
103− ElementBuilder::new(cx.bump, "svg")
104− .namespace(Some("http://www.w3.org/2000/svg"))
105− .attr("class", "works")
106− .finish()
107− }));
108−
109− let vdom = Vdom::new(&container, valid_svg.clone());
110− let weak = vdom.weak();
111−
112− weak.render().await.unwrap();
113−
114− assert_eq!(
115− "works",
116− container.first_child()
117− .expect("unable to get svg")
118− .dyn_ref::<web_sys::Element>()
119− .expect("svg should be an element")
120− .get_attribute("class")
121− .expect("unable to get 'class' of svg")
122− );
123−}
124−
125−before_after! {
126− same_text {
127− before(_cx) {
128− text("hello")
129− }
130− after(_cx) {
131− text("hello")
132− }
133− }
134−
135− update_text {
136− before(_cx) {
137− text("before")
138− }
139− after(_cx) {
140− text("after")
141− }
142− }
143−
144− replace_text_with_elem {
145− before(_cx) {
146− text("before")
147− }
148− after(cx) {
149− div(&cx).finish()
150− }
151− }
152−
153− replace_elem_with_text {
154− before(cx) {
155− div(&cx).finish()
156− }
157− after(_cx) {
158− text("before")
159− }
160− }
161−
162− same_elem {
163− before(cx) {
164− div(&cx).finish()
165− }
166− after(cx) {
167− div(&cx).finish()
168− }
169− }
170−
171− elems_with_different_tag_names {
172− before(cx) {
173− span(&cx).finish()
174− }
175− after(cx) {
176− div(&cx).finish()
177− }
178− }
179−
180− same_tag_name_update_attribute {
181− before(cx) {
182− div(&cx).attr("value", "1").finish()
183− }
184− after(cx) {
185− div(&cx).attr("value", "2").finish()
186− }
187− }
188−
189− same_tag_name_remove_attribute {
190− before(cx) {
191− div(&cx).attr("value", "1").finish()
192− }
193− after(cx) {
194− div(&cx).finish()
195− }
196− }
197−
198− same_tag_name_add_attribute {
199− before(cx) {
200− div(&cx).finish()
201− }
202− after(cx) {
203− div(&cx).attr("value", "2").finish()
204− }
205− }
206−
207− same_tag_name_many_attributes {
208− before(cx) {
209− div(&cx)
210− .attr("before-1", "1")
211− .attr("shared-1", "1")
212− .attr("modified-1", "1")
213− .attr("before-2", "2")
214− .attr("shared-2", "2")
215− .attr("modified-2", "2")
216− .attr("before-3", "3")
217− .attr("shared-3", "3")
218− .attr("modified-3", "3")
219− .finish()
220− }
221− after(cx) {
222− div(&cx)
223− .attr("after-1", "1")
224− .attr("shared-1", "1")
225− .attr("modified-1", "100")
226− .attr("after-2", "2")
227− .attr("shared-2", "2")
228− .attr("modified-2", "200")
229− .attr("after-3", "3")
230− .attr("shared-3", "3")
231− .attr("modified-3", "300")
232− .finish()
233− }
234− }
235−
236− same_tag_same_children {
237− before(cx) {
238− div(&cx).child(text("child")).finish()
239− }
240− after(cx) {
241− div(&cx).child(text("child")).finish()
242− }
243− }
244−
245− same_tag_update_child {
246− before(cx) {
247− div(&cx).child(text("before")).finish()
248− }
249− after(cx) {
250− div(&cx).child(text("after")).finish()
251− }
252− }
253−
254− same_tag_add_child {
255− before(cx) {
256− div(&cx).finish()
257− }
258− after(cx) {
259− div(&cx).child(text("child")).finish()
260− }
261− }
262−
263− same_tag_remove_child {
264− before(cx) {
265− div(&cx).child(text("child")).finish()
266− }
267− after(cx) {
268− div(&cx).finish()
269− }
270− }
271−
272− same_tag_update_many_children {
273− before(cx) {
274− div(&cx)
275− .children([
276− div(&cx).finish(),
277− span(&cx).finish(),
278− p(&cx).finish(),
279− ])
280− .finish()
281− }
282− after(cx) {
283− div(&cx)
284− .children([
285− span(&cx).finish(),
286− p(&cx).finish(),
287− div(&cx).finish(),
288− ])
289− .finish()
290− }
291− }
292−
293− same_tag_remove_many_children {
294− before(cx) {
295− div(&cx)
296− .children([
297− div(&cx).finish(),
298− span(&cx).finish(),
299− p(&cx).finish(),
300− ])
301− .finish()
302− }
303− after(cx) {
304− div(&cx)
305− .children([
306− div(&cx).finish(),
307− ])
308− .finish()
309− }
310− }
311−
312− same_tag_add_many_children {
313− before(cx) {
314− div(&cx)
315− .children([
316− div(&cx).finish(),
317− ])
318− .finish()
319− }
320− after(cx) {
321− div(&cx)
322− .children([
323− div(&cx).finish(),
324− span(&cx).finish(),
325− p(&cx).finish(),
326− ])
327− .finish()
328− }
329− }
330−
331− same_tag_different_namespace {
332− before(cx) {
333− div(&cx)
334− .namespace(Some("http://example.com"))
335− .finish()
336− }
337− after(cx) {
338− div(&cx)
339− .namespace(Some("http://example.net"))
340− .finish()
341− }
342− }
343−}
nock/Cargo.lock+5−141
@@ -1,6 +1,6 @@
11# This file is automatically @generated by Cargo.
22# It is not intended for manual editing.
3−version = 4
3+version = 3
44
55[[package]]
66name = "anstream"
@@ -63,12 +63,6 @@ version = "0.8.0"
6363source = "registry+https://github.com/rust-lang/crates.io-index"
6464checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
6565
66−[[package]]
67−name = "bitflags"
68−version = "2.6.0"
69−source = "registry+https://github.com/rust-lang/crates.io-index"
70−checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
71−
7266[[package]]
7367name = "bitter"
7468version = "0.7.0"
@@ -93,12 +87,6 @@ version = "3.16.0"
9387source = "registry+https://github.com/rust-lang/crates.io-index"
9488checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
9589
96−[[package]]
97−name = "bytecount"
98−version = "0.6.8"
99−source = "registry+https://github.com/rust-lang/crates.io-index"
100−checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
101−
10290[[package]]
10391name = "cc"
10492version = "1.1.31"
@@ -114,12 +102,6 @@ version = "1.0.0"
114102source = "registry+https://github.com/rust-lang/crates.io-index"
115103checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
116104
117−[[package]]
118−name = "cfg_aliases"
119−version = "0.2.1"
120−source = "registry+https://github.com/rust-lang/crates.io-index"
121−checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
122−
123105[[package]]
124106name = "clap"
125107version = "4.5.20"
@@ -148,10 +130,10 @@ version = "4.5.18"
148130source = "registry+https://github.com/rust-lang/crates.io-index"
149131checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
150132dependencies = [
151− "heck 0.5.0",
133+ "heck",
152134 "proc-macro2",
153135 "quote",
154− "syn 2.0.85",
136+ "syn",
155137]
156138
157139[[package]]
@@ -176,22 +158,6 @@ dependencies = [
176158 "windows-sys 0.48.0",
177159]
178160
179−[[package]]
180−name = "ctrlc"
181−version = "3.4.5"
182−source = "registry+https://github.com/rust-lang/crates.io-index"
183−checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
184−dependencies = [
185− "nix",
186− "windows-sys 0.59.0",
187−]
188−
189−[[package]]
190−name = "fnv"
191−version = "1.0.7"
192−source = "registry+https://github.com/rust-lang/crates.io-index"
193−checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
194−
195161[[package]]
196162name = "fplist"
197163version = "0.2.1"
@@ -204,12 +170,6 @@ version = "2.0.0"
204170source = "registry+https://github.com/rust-lang/crates.io-index"
205171checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
206172
207−[[package]]
208−name = "heck"
209−version = "0.4.1"
210−source = "registry+https://github.com/rust-lang/crates.io-index"
211−checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
212−
213173[[package]]
214174name = "heck"
215175version = "0.5.0"
@@ -255,18 +215,6 @@ version = "0.5.2"
255215source = "registry+https://github.com/rust-lang/crates.io-index"
256216checksum = "9252111cf132ba0929b6f8e030cac2a24b507f3a4d6db6fb2896f27b354c714b"
257217
258−[[package]]
259−name = "nix"
260−version = "0.29.0"
261−source = "registry+https://github.com/rust-lang/crates.io-index"
262−checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
263−dependencies = [
264− "bitflags",
265− "cfg-if",
266− "cfg_aliases",
267− "libc",
268−]
269−
270218[[package]]
271219name = "nock"
272220version = "0.1.0"
@@ -275,16 +223,13 @@ dependencies = [
275223 "bitter",
276224 "bitvec",
277225 "clap",
278− "ctrlc",
279226 "fplist",
280− "lazy_static",
281227 "murmur3",
282228 "num-bigint",
283229 "num-integer",
284230 "num-traits",
285231 "spinoff",
286232 "stacker",
287− "tabled",
288233 "web-sys",
289234 "xxhash-rust",
290235]
@@ -323,47 +268,12 @@ version = "1.20.2"
323268source = "registry+https://github.com/rust-lang/crates.io-index"
324269checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
325270
326−[[package]]
327−name = "papergrid"
328−version = "0.12.0"
329−source = "registry+https://github.com/rust-lang/crates.io-index"
330−checksum = "c7419ad52a7de9b60d33e11085a0fe3df1fbd5926aa3f93d3dd53afbc9e86725"
331−dependencies = [
332− "bytecount",
333− "fnv",
334− "unicode-width",
335−]
336−
337271[[package]]
338272name = "paste"
339273version = "1.0.15"
340274source = "registry+https://github.com/rust-lang/crates.io-index"
341275checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
342276
343−[[package]]
344−name = "proc-macro-error"
345−version = "1.0.4"
346−source = "registry+https://github.com/rust-lang/crates.io-index"
347−checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
348−dependencies = [
349− "proc-macro-error-attr",
350− "proc-macro2",
351− "quote",
352− "syn 1.0.109",
353− "version_check",
354−]
355−
356−[[package]]
357−name = "proc-macro-error-attr"
358−version = "1.0.4"
359−source = "registry+https://github.com/rust-lang/crates.io-index"
360−checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
361−dependencies = [
362− "proc-macro2",
363− "quote",
364− "version_check",
365−]
366−
367277[[package]]
368278name = "proc-macro2"
369279version = "1.0.89"
@@ -433,17 +343,6 @@ version = "0.11.1"
433343source = "registry+https://github.com/rust-lang/crates.io-index"
434344checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
435345
436−[[package]]
437−name = "syn"
438−version = "1.0.109"
439−source = "registry+https://github.com/rust-lang/crates.io-index"
440−checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
441−dependencies = [
442− "proc-macro2",
443− "quote",
444− "unicode-ident",
445−]
446−
447346[[package]]
448347name = "syn"
449348version = "2.0.85"
@@ -455,29 +354,6 @@ dependencies = [
455354 "unicode-ident",
456355]
457356
458−[[package]]
459−name = "tabled"
460−version = "0.16.0"
461−source = "registry+https://github.com/rust-lang/crates.io-index"
462−checksum = "77c9303ee60b9bedf722012ea29ae3711ba13a67c9b9ae28993838b63057cb1b"
463−dependencies = [
464− "papergrid",
465− "tabled_derive",
466−]
467−
468−[[package]]
469−name = "tabled_derive"
470−version = "0.8.0"
471−source = "registry+https://github.com/rust-lang/crates.io-index"
472−checksum = "bf0fb8bfdc709786c154e24a66777493fb63ae97e3036d914c8666774c477069"
473−dependencies = [
474− "heck 0.4.1",
475− "proc-macro-error",
476− "proc-macro2",
477− "quote",
478− "syn 1.0.109",
479−]
480−
481357[[package]]
482358name = "tap"
483359version = "1.0.1"
@@ -490,24 +366,12 @@ version = "1.0.13"
490366source = "registry+https://github.com/rust-lang/crates.io-index"
491367checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
492368
493−[[package]]
494−name = "unicode-width"
495−version = "0.1.11"
496−source = "registry+https://github.com/rust-lang/crates.io-index"
497−checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
498−
499369[[package]]
500370name = "utf8parse"
501371version = "0.2.2"
502372source = "registry+https://github.com/rust-lang/crates.io-index"
503373checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
504374
505−[[package]]
506−name = "version_check"
507−version = "0.9.5"
508−source = "registry+https://github.com/rust-lang/crates.io-index"
509−checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
510−
511375[[package]]
512376name = "wasm-bindgen"
513377version = "0.2.95"
@@ -530,7 +394,7 @@ dependencies = [
530394 "once_cell",
531395 "proc-macro2",
532396 "quote",
533− "syn 2.0.85",
397+ "syn",
534398 "wasm-bindgen-shared",
535399]
536400
@@ -552,7 +416,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
552416dependencies = [
553417 "proc-macro2",
554418 "quote",
555− "syn 2.0.85",
419+ "syn",
556420 "wasm-bindgen-backend",
557421 "wasm-bindgen-shared",
558422]
nock/Cargo.toml−7
@@ -17,9 +17,6 @@ stacker = "0.1.17"
1717murmur3 = "0.5.2"
1818web-sys = "0.3.70"
1919fplist = "0.2.1"
20−ctrlc = "3.4.5"
21−lazy_static = "1.5.0"
22−tabled = "0.16.0"
2320
2421[dependencies.xxhash-rust]
2522version = "0.8.5"
@@ -29,9 +26,6 @@ features = ["xxh3"]
2926# Tell `rustc` to optimize for small code size.
3027opt-level = 3
3128
32−[features]
33−count-jets = []
34−
3529[[bin]]
3630name = "nock"
3731path = "src/main.rs"
@@ -39,4 +33,3 @@ path = "src/main.rs"
3933[[bin]]
4034name = "hash-gate"
4135path = "src/hash-gate.rs"
42−
nock/bad.hoondeleted−1
@@ -1 +0,0 @@
1−=/ foo [1 2] +(foo)
nock/hello.hoon+1−2
@@ -1,6 +1,5 @@
11|= name=@t
2−~& name
32?+ name (crip (weld "Hello, " (trip name)))
43 'Curtis' 'Hello, daddy...'
5− %'' 'Hello, world!'
4+ '' 'Hello, world!'
65==
nock/hello.nock

Binary file not shown.

nock/src/interpreter.rs+54−228
@@ -1,24 +1,14 @@
1−use core::str;
21use std::borrow::Borrow;
32use std::collections::BTreeMap;
4−use std::collections::HashMap;
53use std::rc::Rc;
6−use std::sync::Mutex;
74
85use fplist::cons;
96use fplist::PersistentList;
10−use lazy_static::lazy_static;
117use num_bigint::BigUint;
128
139use crate::jets::*;
1410use crate::noun::*;
1511
16−// #[cfg(feature = "count-jets")]
17−lazy_static! {
18− pub static ref JET_COUNTER: Mutex<HashMap<Atom, u32>> = Mutex::new(HashMap::new());
19−}
20−
21−#[derive(Clone)]
2212pub struct Nouns {
2313 pub y: Rc<Noun>,
2414 pub n: Rc<Noun>,
@@ -31,11 +21,8 @@ pub struct Nouns {
3121 pub two_three: Rc<Noun>,
3222 pub memo: Rc<Noun>,
3323 pub mean: Rc<Noun>,
34− pub slog: Rc<Noun>,
35− pub fast: Rc<Noun>,
3624}
3725
38−#[derive(Clone)]
3926pub struct BigUints {
4027 pub zero: BigUint,
4128 pub one: BigUint,
@@ -44,14 +31,12 @@ pub struct BigUints {
4431 pub four: BigUint,
4532}
4633
47−#[derive(Clone)]
4834pub struct InterpreterContext {
4935 pub jets: Jets,
5036 pub double_jets: DoubleJets,
5137 pub nouns: Nouns,
5238 pub big_uints: BigUints,
5339 pub memo: BTreeMap<Hash, Rc<Noun>>,
54− pub slog: fn(&String) -> (),
5540}
5641
5742pub fn generate_interpreter_context() -> InterpreterContext {
@@ -73,8 +58,6 @@ pub fn generate_interpreter_context() -> InterpreterContext {
7358 let two_three = cell(&two, &three);
7459 let memo = Rc::new(Noun::from_bytes(b"memo"));
7560 let mean = Rc::new(Noun::from_bytes(b"mean"));
76− let slog = Rc::new(Noun::from_bytes(b"slog"));
77− let fast = Rc::new(Noun::from_bytes(b"fast"));
7861 InterpreterContext {
7962 jets,
8063 double_jets,
@@ -90,12 +73,9 @@ pub fn generate_interpreter_context() -> InterpreterContext {
9073 two_three,
9174 memo,
9275 mean,
93− slog,
94− fast,
9576 },
9677 big_uints,
9778 memo: BTreeMap::new(),
98− slog: |str: &String| eprintln!("{}", str),
9979 }
10080}
10181
@@ -106,7 +86,7 @@ fn wut<'a>(ctx: &'a InterpreterContext, noun: &Noun) -> &'a Rc<Noun> {
10686 }
10787}
10888
109−fn lus(noun: &Noun) -> Result<Noun, TTanks> {
89+fn lus(noun: &Noun) -> Result<Noun, Tanks> {
11090 match noun {
11191 Noun::Cell { .. } => Err(PersistentList::new()),
11292 Noun::Atom(atm) => Ok(Noun::Atom(atm + 1u32)),
@@ -121,7 +101,7 @@ fn tis<'a>(ctx: &'a InterpreterContext, lhs: &Noun, rhs: &Noun) -> &'a Rc<Noun>
121101 }
122102}
123103
124−fn fas(ctx: &InterpreterContext, addr: &Atom, noun: &Noun) -> Result<Rc<Noun>, TTanks> {
104+fn fas(ctx: &InterpreterContext, addr: &Atom, noun: &Noun) -> Result<Rc<Noun>, Tanks> {
125105 let mut addr_iter = addr.iter_u32_digits();
126106
127107 match (addr_iter.next(), addr_iter.next()) {
@@ -141,7 +121,7 @@ fn fas(ctx: &InterpreterContext, addr: &Atom, noun: &Noun) -> Result<Rc<Noun>, T
141121 }
142122}
143123
144−fn fas_u32(addr: u32, noun: &Noun) -> Result<Rc<Noun>, TTanks> {
124+fn fas_u32(addr: u32, noun: &Noun) -> Result<Rc<Noun>, Tanks> {
145125 match addr {
146126 0 => Err(PersistentList::new()),
147127 1 => Ok(Rc::new(noun.clone())),
@@ -159,7 +139,7 @@ fn fas_u32(addr: u32, noun: &Noun) -> Result<Rc<Noun>, TTanks> {
159139 }
160140}
161141
162−fn hax_u32(addr: u32, new_value: &Rc<Noun>, target: &Rc<Noun>) -> Result<Rc<Noun>, TTanks> {
142+fn hax_u32(addr: u32, new_value: &Rc<Noun>, target: &Rc<Noun>) -> Result<Rc<Noun>, Tanks> {
163143 match addr {
164144 0 => Err(PersistentList::new()),
165145 1 => Ok(new_value.clone()),
@@ -181,11 +161,11 @@ fn hax(
181161 addr: &Atom,
182162 new_value: &Rc<Noun>,
183163 target: &Rc<Noun>,
184−) -> Result<Rc<Noun>, TTanks> {
164+) -> Result<Rc<Noun>, Tanks> {
185165 let mut addr_iter = addr.iter_u32_digits();
186166
187167 match (addr_iter.next(), addr_iter.next()) {
188− (None, _) => Err(TTanks::new()),
168+ (None, _) => Err(Tanks::new()),
189169 (Some(x), None) => hax_u32(x, new_value, target),
190170 _ => hax(
191171 ctx,
@@ -204,18 +184,18 @@ fn hax(
204184
205185fn tar_u32<'a>(
206186 ctx: &mut InterpreterContext,
207− subj: &'a Rc<Noun>,
187+ subj: Rc<Noun>,
208188 op: u32,
209189 formula: &'a Rc<Noun>,
210−) -> Result<Rc<Noun>, TTanks> {
190+) -> Result<Rc<Noun>, Tanks> {
211191 match op {
212192 0 => {
213− let b = formula.as_atom().ok_or(TTanks::new())?;
193+ let b = formula.as_atom().ok_or(Tanks::new())?;
214194 fas(ctx, b, &*subj)
215195 }
216196 1 => Ok(formula.clone()),
217197 2 => {
218− let (b, c) = formula.as_cell().ok_or(TTanks::new())?;
198+ let (b, c) = formula.as_cell().ok_or(Tanks::new())?;
219199
220200 match c.borrow() {
221201 Noun::Cell { p, q, .. }
@@ -227,16 +207,16 @@ fn tar_u32<'a>(
227207 match ctx.jets.get(&hash) {
228208 Some(f) => f(ctx, sample),
229209 None => {
230− let foo = tar(ctx, subj, &b)?;
210+ let foo = tar(ctx, subj.clone(), &b)?;
231211 let bar = tar(ctx, subj, &c)?;
232− tar(ctx, &foo, &bar)
212+ tar(ctx, foo, &bar)
233213 }
234214 }
235215 }
236216 _ => {
237− let foo = tar(ctx, subj, &b)?;
217+ let foo = tar(ctx, subj.clone(), &b)?;
238218 let bar = tar(ctx, subj, &c)?;
239− tar(ctx, &foo, &bar)
219+ tar(ctx, foo, &bar)
240220 }
241221 }
242222 }
@@ -246,57 +226,57 @@ fn tar_u32<'a>(
246226 }
247227 4 => Ok(Rc::new(lus(&*tar(ctx, subj, &formula)?)?)),
248228 5 => {
249− let (b, c) = formula.as_cell().ok_or(TTanks::new())?;
250− let foo = tar(ctx, subj, &b)?;
229+ let (b, c) = formula.as_cell().ok_or(Tanks::new())?;
230+ let foo = tar(ctx, subj.clone(), &b)?;
251231 let bar = tar(ctx, subj, &c)?;
252232 Ok(tis(ctx, &foo, &bar).clone())
253233 }
254234 6 => {
255− let (b, c) = formula.as_cell().ok_or(TTanks::new())?;
256− let (c, d) = c.as_cell().ok_or(TTanks::new())?;
235+ let (b, c) = formula.as_cell().ok_or(Tanks::new())?;
236+ let (c, d) = c.as_cell().ok_or(Tanks::new())?;
257237
258− let bar = tar_u32(ctx, subj, 4, &cell(&ctx.nouns.four.clone(), &b))?;
238+ let bar = tar_u32(ctx, subj.clone(), 4, &cell(&ctx.nouns.four.clone(), &b))?;
259239
260− let foo = tar_u32(ctx, &ctx.nouns.two_three.clone(), 0, &bar)?;
261− let foo = tar_u32(ctx, &cell(&c, &d), 0, &foo)?;
240+ let foo = tar_u32(ctx, ctx.nouns.two_three.clone(), 0, &bar)?;
241+ let foo = tar_u32(ctx, cell(&c, &d), 0, &foo)?;
262242
263243 tar(ctx, subj, &foo)
264244 }
265245 7 => {
266− let (b, c) = formula.as_cell().ok_or(TTanks::new())?;
246+ let (b, c) = formula.as_cell().ok_or(Tanks::new())?;
267247 let foo = tar(ctx, subj, &b)?;
268− tar(ctx, &foo, &c)
248+ tar(ctx, foo, &c)
269249 }
270250 8 => {
271− let (b, c) = formula.as_cell().ok_or(TTanks::new())?;
272− let foo = tar(ctx, subj, &b)?;
273− tar(ctx, &cell(&foo, &subj), &c)
251+ let (b, c) = formula.as_cell().ok_or(Tanks::new())?;
252+ let foo = tar(ctx, subj.clone(), &b)?;
253+ tar(ctx, cell(&foo, &subj), &c)
274254 }
275255 9 => {
276− let (b, c) = formula.as_cell().ok_or(TTanks::new())?;
256+ let (b, c) = formula.as_cell().ok_or(Tanks::new())?;
277257 let foo = tar(ctx, subj, &c)?;
278258 tar_u32(
279259 ctx,
280− &foo,
260+ foo,
281261 2,
282262 &cell(&ctx.nouns.sig_one, &cell(&ctx.nouns.sig, &b)),
283263 )
284264 }
285265 10 => {
286− let (b, d) = formula.as_cell().ok_or(TTanks::new())?;
287− let (b, c) = b.as_cell().ok_or(TTanks::new())?;
288− let b = b.as_atom().ok_or(TTanks::new())?;
266+ let (b, d) = formula.as_cell().ok_or(Tanks::new())?;
267+ let (b, c) = b.as_cell().ok_or(Tanks::new())?;
268+ let b = b.as_atom().ok_or(Tanks::new())?;
289269
290− let foo = tar(ctx, subj, &c)?;
270+ let foo = tar(ctx, subj.clone(), &c)?;
291271 let bar = tar(ctx, subj, &d)?;
292272 hax(ctx, b, &foo, &bar)
293273 }
294274 11 => {
295− let (b, d) = formula.as_cell().ok_or(TTanks::new())?;
275+ let (b, d) = formula.as_cell().ok_or(Tanks::new())?;
296276 match b.borrow() {
297277 Noun::Atom(_) => tar(ctx, subj, &d),
298278 Noun::Cell { p, q, .. } => {
299− let eval = |ctx: &mut InterpreterContext| tar(ctx, subj, &d);
279+ let eval = |ctx: &mut InterpreterContext| tar(ctx, subj.clone(), &d);
300280 if *p == ctx.nouns.memo {
301281 let hash = cell(&subj, &d).hash();
302282 match ctx.memo.get(&hash) {
@@ -309,188 +289,37 @@ fn tar_u32<'a>(
309289 }
310290 } else if *p == ctx.nouns.mean {
311291 with_trace((subj.clone(), q.clone()), eval(ctx))
312− } else if *p == ctx.nouns.slog {
313− let noun = tar(ctx, subj, &q)?;
314− let (_prio, tank) = noun.as_cell().ok_or(TTanks::new())?;
315−
316− (ctx.slog)(&ram(ctx, tank.clone()).unwrap_or(String::new()));
317− eval(ctx)
318− } else if *p == ctx.nouns.fast {
319− if cfg!(feature = "count-jets") {
320− let tag = q.as_cell().unwrap().1.as_cell().unwrap().0.as_atom();
321− match tag {
322− None => (),
323− Some(tag) => {
324− let mut counter = JET_COUNTER.lock().unwrap();
325− match counter.get(tag) {
326− None => {
327− counter.insert(tag.clone(), 1);
328− ()
329− }
330− Some(v) => {
331− let v = *v;
332− counter.insert(tag.clone(), v + 1);
333− ()
334− }
335− }
336− }
337− }
338− }
339− eval(ctx)
340292 } else {
341293 eval(ctx)
342294 }
343295 }
344296 }
345297 }
346− _ => Err(TTanks::new()),
298+ _ => Err(Tanks::new()),
347299 }
348300}
349301
350−fn ram(ctx: &mut InterpreterContext, tank: Rc<Noun>) -> Option<String> {
351− match tank.borrow() {
352− Noun::Atom(str) => {
353− let mut bytes = str.to_bytes_le();
354− Some(String::from(str::from_utf8_mut(&mut bytes).ok()?))
355− }
356− Noun::Cell { p, q, .. } => {
357− let tag = p.as_atom()?.to_bytes_le();
358− if tag == b"leaf" {
359− let mut bytes = crip(ctx, q.clone()).ok()?.as_bytes()?;
360− Some(String::from(str::from_utf8_mut(&mut bytes).ok()?))
361− } else if tag == b"palm" {
362− let (spacers, tanks) = q.as_cell()?;
363− let (mid, open, flat_open, close) = spacers.tuple_4()?;
364−
365− let mut mid = crip(ctx, mid.clone()).ok()?.as_bytes()?;
366− let mid = String::from(str::from_utf8_mut(&mut mid).ok()?);
367−
368− let mut open = crip(ctx, open.clone()).ok()?.as_bytes()?;
369− let open = String::from(str::from_utf8_mut(&mut open).ok()?);
370−
371− let mut flat_open = crip(ctx, flat_open.clone()).ok()?.as_bytes()?;
372− let flat_open = String::from(str::from_utf8_mut(&mut flat_open).ok()?);
373−
374− let mut close = crip(ctx, close.clone()).ok()?.as_bytes()?;
375− let close = String::from(str::from_utf8_mut(&mut close).ok()?);
376−
377− let mut target = String::new();
378−
379− target.push_str(&*open);
380− target.push_str(&*flat_open);
381−
382− let mut is_first = true;
383− for tank in tanks.list_iter() {
384− if !is_first {
385− target.push_str(&*mid);
386− }
387−
388− target.push_str(&*(ram(ctx, tank.clone())?));
389−
390− is_first = false;
391− }
392−
393− target.push_str(&*close);
394−
395− Some(target)
396− } else if tag == b"rose" {
397− let (spacers, tanks) = q.as_cell()?;
398− let (mid, open, close) = spacers.tuple_3()?;
399−
400− let mut mid = crip(ctx, mid.clone()).ok()?.as_bytes()?;
401− let mid = String::from(str::from_utf8_mut(&mut mid).ok()?);
402−
403− let mut open = crip(ctx, open.clone()).ok()?.as_bytes()?;
404− let open = String::from(str::from_utf8_mut(&mut open).ok()?);
405−
406− let mut close = crip(ctx, close.clone()).ok()?.as_bytes()?;
407− let close = String::from(str::from_utf8_mut(&mut close).ok()?);
408−
409− let mut target = String::new();
410−
411− target.push_str(&*open);
412−
413− let mut is_first = true;
414− for tank in tanks.list_iter() {
415− if !is_first {
416− target.push_str(&*mid);
417− }
418−
419− target.push_str(&*(ram(ctx, tank.clone())?));
420−
421− is_first = false;
422− }
423−
424− target.push_str(&*close);
425−
426− Some(target)
427− } else {
428− None
429− }
430− }
431− }
432−}
433−
434−pub fn ram_ttanks(ctx: &mut InterpreterContext, tanks: TTanks) -> String {
435− let mut target = String::new();
436−
437− let mut first = true;
438− for tank in tanks.iter().collect::<Vec<_>>().iter().cloned().rev() {
439− if !first {
440− target.push('\n');
441− }
442−
443− target.push_str(&*ram_ttank(ctx, tank.clone()));
444−
445− first = false;
446− }
447−
448− target
449−}
450−
451−pub fn ram_ttank(ctx: &mut InterpreterContext, ttank: TTank) -> String {
452− let (subj, q) = ttank;
453−
454− let tank = match tar(ctx, &subj, &q) {
455− Ok(gate) => match eval_pulled_gate(ctx, gate) {
456− Ok(tank) => tank,
457− Err(_) => {
458− return String::new();
459− }
460− },
461− Err(_) => {
462− return String::new();
463− }
464− };
465− ram(ctx, tank).unwrap_or(String::new())
466−}
467−
468−pub fn crip(ctx: &mut InterpreterContext, tape: Rc<Noun>) -> Result<Rc<Noun>, TTanks> {
469− RAP(ctx, &cell(&Rc::new(Noun::from_u32(3)), &tape))
470−}
471−
472−fn with_trace<T>(tank: (Rc<Noun>, Rc<Noun>), src: Result<T, TTanks>) -> Result<T, TTanks> {
302+fn with_trace<T>(tank: (Rc<Noun>, Rc<Noun>), src: Result<T, Tanks>) -> Result<T, Tanks> {
473303 match src {
474304 Ok(a) => Ok(a),
475305 Err(tanks) => Err(cons(tank, tanks)),
476306 }
477307}
478308
479−/// Not actually a tank, but rather a thunk (subject, formula) which calculates the tank.
480−pub type TTank = (Rc<Noun>, Rc<Noun>);
481−pub type TTanks = PersistentList<TTank>;
309+pub type Tank = Rc<Noun>;
310+pub type Tanks = PersistentList<(Rc<Noun>, Rc<Noun>)>;
482311
483−pub const EMPTY_TANKS: TTanks = TTanks::new();
312+pub const EMPTY_TANKS: Tanks = Tanks::new();
484313
485314pub fn tar<'a>(
486315 ctx: &mut InterpreterContext,
487− subj: &'a Rc<Noun>,
316+ subj: Rc<Noun>,
488317 formula: &'a Noun,
489−) -> Result<Rc<Noun>, TTanks> {
490− let (op, formula) = formula.as_cell().ok_or(TTanks::new())?;
318+) -> Result<Rc<Noun>, Tanks> {
319+ let (op, formula) = formula.as_cell().ok_or(Tanks::new())?;
491320 match op.borrow() {
492321 Noun::Cell { .. } => {
493− let foo = tar(ctx, subj, &op)?;
322+ let foo = tar(ctx, subj.clone(), &op)?;
494323 let bar = tar(ctx, subj, &formula)?;
495324 Ok(cell(&foo, &bar))
496325 }
@@ -501,17 +330,14 @@ pub fn tar<'a>(
501330 1 => Some(op_iter.next().unwrap()),
502331 _ => None,
503332 };
504− let op = op.ok_or(TTanks::new())?;
333+ let op = op.ok_or(Tanks::new())?;
505334 stacker::maybe_grow(32 * 1024, 1024 * 1024, || tar_u32(ctx, subj, op, formula))
506335 }
507336 }
508337}
509338
510−pub fn eval_gate(
511− ctx: &mut InterpreterContext,
512− gate_factory: &Rc<Noun>,
513−) -> Result<Rc<Noun>, TTanks> {
514− let pulled_gate = tar(ctx, &ctx.nouns.sig.clone(), &gate_factory)?;
339+pub fn eval_gate(ctx: &mut InterpreterContext, gate_factory: &Rc<Noun>) -> Result<Rc<Noun>, Tanks> {
340+ let pulled_gate = tar(ctx, ctx.nouns.sig.clone(), &gate_factory)?;
515341 eval_pulled_gate(ctx, pulled_gate)
516342}
517343
@@ -519,18 +345,18 @@ pub fn slam(
519345 ctx: &mut InterpreterContext,
520346 gate_factory: &Rc<Noun>,
521347 sample: &Rc<Noun>,
522−) -> Result<Rc<Noun>, TTanks> {
523− let gate = tar(ctx, &ctx.nouns.sig.clone(), &gate_factory)?;
348+) -> Result<Rc<Noun>, Tanks> {
349+ let gate = tar(ctx, ctx.nouns.sig.clone(), &gate_factory)?;
524350
525351 eval_pulled_gate(ctx, replace_sample(&gate, sample)?)
526352}
527353
528354/// This is useful for evaluaing gates produced by running `.foo/nock gate-name` in the dojo.
529355/// Also for calling gates from within jets.
530−pub fn eval_pulled_gate(ctx: &mut InterpreterContext, gate: Rc<Noun>) -> Result<Rc<Noun>, TTanks> {
356+pub fn eval_pulled_gate(ctx: &mut InterpreterContext, gate: Rc<Noun>) -> Result<Rc<Noun>, Tanks> {
531357 tar(
532358 ctx,
533− &gate,
359+ gate,
534360 &cell(
535361 &Rc::new(Noun::Atom(BigUint::new(vec![9]))),
536362 &cell(&ctx.nouns.two, &cell(&ctx.nouns.sig, &ctx.nouns.one)),
@@ -544,13 +370,13 @@ pub fn slam_pulled_gate(
544370 ctx: &mut InterpreterContext,
545371 gate: &Rc<Noun>,
546372 sample: &Rc<Noun>,
547−) -> Result<Rc<Noun>, TTanks> {
373+) -> Result<Rc<Noun>, Tanks> {
548374 eval_pulled_gate(ctx, replace_sample(gate, sample)?)
549375}
550376
551−pub fn replace_sample(gate: &Rc<Noun>, sample: &Rc<Noun>) -> Result<Rc<Noun>, TTanks> {
552− let (battery, payload) = gate.as_cell().ok_or(TTanks::new())?;
553− let (_sample, context) = payload.as_cell().ok_or(TTanks::new())?;
377+pub fn replace_sample(gate: &Rc<Noun>, sample: &Rc<Noun>) -> Result<Rc<Noun>, Tanks> {
378+ let (battery, payload) = gate.as_cell().ok_or(Tanks::new())?;
379+ let (_sample, context) = payload.as_cell().ok_or(Tanks::new())?;
554380
555381 Ok(cell(battery, &cell(sample, context)))
556382}
nock/src/jets.rs+129−304
@@ -2,15 +2,14 @@ use core::panic;
22use murmur3::murmur3_32;
33use num_integer::Integer;
44use std::{
5− borrow::Borrow,
65 collections::BTreeMap,
76 iter::{once, repeat_n, zip},
87 rc::Rc,
98};
109
1110use crate::{
12− interpreter::{slam_pulled_gate, InterpreterContext, TTanks, EMPTY_TANKS},
13− noun::{self, cell, AsNoun, Atom, Edge, Hair, Nail, Noun},
11+ interpreter::{slam_pulled_gate, InterpreterContext, Tanks},
12+ noun::{self, cell, Atom, Edge, Hair, Nail, Noun},
1413};
1514
1615fn traverse_iter_result<E: Clone, T: Clone, I: Iterator<Item = Result<T, E>>>(
@@ -108,23 +107,18 @@ pub fn generate_jets() -> Jets {
108107 (124976064672625230427889101290245200507, TRIP),
109108 (4078279288073906183633157465551067635, MUK),
110109 (261900159748071234702685239353558471892, MUG),
111− (198191257620924788695943358402651905954, AOR),
112− (299422828284010240628877806738195667652, DOR),
113− (166326796552601297695313176538120818632, GOR),
114− (7942398240152905363972500667649380756, MOR),
115− (315687984998079865981003916817293521765, LOOK),
116110 ])
117111}
118112
119−pub type Jet = fn(ctx: &mut InterpreterContext, &Rc<Noun>) -> Result<Rc<Noun>, TTanks>;
113+pub type Jet = fn(ctx: &mut InterpreterContext, &Rc<Noun>) -> Result<Rc<Noun>, Tanks>;
120114pub type DoubleJet = fn(ctx: &mut InterpreterContext, &Rc<Noun>, &Rc<Noun>) -> Option<Rc<Noun>>;
121115
122−static BINARY_ATOM: fn(&Rc<Noun>, fn(&Atom, &Atom) -> Atom) -> Result<Rc<Noun>, TTanks> =
116+static BINARY_ATOM: fn(&Rc<Noun>, fn(&Atom, &Atom) -> Atom) -> Result<Rc<Noun>, Tanks> =
123117 |n: &Rc<Noun>, f: fn(&Atom, &Atom) -> Atom| {
124− let (p, q) = n.as_cell().ok_or(TTanks::new())?;
118+ let (p, q) = n.as_cell().ok_or(Tanks::new())?;
125119 Ok(Rc::new(Noun::Atom(f(
126− p.as_atom().ok_or(TTanks::new())?,
127− q.as_atom().ok_or(TTanks::new())?,
120+ p.as_atom().ok_or(Tanks::new())?,
121+ q.as_atom().ok_or(Tanks::new())?,
128122 ))))
129123 };
130124
@@ -132,13 +126,13 @@ static BINARY_ATOM_LOOB: fn(
132126 &InterpreterContext,
133127 &Rc<Noun>,
134128 fn(&Atom, &Atom) -> bool,
135−) -> Result<Rc<Noun>, TTanks> =
129+) -> Result<Rc<Noun>, Tanks> =
136130 |ctx: &InterpreterContext, n: &Rc<Noun>, f: fn(&Atom, &Atom) -> bool| {
137− let (p, q) = n.as_cell().ok_or(TTanks::new())?;
131+ let (p, q) = n.as_cell().ok_or(Tanks::new())?;
138132
139133 if f(
140− p.as_atom().ok_or(TTanks::new())?,
141− q.as_atom().ok_or(TTanks::new())?,
134+ p.as_atom().ok_or(Tanks::new())?,
135+ q.as_atom().ok_or(Tanks::new())?,
142136 ) {
143137 Ok(ctx.nouns.sig.clone())
144138 } else {
@@ -149,22 +143,18 @@ static BINARY_ATOM_LOOB: fn(
149143static BINARY_ATOM_PAIR: fn(
150144 &Rc<Noun>,
151145 fn(&Atom, &Atom) -> (Atom, Atom),
152−) -> Result<Rc<Noun>, TTanks> = |n: &Rc<Noun>, f: fn(&Atom, &Atom) -> (Atom, Atom)| {
153− let (p, q) = n.as_cell().ok_or(TTanks::new())?;
146+) -> Result<Rc<Noun>, Tanks> = |n: &Rc<Noun>, f: fn(&Atom, &Atom) -> (Atom, Atom)| {
147+ let (p, q) = n.as_cell().ok_or(Tanks::new())?;
154148
155149 let (a, b) = f(
156− p.as_atom().ok_or(TTanks::new())?,
157− q.as_atom().ok_or(TTanks::new())?,
150+ p.as_atom().ok_or(Tanks::new())?,
151+ q.as_atom().ok_or(Tanks::new())?,
158152 );
159153 Ok(cell(&Rc::new(Noun::Atom(a)), &Rc::new(Noun::Atom(b))))
160154};
161155
162156static ADD: Jet = |_ctx, n| BINARY_ATOM(n, |a, b| a + b);
163−static DEC: Jet = |_ctx, n| {
164− Ok(Rc::new(Noun::Atom(
165− n.as_atom().ok_or(TTanks::new())? - 1u32,
166− )))
167−};
157+static DEC: Jet = |_ctx, n| Ok(Rc::new(Noun::Atom(n.as_atom().ok_or(Tanks::new())? - 1u32)));
168158static DVR: Jet = |_ctx, n| BINARY_ATOM_PAIR(n, |a, b| a.div_rem(&b));
169159static DIV: Jet = |_ctx, n| BINARY_ATOM(n, |a, b| a / b);
170160static GTE: Jet = |ctx, n| BINARY_ATOM_LOOB(ctx, n, |a, b| a >= b);
@@ -178,30 +168,30 @@ static MUL: Jet = |_ctx, n| BINARY_ATOM(n, |a, b| a * b);
178168static SUB: Jet = |_ctx, n| BINARY_ATOM(n, |a, b| a - b);
179169
180170static CAP: Jet = |ctx, n| {
181− let n = n.as_atom().ok_or(TTanks::new())?;
171+ let n = n.as_atom().ok_or(Tanks::new())?;
182172 CAP_ATOM(ctx, n.clone())
183173};
184174
185−static CAP_ATOM: fn(&InterpreterContext, Atom) -> Result<Rc<Noun>, TTanks> = |ctx, n: Atom| {
175+static CAP_ATOM: fn(&InterpreterContext, Atom) -> Result<Rc<Noun>, Tanks> = |ctx, n: Atom| {
186176 let mut n_iter = n.iter_u32_digits();
187177
188178 match n_iter.len() {
189− 0 => Err(TTanks::new()),
179+ 0 => Err(Tanks::new()),
190180 1 => CAP_U32(ctx, n_iter.next().unwrap()),
191181 _ => CAP_ATOM(ctx, n >> 1),
192182 }
193183};
194−static CAP_U32: fn(ctx: &InterpreterContext, u32) -> Result<Rc<Noun>, TTanks> = |ctx, n| match n {
195− 0 | 1 => Err(TTanks::new()),
184+static CAP_U32: fn(ctx: &InterpreterContext, u32) -> Result<Rc<Noun>, Tanks> = |ctx, n| match n {
185+ 0 | 1 => Err(Tanks::new()),
196186 2 => Ok(ctx.nouns.two.clone()),
197187 3 => Ok(ctx.nouns.three.clone()),
198188 n => CAP_U32(ctx, n >> 1),
199189};
200190
201191static MAS: Jet = |ctx, n| {
202− let n = n.as_atom().ok_or(TTanks::new())?;
192+ let n = n.as_atom().ok_or(Tanks::new())?;
203193 Ok(Rc::new(Noun::Atom(
204− MAS_ATOM(ctx, n.clone()).ok_or(TTanks::new())?,
194+ MAS_ATOM(ctx, n.clone()).ok_or(Tanks::new())?,
205195 )))
206196};
207197static MAS_ATOM: fn(&InterpreterContext, Atom) -> Option<Atom> = |ctx, n| {
@@ -223,22 +213,22 @@ static MAS_U32: fn(u32) -> u32 = |n| match n {
223213};
224214
225215static PEG: Jet = |ctx, n| {
226− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
216+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
227217
228− let a = a.as_atom().ok_or(TTanks::new())?;
229− let b = b.as_atom().ok_or(TTanks::new())?;
218+ let a = a.as_atom().ok_or(Tanks::new())?;
219+ let b = b.as_atom().ok_or(Tanks::new())?;
230220
231221 if *a == Atom::ZERO {
232− return Err(TTanks::new());
222+ return Err(Tanks::new());
233223 };
234224
235225 Ok(Rc::new(Noun::Atom(PEG_ATOM(ctx, a, b.clone())?)))
236226};
237−static PEG_ATOM: fn(&InterpreterContext, &Atom, Atom) -> Result<Atom, TTanks> = |ctx, a, b| {
227+static PEG_ATOM: fn(&InterpreterContext, &Atom, Atom) -> Result<Atom, Tanks> = |ctx, a, b| {
238228 let mut b_iter = b.iter_u32_digits();
239229
240230 match b_iter.len() {
241− 0 => Err(TTanks::new()),
231+ 0 => Err(Tanks::new()),
242232 1 => Ok(PEG_U32(a, b_iter.next().unwrap())),
243233 _ => Ok((&b & &ctx.big_uints.one) + (PEG_ATOM(ctx, a, b >> 1)? << 1)),
244234 }
@@ -252,7 +242,7 @@ static PEG_U32: fn(&Atom, u32) -> Atom = |a, b| match b {
252242};
253243
254244static FAND: Jet = |ctx, n| {
255− let (nedl, hstk) = n.as_cell().ok_or(TTanks::new())?;
245+ let (nedl, hstk) = n.as_cell().ok_or(Tanks::new())?;
256246 if nedl.is_sig() || hstk.is_sig() {
257247 return Ok(ctx.nouns.sig.clone());
258248 }
@@ -282,7 +272,7 @@ static FAND: Jet = |ctx, n| {
282272};
283273
284274static FIND: Jet = |ctx, n| {
285− let (nedl, hstk) = n.as_cell().ok_or(TTanks::new())?;
275+ let (nedl, hstk) = n.as_cell().ok_or(Tanks::new())?;
286276 if nedl.is_sig() || hstk.is_sig() {
287277 return Ok(ctx.nouns.sig.clone());
288278 }
@@ -315,13 +305,13 @@ static FLOP: Jet = |ctx, n| {
315305};
316306
317307static GULF: Jet = |_ctx, n| {
318− let (from, to) = n.as_cell().ok_or(TTanks::new())?;
308+ let (from, to) = n.as_cell().ok_or(Tanks::new())?;
319309
320− let mut from = (*from.as_atom().ok_or(TTanks::new())?).clone();
321− let to = to.as_atom().ok_or(TTanks::new())?.clone();
310+ let mut from = (*from.as_atom().ok_or(Tanks::new())?).clone();
311+ let to = to.as_atom().ok_or(Tanks::new())?.clone();
322312
323313 if from > to {
324− return Err(TTanks::new());
314+ return Err(Tanks::new());
325315 }
326316
327317 let mut tmp = Vec::new();
@@ -335,10 +325,10 @@ static GULF: Jet = |_ctx, n| {
335325};
336326
337327static INTO: Jet = |_ctx, n| {
338− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
339− let (b, c) = b.as_cell().ok_or(TTanks::new())?;
328+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
329+ let (b, c) = b.as_cell().ok_or(Tanks::new())?;
340330
341− let mut b_iter = b.as_atom().ok_or(TTanks::new())?.iter_u32_digits();
331+ let mut b_iter = b.as_atom().ok_or(Tanks::new())?.iter_u32_digits();
342332
343333 match b_iter.len() {
344334 0 => Ok(cell(&c, &a)),
@@ -368,7 +358,7 @@ static INTO: Jet = |_ctx, n| {
368358};
369359
370360static JOIN: Jet = |_ctx, n| {
371− let (sep, list) = n.as_cell().ok_or(TTanks::new())?;
361+ let (sep, list) = n.as_cell().ok_or(Tanks::new())?;
372362 Ok(Noun::list_refs(
373363 list.list_iter()
374364 .intersperse(sep)
@@ -386,7 +376,7 @@ static LENT: Jet = |_ctx, n| {
386376};
387377
388378static LEVY: Jet = |ctx, n| {
389− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
379+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
390380 if traverse_iter_result(a.list_iter().map(|n| slam_pulled_gate(ctx, b, n)))?
391381 .iter()
392382 .all(|x| x.is_y())
@@ -398,7 +388,7 @@ static LEVY: Jet = |ctx, n| {
398388};
399389
400390static LIEN: Jet = |ctx, n| {
401− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
391+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
402392 if traverse_iter_result(a.list_iter().map(|n| slam_pulled_gate(ctx, b, n)))?
403393 .iter()
404394 .any(|x| x.is_y())
@@ -410,7 +400,7 @@ static LIEN: Jet = |ctx, n| {
410400};
411401
412402static MURN: Jet = |ctx, n| {
413− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
403+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
414404
415405 Ok(Noun::list_refs(
416406 traverse_iter_result(
@@ -423,7 +413,7 @@ static MURN: Jet = |ctx, n| {
423413};
424414
425415static REAP: Jet = |_ctx, n| {
426− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
416+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
427417
428418 Ok(Noun::list_refs(repeat_n(
429419 b.clone(),
@@ -431,17 +421,17 @@ static REAP: Jet = |_ctx, n| {
431421 )))
432422};
433423
434−static REAR: Jet = |_ctx, n| n.list_iter().last().ok_or(TTanks::new()).cloned();
424+static REAR: Jet = |_ctx, n| n.list_iter().last().ok_or(Tanks::new()).cloned();
435425
436426static REEL: Jet = |ctx, n| {
437− let (list, gate) = n.as_cell().ok_or(TTanks::new())?;
427+ let (list, gate) = n.as_cell().ok_or(Tanks::new())?;
438428
439429 list.list_iter().collect::<Vec<_>>().iter().rfold(
440430 Ok(gate
441431 .gate_sample()
442− .ok_or(TTanks::new())?
432+ .ok_or(Tanks::new())?
443433 .as_cell()
444− .ok_or(TTanks::new())?
434+ .ok_or(Tanks::new())?
445435 .1
446436 .clone()),
447437 |acc, next| {
@@ -452,15 +442,15 @@ static REEL: Jet = |ctx, n| {
452442};
453443
454444static ROLL: Jet = |ctx, n| {
455− let (list, gate) = n.as_cell().ok_or(TTanks::new())?;
445+ let (list, gate) = n.as_cell().ok_or(Tanks::new())?;
456446
457447 list.list_iter().fold(
458448 Ok(gate
459449 .clone()
460450 .gate_sample()
461− .ok_or(TTanks::new())?
451+ .ok_or(Tanks::new())?
462452 .as_cell()
463− .ok_or(TTanks::new())?
453+ .ok_or(Tanks::new())?
464454 .1
465455 .clone()),
466456 |acc, next| {
@@ -471,7 +461,7 @@ static ROLL: Jet = |ctx, n| {
471461};
472462
473463static SCAG: Jet = |_ctx, n| {
474− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
464+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
475465
476466 Ok(Noun::list_refs(
477467 b.list_iter()
@@ -484,7 +474,7 @@ static SCAG: Jet = |_ctx, n| {
484474};
485475
486476static SKID: Jet = |ctx, n| {
487− let (list, gate) = n.as_cell().ok_or(TTanks::new())?;
477+ let (list, gate) = n.as_cell().ok_or(Tanks::new())?;
488478
489479 let slammed_n = traverse_iter_result(
490480 list.list_iter()
@@ -499,7 +489,7 @@ static SKID: Jet = |ctx, n| {
499489};
500490
501491static SKIM: Jet = |ctx, n| {
502− let (list, gate) = n.as_cell().ok_or(TTanks::new())?;
492+ let (list, gate) = n.as_cell().ok_or(Tanks::new())?;
503493
504494 Ok(Noun::list_refs(
505495 traverse_iter_result(
@@ -513,7 +503,7 @@ static SKIM: Jet = |ctx, n| {
513503};
514504
515505static SKIP: Jet = |ctx, n| {
516− let (list, gate) = n.as_cell().ok_or(TTanks::new())?;
506+ let (list, gate) = n.as_cell().ok_or(Tanks::new())?;
517507
518508 Ok(Noun::list_refs(
519509 traverse_iter_result(
@@ -527,9 +517,9 @@ static SKIP: Jet = |ctx, n| {
527517};
528518
529519static SLAG: Jet = |ctx, n| {
530− let (a, list) = n.as_cell().ok_or(TTanks::new())?;
520+ let (a, list) = n.as_cell().ok_or(Tanks::new())?;
531521
532− let mut a_iter = a.as_atom().ok_or(TTanks::new())?.iter_u32_digits();
522+ let mut a_iter = a.as_atom().ok_or(Tanks::new())?.iter_u32_digits();
533523
534524 match a_iter.len() {
535525 0 => Ok(list.clone()),
@@ -546,12 +536,12 @@ static SLAG: Jet = |ctx, n| {
546536};
547537
548538static SNAG: Jet = |_ctx, n| {
549− let (a, list) = n.as_cell().ok_or(TTanks::new())?;
539+ let (a, list) = n.as_cell().ok_or(Tanks::new())?;
550540
551541 list.list_iter()
552542 .nth(a.as_u32().unwrap() as usize)
553543 .cloned()
554− .ok_or(TTanks::new())
544+ .ok_or(Tanks::new())
555545};
556546
557547static SNIP: Jet = |_ctx, n| {
@@ -568,7 +558,7 @@ static SNIP: Jet = |_ctx, n| {
568558};
569559
570560static SNOC: Jet = |_ctx, n| {
571− let (list, b) = n.as_cell().ok_or(TTanks::new())?;
561+ let (list, b) = n.as_cell().ok_or(Tanks::new())?;
572562
573563 Ok(Noun::list_refs(
574564 list.list_iter()
@@ -599,15 +589,15 @@ static SNOC: Jet = |_ctx, n| {
599589// };
600590
601591static SPIN: Jet = |ctx, n| {
602− let (list, b) = n.as_cell().ok_or(TTanks::new())?;
603− let (acc, gate) = b.as_cell().ok_or(TTanks::new())?;
592+ let (list, b) = n.as_cell().ok_or(Tanks::new())?;
593+ let (acc, gate) = b.as_cell().ok_or(Tanks::new())?;
604594
605595 let mut res = Vec::new();
606596 let mut acc = acc.clone();
607597
608598 for n in list.list_iter() {
609599 let foo = slam_pulled_gate(ctx, gate, &cell(&n, &acc))?;
610− let (el, new_acc) = foo.as_cell().ok_or(TTanks::new())?;
600+ let (el, new_acc) = foo.as_cell().ok_or(Tanks::new())?;
611601 acc = new_acc.clone();
612602 res.push(el.clone());
613603 }
@@ -616,20 +606,20 @@ static SPIN: Jet = |ctx, n| {
616606};
617607
618608static SPUN: Jet = |ctx, n| {
619− let (list, gate) = n.as_cell().ok_or(TTanks::new())?;
609+ let (list, gate) = n.as_cell().ok_or(Tanks::new())?;
620610
621611 let mut res = Vec::new();
622612 let mut acc = gate
623613 .gate_sample()
624− .ok_or(TTanks::new())?
614+ .ok_or(Tanks::new())?
625615 .as_cell()
626− .ok_or(TTanks::new())?
616+ .ok_or(Tanks::new())?
627617 .1
628618 .clone();
629619
630620 for n in list.list_iter() {
631621 let foo = slam_pulled_gate(ctx, gate, &cell(&n, &acc))?;
632− let (el, new_acc) = foo.as_cell().ok_or(TTanks::new())?;
622+ let (el, new_acc) = foo.as_cell().ok_or(Tanks::new())?;
633623 acc = new_acc.clone();
634624 res.push(el.clone());
635625 }
@@ -638,7 +628,7 @@ static SPUN: Jet = |ctx, n| {
638628};
639629
640630static TURN: Jet = |ctx, n| {
641− let (list, gate) = n.as_cell().ok_or(TTanks::new())?;
631+ let (list, gate) = n.as_cell().ok_or(Tanks::new())?;
642632 Ok(Noun::list_refs(
643633 traverse_iter_result(list.list_iter().map(|x| slam_pulled_gate(ctx, gate, x)))?
644634 .iter()
@@ -647,7 +637,7 @@ static TURN: Jet = |ctx, n| {
647637};
648638
649639static WELD: Jet = |_ctx, n| {
650− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
640+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
651641
652642 Ok(Noun::list_refs(
653643 a.list_iter()
@@ -680,20 +670,18 @@ static BEX: Jet = |ctx, n| {
680670};
681671
682672static END: Jet = |ctx, n| {
683− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
673+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
684674 let bite = a.as_bite();
685675
686676 let bits = bite.bits();
687677
688678 let mask = (&ctx.big_uints.one << bits) - &ctx.big_uints.one;
689679
690− Ok(Rc::new(Noun::Atom(
691− mask & b.as_atom().ok_or(TTanks::new())?,
692− )))
680+ Ok(Rc::new(Noun::Atom(mask & b.as_atom().ok_or(Tanks::new())?)))
693681};
694682
695683static CAN: Jet = |ctx, n| {
696− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
684+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
697685 let bloq = a.as_u32().unwrap();
698686 let bloq_ = 2u32.pow(bloq);
699687
@@ -708,16 +696,16 @@ static CAN: Jet = |ctx, n| {
708696 Some((acc? << bits) | (mask & q.as_atom()?))
709697 },
710698 );
711− Ok(Rc::new(Noun::Atom(atom.ok_or(TTanks::new())?)))
699+ Ok(Rc::new(Noun::Atom(atom.ok_or(Tanks::new())?)))
712700};
713701
714702static CAT: Jet = |_ctx, n| {
715− let (bloq, b) = n.as_cell().ok_or(TTanks::new())?;
716− let (b, c) = b.as_cell().ok_or(TTanks::new())?;
703+ let (bloq, b) = n.as_cell().ok_or(Tanks::new())?;
704+ let (b, c) = b.as_cell().ok_or(Tanks::new())?;
717705
718− let bloq = bloq.as_u32().ok_or(TTanks::new())?;
719− let b = b.as_atom().ok_or(TTanks::new())?;
720− let c = c.as_atom().ok_or(TTanks::new())?;
706+ let bloq = bloq.as_u32().ok_or(Tanks::new())?;
707+ let b = b.as_atom().ok_or(Tanks::new())?;
708+ let c = c.as_atom().ok_or(Tanks::new())?;
721709
722710 Ok(Rc::new(Noun::Atom(
723711 (c << (met(bloq, &b) * 2u32.pow(bloq))) | b,
@@ -725,8 +713,8 @@ static CAT: Jet = |_ctx, n| {
725713};
726714
727715static MET: Jet = |_ctx, n| {
728− let (bloq, b) = n.as_cell().ok_or(TTanks::new())?;
729− let b: Atom = b.as_atom().ok_or(TTanks::new())?.clone();
716+ let (bloq, b) = n.as_cell().ok_or(Tanks::new())?;
717+ let b: Atom = b.as_atom().ok_or(Tanks::new())?.clone();
730718 let bloq = bloq.as_u32().unwrap();
731719
732720 Ok(Rc::new(Noun::from_u32(met(bloq, &b))))
@@ -737,28 +725,28 @@ fn met(bloq: u32, a: &Atom) -> u32 {
737725}
738726
739727static CUT: Jet = |ctx, n| {
740− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
741− let (b, d) = b.as_cell().ok_or(TTanks::new())?;
742− let (b, c) = b.as_cell().ok_or(TTanks::new())?;
728+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
729+ let (b, d) = b.as_cell().ok_or(Tanks::new())?;
730+ let (b, c) = b.as_cell().ok_or(Tanks::new())?;
743731
744732 let bits = 2u32.pow(a.as_u32().unwrap());
745733
746734 let mask = (&ctx.big_uints.one << (bits * c.as_u32().unwrap())) - &ctx.big_uints.one;
747735
748736 Ok(Rc::new(Noun::Atom(
749− (d.as_atom().ok_or(TTanks::new())? >> (bits * b.as_u32().unwrap())) & mask,
737+ (d.as_atom().ok_or(Tanks::new())? >> (bits * b.as_u32().unwrap())) & mask,
750738 )))
751739};
752740
753741static FIL: Jet = |ctx, n| {
754− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
755− let (b, c) = b.as_cell().ok_or(TTanks::new())?;
742+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
743+ let (b, c) = b.as_cell().ok_or(Tanks::new())?;
756744
757745 let bits = 2u32.pow(a.as_u32().unwrap());
758746
759747 let mask = (&ctx.big_uints.one << bits) - &ctx.big_uints.one;
760748
761− let src = c.as_atom().ok_or(TTanks::new())? & mask;
749+ let src = c.as_atom().ok_or(Tanks::new())? & mask;
762750
763751 let mut target = ctx.big_uints.zero.clone();
764752
@@ -770,30 +758,30 @@ static FIL: Jet = |ctx, n| {
770758};
771759
772760static LSH: Jet = |_ctx, n| {
773− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
761+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
774762 let a = a.as_bite();
775− let b = b.as_atom().ok_or(TTanks::new())?;
763+ let b = b.as_atom().ok_or(Tanks::new())?;
776764
777765 Ok(Rc::new(Noun::Atom(b << a.bits())))
778766};
779767
780768static RSH: Jet = |_ctx, n| {
781− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
769+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
782770 let a = a.as_bite();
783− let b = b.as_atom().ok_or(TTanks::new())?;
771+ let b = b.as_atom().ok_or(Tanks::new())?;
784772
785773 Ok(Rc::new(Noun::Atom(b >> a.bits())))
786774};
787775
788−pub static RAP: Jet = |ctx, n| {
789− let (bloq, b) = n.as_cell().ok_or(TTanks::new())?;
776+static RAP: Jet = |ctx, n| {
777+ let (bloq, b) = n.as_cell().ok_or(Tanks::new())?;
790778 let bloq = bloq.as_u32().unwrap();
791779 let bits = 2u32.pow(bloq);
792780
793781 let mut target = ctx.big_uints.zero.clone();
794782
795783 for el in b.list_iter().collect::<Vec<_>>().iter().rev() {
796− let el = el.as_atom().ok_or(TTanks::new())?;
784+ let el = el.as_atom().ok_or(Tanks::new())?;
797785 if el != &ctx.big_uints.zero {
798786 target = (target << (bits * met(bloq, &el))) | el;
799787 }
@@ -803,7 +791,7 @@ pub static RAP: Jet = |ctx, n| {
803791};
804792
805793static REP: Jet = |ctx, n| {
806− let (bite, b) = n.as_cell().ok_or(TTanks::new())?;
794+ let (bite, b) = n.as_cell().ok_or(Tanks::new())?;
807795 let bite = bite.as_bite();
808796
809797 let bits = bite.bits();
@@ -812,18 +800,18 @@ static REP: Jet = |ctx, n| {
812800 let mut target = ctx.big_uints.zero.clone();
813801
814802 for el in b.list_iter().collect::<Vec<_>>().iter().rev() {
815− target = (target << bits) | (el.as_atom().ok_or(TTanks::new())? & &mask);
803+ target = (target << bits) | (el.as_atom().ok_or(Tanks::new())? & &mask);
816804 }
817805
818806 Ok(Rc::new(Noun::Atom(target)))
819807};
820808
821809static REV: Jet = |ctx, n| {
822− let (bloq, b) = n.as_cell().ok_or(TTanks::new())?;
823− let (len, dat) = b.as_cell().ok_or(TTanks::new())?;
810+ let (bloq, b) = n.as_cell().ok_or(Tanks::new())?;
811+ let (len, dat) = b.as_cell().ok_or(Tanks::new())?;
824812 let bloq = bloq.as_u32().unwrap();
825813 let len = len.as_u32().unwrap();
826− let mut dat: Atom = dat.as_atom().ok_or(TTanks::new())?.clone();
814+ let mut dat: Atom = dat.as_atom().ok_or(Tanks::new())?.clone();
827815
828816 let bits = 2u32.pow(bloq);
829817 let mask = (&ctx.big_uints.one << bits) - &ctx.big_uints.one;
@@ -839,9 +827,9 @@ static REV: Jet = |ctx, n| {
839827};
840828
841829static RIP: Jet = |ctx, n| {
842− let (bite, b) = n.as_cell().ok_or(TTanks::new())?;
830+ let (bite, b) = n.as_cell().ok_or(Tanks::new())?;
843831 let bite = bite.as_bite();
844− let mut b: Atom = b.as_atom().ok_or(TTanks::new())?.clone();
832+ let mut b: Atom = b.as_atom().ok_or(Tanks::new())?.clone();
845833
846834 let bits = bite.bits();
847835 let mask = (&ctx.big_uints.one << bits) - &ctx.big_uints.one;
@@ -857,11 +845,11 @@ static RIP: Jet = |ctx, n| {
857845};
858846
859847static RUN: Jet = |ctx, n| {
860− let (bite, b) = n.as_cell().ok_or(TTanks::new())?;
861− let (b, gate) = b.as_cell().ok_or(TTanks::new())?;
848+ let (bite, b) = n.as_cell().ok_or(Tanks::new())?;
849+ let (b, gate) = b.as_cell().ok_or(Tanks::new())?;
862850
863851 let bite = bite.as_bite();
864− let mut b: Atom = b.as_atom().ok_or(TTanks::new())?.clone();
852+ let mut b: Atom = b.as_atom().ok_or(Tanks::new())?.clone();
865853
866854 let bits = bite.bits();
867855 let mask = (&ctx.big_uints.one << bits) - &ctx.big_uints.one;
@@ -872,7 +860,7 @@ static RUN: Jet = |ctx, n| {
872860 target = (target << bits)
873861 | ((*slam_pulled_gate(ctx, gate, &Rc::new(Noun::Atom(&mask & &b)))?)
874862 .as_atom()
875− .ok_or(TTanks::new())?
863+ .ok_or(Tanks::new())?
876864 & &mask);
877865 b = b >> bits;
878866 }
@@ -881,11 +869,11 @@ static RUN: Jet = |ctx, n| {
881869};
882870
883871static RUT: Jet = |ctx, n| {
884− let (bite, b) = n.as_cell().ok_or(TTanks::new())?;
885− let (b, gate) = b.as_cell().ok_or(TTanks::new())?;
872+ let (bite, b) = n.as_cell().ok_or(Tanks::new())?;
873+ let (b, gate) = b.as_cell().ok_or(Tanks::new())?;
886874
887875 let bite = bite.as_bite();
888− let mut b: Atom = b.as_atom().ok_or(TTanks::new())?.clone();
876+ let mut b: Atom = b.as_atom().ok_or(Tanks::new())?.clone();
889877
890878 let bits = bite.bits();
891879 let mask = (&ctx.big_uints.one << bits) - &ctx.big_uints.one;
@@ -896,7 +884,7 @@ static RUT: Jet = |ctx, n| {
896884 target.push(
897885 (*slam_pulled_gate(ctx, gate, &Rc::new(Noun::Atom(&mask & &b)))?)
898886 .as_atom()
899− .ok_or(TTanks::new())?
887+ .ok_or(Tanks::new())?
900888 & &mask,
901889 );
902890 b = b >> bits;
@@ -906,12 +894,12 @@ static RUT: Jet = |ctx, n| {
906894};
907895
908896static SEW: Jet = |ctx, n| {
909− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
910− let (b, recipient) = b.as_cell().ok_or(TTanks::new())?;
911− let (sew_start, c) = b.as_cell().ok_or(TTanks::new())?;
912− let (number_of_blocks, donor) = c.as_cell().ok_or(TTanks::new())?;
913− let recipient = recipient.as_atom().ok_or(TTanks::new())?;
914− let donor = donor.as_atom().ok_or(TTanks::new())?;
897+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
898+ let (b, recipient) = b.as_cell().ok_or(Tanks::new())?;
899+ let (sew_start, c) = b.as_cell().ok_or(Tanks::new())?;
900+ let (number_of_blocks, donor) = c.as_cell().ok_or(Tanks::new())?;
901+ let recipient = recipient.as_atom().ok_or(Tanks::new())?;
902+ let donor = donor.as_atom().ok_or(Tanks::new())?;
915903
916904 let bloq = a.as_u32().unwrap();
917905 let bits = 2u32.pow(bloq);
@@ -927,12 +915,12 @@ static SEW: Jet = |ctx, n| {
927915};
928916
929917static SWP: Jet = |ctx, n| {
930− let (bloq, b) = n.as_cell().ok_or(TTanks::new())?;
918+ let (bloq, b) = n.as_cell().ok_or(Tanks::new())?;
931919 let bloq = bloq.as_u32().unwrap();
932920 let bits = 2u32.pow(bloq);
933921 let mask = (&ctx.big_uints.one << bits) - &ctx.big_uints.one;
934922
935− let mut source = b.as_atom().ok_or(TTanks::new())?.clone();
923+ let mut source = b.as_atom().ok_or(Tanks::new())?.clone();
936924 let mut target = ctx.big_uints.zero.clone();
937925
938926 while source > ctx.big_uints.zero {
@@ -944,7 +932,7 @@ static SWP: Jet = |ctx, n| {
944932};
945933
946934static XEB: Jet = |_ctx, n| {
947− let mut a = n.as_atom().ok_or(TTanks::new())?.clone();
935+ let mut a = n.as_atom().ok_or(Tanks::new())?.clone();
948936
949937 let mut c = 0;
950938
@@ -961,22 +949,22 @@ static DIS: Jet = |_ctx, n| BINARY_ATOM(n, |a, b| a & b);
961949static MIX: Jet = |_ctx, n| BINARY_ATOM(n, |a, b| a ^ b);
962950
963951static NOT: Jet = |ctx, n| {
964− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
965− let (b, c) = b.as_cell().ok_or(TTanks::new())?;
952+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
953+ let (b, c) = b.as_cell().ok_or(Tanks::new())?;
966954
967955 let bloq = a.as_u32().unwrap();
968956 let bits = 2u32.pow(bloq);
969957 let mask = (&ctx.big_uints.one << (bits * b.as_u32().unwrap())) - &ctx.big_uints.one;
970958
971− let target = c.as_atom().ok_or(TTanks::new())? ^ &mask;
959+ let target = c.as_atom().ok_or(Tanks::new())? ^ &mask;
972960
973961 Ok(Rc::new(Noun::Atom(target)))
974962};
975963
976964static LAST: Jet = |_ctx, n| {
977− let (a, b) = n.as_cell().ok_or(TTanks::new())?;
978− let a = a.as_hair().ok_or(TTanks::new())?;
979− let b = b.as_hair().ok_or(TTanks::new())?;
965+ let (a, b) = n.as_cell().ok_or(Tanks::new())?;
966+ let a = a.as_hair().ok_or(Tanks::new())?;
967+ let b = b.as_hair().ok_or(Tanks::new())?;
980968 Ok(Noun::from_hair(last(&a, &b)))
981969};
982970
@@ -997,185 +985,22 @@ fn last<'a>(a: &'a Hair, b: &'a Hair) -> &'a Hair {
997985}
998986
999987static TRIP: Jet = |_ctx, n| {
1000− let a = n.as_atom().ok_or(TTanks::new())?;
988+ let a = n.as_atom().ok_or(Tanks::new())?;
1001989 Ok(Noun::list(
1002990 a.to_bytes_le().iter().map(|b| Noun::from_u32(*b as u32)),
1003991 ))
1004992};
1005993
1006994static MUK: Jet = |_ctx, n| {
1007− let (seed, len) = n.as_cell().ok_or(TTanks::new())?;
1008− let (len, key) = len.as_cell().ok_or(TTanks::new())?;
1009− let seed = seed.as_u32().ok_or(TTanks::new())?;
1010− let len = len.as_u32().ok_or(TTanks::new())?;
1011− let key = key.as_atom().ok_or(TTanks::new())?;
995+ let (seed, len) = n.as_cell().ok_or(Tanks::new())?;
996+ let (len, key) = len.as_cell().ok_or(Tanks::new())?;
997+ let seed = seed.as_u32().ok_or(Tanks::new())?;
998+ let len = len.as_u32().ok_or(Tanks::new())?;
999+ let key = key.as_atom().ok_or(Tanks::new())?;
10121000
10131001 Ok(Rc::new(Noun::from_u32(muk(seed, len, key))))
10141002};
10151003
1016−static AOR: Jet = |ctx, n| {
1017− let (a, b) = n.as_cell().ok_or(EMPTY_TANKS)?;
1018−
1019− if a == b {
1020− Ok(ctx.nouns.y.clone())
1021− } else {
1022− match (a.borrow(), b.borrow()) {
1023− (Noun::Cell { .. }, Noun::Atom(_)) => Ok(ctx.nouns.n.clone()),
1024− (Noun::Cell { p: a_p, q: a_q, .. }, Noun::Cell { p: b_p, q: b_q, .. }) => {
1025− if a_p == b_p {
1026− AOR(ctx, &cell(a_q, b_q))
1027− } else {
1028− AOR(ctx, &cell(a_p, b_p))
1029− }
1030− }
1031− (Noun::Atom(_), Noun::Cell { .. }) => Ok(ctx.nouns.y.clone()),
1032− (Noun::Atom(a), Noun::Atom(b)) => {
1033− let a = a.to_bytes_le();
1034− let b = b.to_bytes_le();
1035−
1036− let mut i = 0;
1037− loop {
1038− let c = a.get(i).unwrap_or(&0);
1039− let d = b.get(i).unwrap_or(&0);
1040−
1041− if a[0] == b[0] {
1042− i += 1;
1043− } else {
1044− if c < d {
1045− return Ok(ctx.nouns.y.clone());
1046− } else {
1047− return Ok(ctx.nouns.n.clone());
1048− }
1049− }
1050− }
1051− }
1052− }
1053− }
1054−};
1055−
1056−static DOR: Jet = |ctx, n| {
1057− let (a, b) = n.as_cell().ok_or(EMPTY_TANKS)?;
1058−
1059− if a == b {
1060− Ok(ctx.nouns.y.clone())
1061− } else {
1062− match (a.borrow(), b.borrow()) {
1063− (Noun::Cell { .. }, Noun::Atom(_)) => Ok(ctx.nouns.n.clone()),
1064− (Noun::Cell { p: a_p, q: a_q, .. }, Noun::Cell { p: b_p, q: b_q, .. }) => {
1065− if a_p == b_p {
1066− DOR(ctx, &cell(a_q, b_q))
1067− } else {
1068− DOR(ctx, &cell(a_p, b_p))
1069− }
1070− }
1071− (Noun::Atom(_), Noun::Cell { .. }) => Ok(ctx.nouns.y.clone()),
1072− (Noun::Atom(a), Noun::Atom(b)) => {
1073− if a < b {
1074− Ok(ctx.nouns.y.clone())
1075− } else {
1076− Ok(ctx.nouns.n.clone())
1077− }
1078− }
1079− }
1080− }
1081−};
1082−
1083−static GOR: Jet = |ctx, n| {
1084− let (a, b) = n.as_cell().ok_or(EMPTY_TANKS)?;
1085− let c = a.mug();
1086− let d = b.mug();
1087−
1088− if c == d {
1089− DOR(ctx, n)
1090− } else {
1091− if c < d {
1092− Ok(ctx.nouns.y.clone())
1093− } else {
1094− Ok(ctx.nouns.n.clone())
1095− }
1096− }
1097−};
1098−
1099−static GOR_PAIR: fn(&mut InterpreterContext, &Rc<Noun>, &Rc<Noun>) -> Result<bool, TTanks> =
1100− |ctx, a, b| {
1101− let c = a.mug();
1102− let d = b.mug();
1103−
1104− if c == d {
1105− Ok(DOR(ctx, &(a.clone(), b.clone()).as_noun())? == ctx.nouns.y)
1106− } else {
1107− Ok(c < d)
1108− }
1109− };
1110−
1111−static MOR: Jet = |ctx, n| {
1112− let (a, b) = n.as_cell().ok_or(EMPTY_TANKS)?;
1113− let c = Noun::from_u32(a.mug()).mug();
1114− let d = Noun::from_u32(b.mug()).mug();
1115−
1116− if c == d {
1117− DOR(ctx, n)
1118− } else {
1119− if c < d {
1120− Ok(ctx.nouns.y.clone())
1121− } else {
1122− Ok(ctx.nouns.n.clone())
1123− }
1124− }
1125−};
1126−
1127−static LOOK: Jet = |ctx, n| {
1128− let (cog, dab) = n.as_cell().ok_or(EMPTY_TANKS)?;
1129− LOOK_PAIR(ctx, Atom::new(vec![1]), cog, dab)
1130−};
1131−
1132−static LOOK_PAIR: fn(
1133− &mut InterpreterContext,
1134− Atom,
1135− &Rc<Noun>,
1136− &Rc<Noun>,
1137−) -> Result<Rc<Noun>, TTanks> = |ctx, axe, cog, dab| {
1138− if dab.is_sig() {
1139− Ok(ctx.nouns.sig.clone())
1140− } else {
1141− let (node, b, c) = dab.tuple_3().ok_or(EMPTY_TANKS)?;
1142− let l = b;
1143− let r = c;
1144− let (p, q) = node.as_cell().ok_or(EMPTY_TANKS)?;
1145− if b.is_sig() && c.is_sig() {
1146− if cog == p {
1147− Ok((ctx.nouns.sig.clone(), Noun::Atom(axe), q.clone()).as_noun())
1148− } else {
1149− Ok(ctx.nouns.sig.clone())
1150− }
1151− } else if b.is_sig() {
1152− if cog == p {
1153− Ok((ctx.nouns.sig.clone(), PEG_U32(&axe, 2), q.clone()).as_noun())
1154− } else if GOR_PAIR(ctx, cog, p)? {
1155− Ok(ctx.nouns.sig.clone())
1156− } else {
1157− LOOK_PAIR(ctx, PEG_U32(&axe, 3), cog, r)
1158− }
1159− } else if c.is_sig() {
1160− if cog == p {
1161− Ok((ctx.nouns.sig.clone(), PEG_U32(&axe, 2), q.clone()).as_noun())
1162− } else if GOR_PAIR(ctx, cog, p)? {
1163− LOOK_PAIR(ctx, PEG_U32(&axe, 3), cog, l)
1164− } else {
1165− Ok(ctx.nouns.sig.clone())
1166− }
1167− } else {
1168− if cog == p {
1169− Ok((ctx.nouns.sig.clone(), PEG_U32(&axe, 2), q.clone()).as_noun())
1170− } else if GOR_PAIR(ctx, cog, p)? {
1171− LOOK_PAIR(ctx, PEG_U32(&axe, 6), cog, l)
1172− } else {
1173− LOOK_PAIR(ctx, PEG_U32(&axe, 7), cog, r)
1174− }
1175− }
1176− }
1177−};
1178−
11791004static JUST: DoubleJet = |ctx, n, m| {
11801005 let char = n.as_atom()?;
11811006 let nail = m.as_nail()?;
nock/src/lib.rs+1
@@ -1,5 +1,6 @@
11#![feature(iter_intersperse)]
22#![feature(let_chains)]
3+#![feature(iter_repeat_n)]
34
45use std::rc::Rc;
56
nock/src/main.rs+43−69
@@ -1,8 +1,6 @@
11use clap::{command, error::Result, Parser, Subcommand};
22use core::str;
3−use nock::interpreter::{
4− ram_ttanks, slam_pulled_gate, tar, InterpreterContext, TTanks, JET_COUNTER,
5−};
3+use nock::interpreter::{eval_pulled_gate, slam_pulled_gate, tar, InterpreterContext, Tanks};
64use nock::{
75 cue::cue_bytes,
86 interpreter::{generate_interpreter_context, slam},
@@ -67,38 +65,9 @@ enum RunCommand {
6765 },
6866}
6967
70−fn print_jet_counter() {
71− let counter = JET_COUNTER.lock().unwrap();
72− let mut builder = tabled::builder::Builder::new();
73− builder.push_record(vec!["jet", "number of calls"]);
74− let mut counters = counter.iter().collect::<Vec<_>>();
75− counters.sort_unstable_by_key(|x| x.1);
76− for (key, count) in counters {
77− let key = String::from_utf8(key.to_bytes_le()).unwrap();
78− builder.push_record(vec![key, count.to_string()]);
79− }
80− println!(
81− "\n{}",
82− builder
83− .build()
84− .with(tabled::settings::style::Style::rounded())
85− );
86−}
87−
8868fn main() -> Result<(), std::io::Error> {
89− if cfg!(feature = "count-jets") {
90− ctrlc::set_handler(|| {
91− print_jet_counter();
92− exit(1);
93− })
94− .unwrap();
95− }
96−
9769 let urbit = LazyCell::new(GENERATE_URBIT);
98− let mut ctx = InterpreterContext {
99− // slog: |_| {},
100− ..generate_interpreter_context()
101− };
70+ let mut ctx = generate_interpreter_context();
10271
10372 let cli = Cli::parse();
10473
@@ -120,7 +89,7 @@ fn main() -> Result<(), std::io::Error> {
12089 spinner.fail("Gate execution failed");
12190
12291 let mut spinner = new_spinner(String::from("Building trace"));
123− let trace = ram_ttanks(&mut ctx, tanks);
92+ let trace = wash(&mut ctx, &urbit, tanks);
12493 spinner.success("Trace built");
12594 println!("{trace}");
12695 exit(1);
@@ -167,16 +136,12 @@ fn main() -> Result<(), std::io::Error> {
167136 let (_typ, nok) = read_nock_or_compile(&mut spinner, &mut ctx, &urbit, &nock)?;
168137
169138 let sig = ctx.nouns.sig.clone();
170− let result = tar(&mut ctx, &sig, &nok).unwrap();
139+ let result = tar(&mut ctx, sig, &nok).unwrap();
171140
172141 println!("{result}");
173142 }
174143 }
175144
176− if cfg!(feature = "count-jets") {
177− print_jet_counter()
178− }
179−
180145 Ok(())
181146}
182147
@@ -214,8 +179,8 @@ fn compile_to_nock(
214179 ctx,
215180 &urbit,
216181 &cell(&Rc::new(Noun::from_bytes(b"hoon")), &ctx.nouns.sig),
217− );
218− let hoon = or_wash_fail(ctx, hoon, spinner, "Failed to read Hoon standard library");
182+ )
183+ .unwrap();
219184 let (hoon_type, hoon) = hoon.as_cell().unwrap();
220185
221186 spinner.update_text(format!("Compiling {root:#?}"));
@@ -232,26 +197,25 @@ fn compile_to_nock(
232197 &Rc::new(Noun::Atom(Atom::from_bytes_le(&root_source))),
233198 ),
234199 ),
235− );
236− let target = or_wash_fail(ctx, target, spinner, "Compilation failed");
200+ )
201+ .unwrap();
237202
238203 let (target_type, target_nock) = target.as_cell().unwrap();
239204
240205 spinner.update_text(format!("Combining Nock formulas"));
241206
242− let combined_nock = slam_pulled_gate(
243− ctx,
244− &urbit,
245− &cell(
246− &Rc::new(Noun::from_bytes(b"comb")),
247− &cell(hoon, target_nock),
248− ),
249− );
250− let combined_nock = cell(
207+ Ok(cell(
251208 target_type,
252− &or_wash_fail(ctx, combined_nock, spinner, "Failed to combine Nock"),
253− );
254− Ok(combined_nock)
209+ &slam_pulled_gate(
210+ ctx,
211+ &urbit,
212+ &cell(
213+ &Rc::new(Noun::from_bytes(b"comb")),
214+ &cell(hoon, target_nock),
215+ ),
216+ )
217+ .unwrap(),
218+ ))
255219}
256220
257221fn read_nock_or_compile(
@@ -269,19 +233,29 @@ fn read_nock_or_compile(
269233 }
270234}
271235
272−fn or_wash_fail(
273− ctx: &mut InterpreterContext,
274− source: Result<Rc<Noun>, TTanks>,
275− spinner: &mut Spinner,
276− fail_err: &str,
277−) -> Rc<Noun> {
278− match source {
279− Ok(source) => source,
280− Err(tanks) => {
281− spinner.fail(fail_err);
282− let washed_tanks = ram_ttanks(ctx, tanks);
283− eprintln!("{}", washed_tanks);
284− exit(1)
285− }
236+fn wash(ctx: &mut InterpreterContext, urbit: &Rc<Noun>, tanks: Tanks) -> String {
237+ let mut target = String::new();
238+ for (subj, q) in tanks.iter() {
239+ let gate = tar(ctx, subj.clone(), q).unwrap();
240+ let tank = eval_pulled_gate(ctx, gate).unwrap();
241+ let mut str = slam_pulled_gate(
242+ ctx,
243+ &urbit,
244+ &cell(
245+ &Rc::new(Noun::from_bytes(b"wash")),
246+ &cell(
247+ &cell(&Rc::new(Noun::from_u32(0)), &Rc::new(Noun::from_u32(80))),
248+ &tank,
249+ ),
250+ ),
251+ )
252+ .unwrap()
253+ .as_atom()
254+ .unwrap()
255+ .to_bytes_le();
256+
257+ target.push_str(str::from_utf8_mut(&mut *str).unwrap())
286258 }
259+
260+ target
287261}
nock/src/noun.rs−313
@@ -47,10 +47,6 @@ impl PartialEq for Noun {
4747impl<'a> Noun {
4848 pub const SIG: Noun = Noun::Atom(BigUint::ZERO);
4949
50− pub fn rc(self: Self) -> Rc<Self> {
51− Rc::new(self)
52− }
53−
5450 pub fn from_u32(a: u32) -> Noun {
5551 Noun::Atom(Atom::new(vec![a]))
5652 }
@@ -104,59 +100,6 @@ impl<'a> Noun {
104100 }
105101 }
106102
107− pub fn as_cell_cloned(self: &'a Self) -> Option<(Rc<Noun>, Rc<Noun>)> {
108− match self {
109− Noun::Cell { p, q, .. } => Option::Some((p.clone(), q.clone())),
110− _ => Option::None,
111− }
112− }
113−
114− pub fn tuple_2(self: &Self) -> Option<(&Rc<Noun>, &Rc<Noun>)> {
115− self.as_cell()
116− }
117−
118− pub fn tuple_3(self: &Self) -> Option<(&Rc<Noun>, &Rc<Noun>, &Rc<Noun>)> {
119− let (a, b) = self.as_cell()?;
120− let (b, c) = b.as_cell()?;
121−
122− Some((a, b, c))
123− }
124− pub fn tuple_4(self: &Self) -> Option<(&Rc<Noun>, &Rc<Noun>, &Rc<Noun>, &Rc<Noun>)> {
125− let (a, b) = self.as_cell()?;
126− let (b, c) = b.as_cell()?;
127− let (c, d) = c.as_cell()?;
128−
129− Some((a, b, c, d))
130− }
131−
132− pub fn tuple_5(self: &Self) -> Option<(&Rc<Noun>, &Rc<Noun>, &Rc<Noun>, &Rc<Noun>, &Rc<Noun>)> {
133− let (a, b) = self.as_cell()?;
134− let (b, c) = b.as_cell()?;
135− let (c, d) = c.as_cell()?;
136− let (d, e) = d.as_cell()?;
137−
138− Some((a, b, c, d, e))
139− }
140−
141− pub fn tuple_6(
142− self: &Self,
143− ) -> Option<(
144− &Rc<Noun>,
145− &Rc<Noun>,
146− &Rc<Noun>,
147− &Rc<Noun>,
148− &Rc<Noun>,
149− &Rc<Noun>,
150− )> {
151− let (a, b) = self.as_cell()?;
152− let (b, c) = b.as_cell()?;
153− let (c, d) = c.as_cell()?;
154− let (d, e) = d.as_cell()?;
155− let (e, f) = e.as_cell()?;
156−
157− Some((a, b, c, d, e, f))
158− }
159−
160103 pub fn is_sig(self: &Self) -> bool {
161104 match self {
162105 Noun::Atom(n) if *n == Atom::ZERO => true,
@@ -301,262 +244,6 @@ impl<'a> Noun {
301244 }
302245}
303246
304−pub trait AsNoun {
305− fn as_noun(self: &Self) -> Rc<Noun>;
306−}
307−
308−impl AsNoun for Atom {
309− fn as_noun(self: &Self) -> Rc<Noun> {
310− Rc::new(Noun::Atom(self.clone()))
311− }
312−}
313−
314−impl AsNoun for Noun {
315− fn as_noun(self: &Self) -> Rc<Noun> {
316− Rc::new(self.clone())
317− }
318−}
319−
320−impl AsNoun for Rc<Noun> {
321− fn as_noun(self: &Self) -> Rc<Noun> {
322− self.clone()
323− }
324−}
325−
326−impl<A, B> AsNoun for (A, B)
327−where
328− A: AsNoun,
329− B: AsNoun,
330−{
331− fn as_noun(self: &Self) -> Rc<Noun> {
332− let (a, b) = self;
333−
334− cell(&a.as_noun(), &b.as_noun())
335− }
336−}
337−
338−impl<A, B, C> AsNoun for (A, B, C)
339−where
340− A: AsNoun,
341− B: AsNoun,
342− C: AsNoun,
343−{
344− fn as_noun(self: &Self) -> Rc<Noun> {
345− let (a, b, c) = self;
346−
347− cell(&a.as_noun(), &cell(&b.as_noun(), &c.as_noun()))
348− }
349−}
350−
351−impl<A, B, C, D> AsNoun for (A, B, C, D)
352−where
353− A: AsNoun,
354− B: AsNoun,
355− C: AsNoun,
356− D: AsNoun,
357−{
358− fn as_noun(self: &Self) -> Rc<Noun> {
359− let (a, b, c, d) = self;
360−
361− cell(
362− &a.as_noun(),
363− &cell(&b.as_noun(), &cell(&c.as_noun(), &d.as_noun())),
364− )
365− }
366−}
367−
368−impl<A, B, C, D, E> AsNoun for (A, B, C, D, E)
369−where
370− A: AsNoun,
371− B: AsNoun,
372− C: AsNoun,
373− D: AsNoun,
374− E: AsNoun,
375−{
376− fn as_noun(self: &Self) -> Rc<Noun> {
377− let (a, b, c, d, e) = self;
378−
379− cell(
380− &a.as_noun(),
381− &cell(
382− &b.as_noun(),
383− &cell(&c.as_noun(), &cell(&d.as_noun(), &e.as_noun())),
384− ),
385− )
386− }
387−}
388−
389−impl<A, B, C, D, E, F> AsNoun for (A, B, C, D, E, F)
390−where
391− A: AsNoun,
392− B: AsNoun,
393− C: AsNoun,
394− D: AsNoun,
395− E: AsNoun,
396− F: AsNoun,
397−{
398− fn as_noun(self: &Self) -> Rc<Noun> {
399− let (a, b, c, d, e, f) = self;
400−
401− cell(
402− &a.as_noun(),
403− &cell(
404− &b.as_noun(),
405− &cell(
406− &c.as_noun(),
407− &cell(&d.as_noun(), &cell(&e.as_noun(), &f.as_noun())),
408− ),
409− ),
410− )
411− }
412−}
413−
414−impl From<(Rc<Noun>, Rc<Noun>)> for Noun {
415− fn from(value: (Rc<Noun>, Rc<Noun>)) -> Self {
416− naked_cell(&value.0, &value.1)
417− }
418−}
419−
420−impl From<(Rc<Noun>, Rc<Noun>, Rc<Noun>)> for Noun {
421− fn from(value: (Rc<Noun>, Rc<Noun>, Rc<Noun>)) -> Self {
422− naked_cell(&value.0, &cell(&value.1, &value.2))
423− }
424−}
425−
426−impl From<(Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>)> for Noun {
427− fn from(value: (Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>)) -> Self {
428− naked_cell(&value.0, &cell(&value.1, &cell(&value.2, &value.3)))
429− }
430−}
431−
432−impl From<(Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>)> for Noun {
433− fn from(value: (Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>)) -> Self {
434− naked_cell(
435− &value.0,
436− &cell(&value.1, &cell(&value.2, &cell(&value.3, &value.4))),
437− )
438− }
439−}
440−
441−impl From<(Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>)> for Noun {
442− fn from(value: (Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>)) -> Self {
443− naked_cell(
444− &value.0,
445− &cell(
446− &value.1,
447− &cell(&value.2, &cell(&value.3, &cell(&value.4, &value.5))),
448− ),
449− )
450− }
451−}
452−
453−impl
454− From<(
455− Rc<Noun>,
456− Rc<Noun>,
457− Rc<Noun>,
458− Rc<Noun>,
459− Rc<Noun>,
460− Rc<Noun>,
461− Rc<Noun>,
462− )> for Noun
463−{
464− fn from(
465− value: (
466− Rc<Noun>,
467− Rc<Noun>,
468− Rc<Noun>,
469− Rc<Noun>,
470− Rc<Noun>,
471− Rc<Noun>,
472− Rc<Noun>,
473− ),
474− ) -> Self {
475− naked_cell(
476− &value.0,
477− &cell(
478− &value.1,
479− &cell(
480− &value.2,
481− &cell(&value.3, &cell(&value.4, &cell(&value.5, &value.6))),
482− ),
483− ),
484− )
485− }
486−}
487−
488−impl From<Noun> for Option<(Rc<Noun>, Rc<Noun>)> {
489− fn from(value: Noun) -> Self {
490− value.as_cell_cloned()
491− }
492−}
493−
494−impl From<Noun> for Option<(Rc<Noun>, Rc<Noun>, Rc<Noun>)> {
495− fn from(value: Noun) -> Self {
496− let (a, b) = value.as_cell_cloned()?;
497− let (b, c) = b.as_cell_cloned()?;
498− Some((a, b, c))
499− }
500−}
501−
502−impl From<Noun> for Option<(Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>)> {
503− fn from(value: Noun) -> Self {
504− let (a, b) = value.as_cell_cloned()?;
505− let (b, c) = b.as_cell_cloned()?;
506− let (c, d) = c.as_cell_cloned()?;
507− Some((a, b, c, d))
508− }
509−}
510−
511−impl From<Noun> for Option<(Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>)> {
512− fn from(value: Noun) -> Self {
513− let (a, b) = value.as_cell_cloned()?;
514− let (b, c) = b.as_cell_cloned()?;
515− let (c, d) = c.as_cell_cloned()?;
516− let (d, e) = d.as_cell_cloned()?;
517− Some((a, b, c, d, e))
518− }
519−}
520−
521−impl From<Noun> for Option<(Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>, Rc<Noun>)> {
522− fn from(value: Noun) -> Self {
523− let (a, b) = value.as_cell_cloned()?;
524− let (b, c) = b.as_cell_cloned()?;
525− let (c, d) = c.as_cell_cloned()?;
526− let (d, e) = d.as_cell_cloned()?;
527− let (e, f) = e.as_cell_cloned()?;
528− Some((a, b, c, d, e, f))
529− }
530−}
531−
532−impl From<Noun>
533− for Option<(
534− Rc<Noun>,
535− Rc<Noun>,
536− Rc<Noun>,
537− Rc<Noun>,
538− Rc<Noun>,
539− Rc<Noun>,
540− Rc<Noun>,
541− )>
542−{
543− fn from(value: Noun) -> Self {
544− let (a, b) = value.as_cell_cloned()?;
545− let (b, c) = b.as_cell_cloned()?;
546− let (c, d) = c.as_cell_cloned()?;
547− let (d, e) = d.as_cell_cloned()?;
548− let (e, f) = e.as_cell_cloned()?;
549− let (f, g) = f.as_cell_cloned()?;
550− Some((a, b, c, d, e, f, g))
551− }
552−}
553−
554−impl From<&[u8]> for Noun {
555− fn from(value: &[u8]) -> Self {
556− Noun::from_bytes(value)
557− }
558−}
559−
560247#[derive(Clone)]
561248pub struct Edge<'a> {
562249 pub hair: Hair,
nock/test.hoondeleted−3
@@ -1,3 +0,0 @@
1−|= *
2−=/ target (gor 'd' 'c')
3−(crip "{<target>}")
nock/wock.hoon+13−201
@@ -1,204 +1,16 @@
11=>
22|%
3−++ http ^?
4− |%
5− :: +header-list: an ordered list of http headers
6− ::
7− +$ header-list
8− (list [key=@t value=@t])
9− :: +method: exhaustive list of http verbs
10− ::
11− +$ method
12− $? %'CONNECT'
13− %'DELETE'
14− %'GET'
15− %'HEAD'
16− %'OPTIONS'
17− %'PATCH'
18− %'POST'
19− %'PUT'
20− %'TRACE'
21− ==
22− :: +request: a single http request
23− ::
24− +$ request
25− $: :: method: http method
26− ::
27− method=method
28− :: url: the url requested
29− ::
30− :: The url is not escaped. There is no escape.
31− ::
32− url=@t
33− :: header-list: headers to pass with this request
34− ::
35− =header-list
36− :: body: optionally, data to send with this request
37− ::
38− body=(unit octs)
39− ==
40− :: +response-header: the status code and header list on an http request
41− ::
42− :: We separate these away from the body data because we may not wait for
43− :: the entire body before we send a %progress to the caller.
44− ::
45− +$ response-header
46− $: :: status: http status code
47− ::
48− status-code=@ud
49− :: headers: http headers
50− ::
51− headers=header-list
52− ==
53− :: +http-event: packetized http
54− ::
55− :: Urbit treats Earth's HTTP servers as pipes, where Urbit sends or
56− :: receives one or more %http-events. The first of these will always be a
57− :: %start or an %error, and the last will always be %cancel or will have
58− :: :complete set to %.y to finish the connection.
59− ::
60− :: Calculation of control headers such as 'Content-Length' or
61− :: 'Transfer-Encoding' should be performed at a higher level; this structure
62− :: is merely for what gets sent to or received from Earth.
63− ::
64− +$ http-event
65− $% :: %start: the first packet in a response
66− ::
67− $: %start
68− :: response-header: first event information
69− ::
70− =response-header
71− :: data: data to pass to the pipe
72− ::
73− data=(unit octs)
74− :: whether this completes the request
75− ::
76− complete=?
77− ==
78− :: %continue: every subsequent packet
79− ::
80− $: %continue
81− :: data: data to pass to the pipe
82− ::
83− data=(unit octs)
84− :: complete: whether this completes the request
85− ::
86− complete=?
87− ==
88− :: %cancel: represents unsuccessful termination
89− ::
90− [%cancel ~]
91− ==
92− :: +get-header: returns the value for :header, if it exists in :header-list
93− ::
94− ++ get-header
95− |= [header=@t =header-list]
96− ^- (unit @t)
97− ::
98− ?~ header-list
99− ~
100− ::
101− ?: =(key.i.header-list header)
102− `value.i.header-list
103− ::
104− $(header-list t.header-list)
105− :: +set-header: sets the value of an item in the header list
106− ::
107− :: This adds to the end if it doesn't exist.
108− ::
109− ++ set-header
110− |= [header=@t value=@t =header-list]
111− ^- ^header-list
112− ::
113− ?~ header-list
114− :: we didn't encounter the value, add it to the end
115− ::
116− [[header value] ~]
117− ::
118− ?: =(key.i.header-list header)
119− [[header value] t.header-list]
120− ::
121− [i.header-list $(header-list t.header-list)]
122− :: +delete-header: removes the first instance of a header from the list
123− ::
124− ++ delete-header
125− |= [header=@t =header-list]
126− ^- ^header-list
127− ::
128− ?~ header-list
129− ~
130− :: if we see it in the list, remove it
131− ::
132− ?: =(key.i.header-list header)
133− t.header-list
134− ::
135− [i.header-list $(header-list t.header-list)]
136− :: +unpack-header: parse header field values
137− ::
138− ++ unpack-header
139− |^ |= value=@t
140− ^- (unit (list (map @t @t)))
141− (rust (cass (trip value)) values)
142− ::
143− ++ values
144− %+ more
145− (ifix [. .]:(star ;~(pose ace (just '\09'))) com)
146− pairs
147− ::
148− ++ pairs
149− %+ cook
150− ~(gas by *(map @t @t))
151− %+ most (ifix [. .]:(star ace) mic)
152− ;~(plug token ;~(pose ;~(pfix tis value) (easy '')))
153− ::
154− ++ value
155− ;~(pose token quoted-string)
156− ::
157− ++ token :: 7230 token
158− %+ cook crip
159− ::NOTE this is ptok:de-purl:html, but can't access that here
160− %- plus
161− ;~ pose
162− aln zap hax buc cen pam soq tar lus
163− hep dot ket cab tic bar sig
164− ==
165− ::
166− ++ quoted-string :: 7230 quoted string
167− %+ cook crip
168− %+ ifix [. .]:;~(less (jest '\\"') doq)
169− %- star
170− ;~ pose
171− ;~(pfix bas ;~(pose (just '\09') ace prn))
172− ;~(pose (just '\09') ;~(less (mask "\22\5c\7f") (shim 0x20 0xff)))
173− ==
174− --
175− :: +simple-payload: a simple, one event response used for generators
176− ::
177− +$ simple-payload
178− $: :: response-header: status code, etc
179− ::
180− =response-header
181− :: data: the data returned as the body
182− ::
183− data=(unit octs)
184− ==
185− --
186−++ hop
187− |$ step
188− $%
189− [%fetch request:http]
190− ==
1913++ wock-app
192− |$ [model step]
4+ |$ [model event]
1935 $_ ^?
1946 |%
1957 ++ init *model
1968 ++ view |~ model *manx
197− ++ move |~ [model step] *model
9+ ++ move |~ [model [event *]] *model
19810 --
19911++ app
200− |* [model=mold step=mold]
201− |= app=(wock-app model step)
12+ |* [model=mold event=mold]
13+ |= app=(wock-app model event)
20214 |= [cmd=@tas args=*]
20315 ?+ cmd !!
20416 %init
@@ -206,19 +18,19 @@
20618 %view
20719 (view.app (model args))
20820 %move
209− =/ [=model step-tape=tape payload=*] (,[model tape *] args)
210− (move.app [model (step [(crip step-tape) payload])])
21+ =/ [=model event-tape=tape payload=*] (,[model tape *] args)
22+ (move.app [model (event (crip event-tape)) payload])
21123 ==
21224--
21325=>
21426|%
215−+$ step
216− $%
217− [%inc ~]
218− [%dec ~]
27++$ event
28+ $?
29+ %inc
30+ %dec
21931 ==
22032--
221−%- (app @ud step)
33+%- (app @ud event)
22234|%
22335++ view
22436 |= count=@ud
@@ -228,9 +40,9 @@
22840 ;button(on-click "inc"): +
22941 ;button(on-click "dec"): -
23042 ==
231−++ init 11
43+++ init 8
23244++ move
233− |= [model=@ud step=step]
45+ |= [model=@ud step=[event *]]
23446 ^- @ud
23547 ?- -.step
23648 %inc +(model)
wock-web/Cargo.toml+1−1
@@ -22,7 +22,7 @@ console_error_panic_hook = { version = "0.1.7", optional = true }
2222wasm-bindgen-futures = "0.4.45"
2323js-sys = "0.3.72"
2424# dodrio = "0.2.0"
25−dodrio = { path = "../dodrio" }
25+dodrio = { path = "../../dodrio" }
2626
2727[dependencies.web-sys]
2828version = "0.3.72"
wock-web/src/lib.rs+44−72
@@ -1,24 +1,25 @@
11#![feature(iter_collect_into)]
22mod utils;
33
4−use core::panic;
5−use std::{borrow::BorrowMut, cell::RefCell, rc::Rc};
4+use std::{
5+ borrow::BorrowMut,
6+ cell::RefCell,
7+ ops::{Deref, DerefMut},
8+ rc::Rc,
9+};
610
711use dodrio::{
812 bumpalo::{self, Bump},
913 Attribute, Listener, Node, NodeKey, Render, RenderContext, RootRender, VdomWeak,
1014};
11−use gloo::net::{self, http::Request};
1215use nock::{
1316 cue::cue_bytes,
14− interpreter::{generate_interpreter_context, ram_ttanks, slam, InterpreterContext},
17+ interpreter::{generate_interpreter_context, slam, InterpreterContext},
1518 noun::{cell, Noun},
1619};
1720use wasm_bindgen::prelude::*;
18−use web_sys::{
19− console::{error_1, log_1},
20− Element,
21−};
21+use wasm_bindgen_futures::JsFuture;
22+use web_sys::Element;
2223
2324#[wasm_bindgen]
2425extern "C" {
@@ -26,12 +27,8 @@ extern "C" {
2627
2728 #[wasm_bindgen(js_namespace = console)]
2829 fn log(s: &str);
29−
30− #[wasm_bindgen(js_namespace = console)]
31− fn error(s: &str);
3230}
3331
34−#[derive(Clone)]
3532struct WockApp {
3633 nock: Rc<Noun>,
3734 ctx: RefCell<InterpreterContext>,
@@ -40,19 +37,14 @@ struct WockApp {
4037
4138impl<'a> Render<'a> for WockApp {
4239 fn render(&self, cx: &mut dodrio::RenderContext<'a>) -> dodrio::Node<'a> {
43− match slam(
40+ let sail = slam(
4441 &mut *self.ctx.borrow_mut(),
4542 &self.nock,
4643 &cell(&Rc::new(Noun::from_bytes(b"view")), &self.model),
47− ) {
48− Ok(sail) => render_sail(cx, sail),
49− Err(tanks) => {
50− let str = ram_ttanks(&mut (*self).ctx.borrow_mut(), tanks);
51− error_1(&JsValue::from_str(&str));
52−
53− panic!("Could not render");
54− }
55− }
44+ )
45+ .unwrap();
46+
47+ render_sail(cx, sail)
5648 }
5749}
5850
@@ -90,40 +82,23 @@ fn render_sail<'a>(ctx: &mut RenderContext<'a>, manx: Rc<Noun>) -> Node<'a> {
9082 _event: web_sys::Event| {
9183 let app = root.unwrap_mut::<WockApp>();
9284
93− let binding = app.ctx.clone();
94− let mut ctx = binding.borrow_mut();
95− let sig = ctx.nouns.sig.clone();
85+ let mut ctx = app.ctx.borrow_mut();
9686
97− let step: Rc<Noun> = Rc::new((value.clone(), sig).into());
87+ let sig = ctx.nouns.sig.clone();
9888
99− match slam(
89+ let new_model = slam(
10090 &mut ctx,
10191 &app.nock,
102− &Rc::new(
103− (
104− Noun::from_bytes(b"move").rc(),
105− app.model.clone(),
106− step.clone(),
107− )
108− .into(),
92+ &cell(
93+ &Rc::new(Noun::from_bytes(b"move")),
94+ &cell(&app.model, &cell(&value, &sig)),
10995 ),
110− ) {
111− Ok(new_model) => {
112− app.model = new_model;
113− vdom.schedule_render();
114− }
115− Err(tanks) => {
116− let mut ctx = ctx.borrow_mut().clone();
117−
118− let str = ram_ttanks(&mut ctx, tanks);
119− error_1(&JsValue::from_str(&str));
120−
121− error(&*format!(
122− "Application failed to process event: {}",
123− step
124− ))
125− }
126− };
96+ )
97+ .unwrap();
98+
99+ app.model = new_model;
100+
101+ vdom.schedule_render();
127102 },
128103 )
129104 },
@@ -167,32 +142,19 @@ fn tape_to_bump_str<'a>(bump: &'a Bump, non: &Rc<Noun>) -> &'a str {
167142
168143#[wasm_bindgen]
169144pub async fn main(path: &str) {
170− let nock = load_nock(path).await.unwrap_or_else(|err| {
171− error(&*err.to_string());
172− panic!("Could not load nock.")
173− });
145+ let nock = load_nock(path).await;
174146
175147 let document = gloo::utils::document();
176148 let output_el: Element = document.get_element_by_id("output").unwrap();
177149
178− let mut ctx = InterpreterContext {
179− slog: |str: &String| log_1(&JsValue::from_str(&*str)),
180− ..generate_interpreter_context()
181− };
150+ let mut ctx = generate_interpreter_context();
182151
183152 let model = slam(
184153 &mut ctx,
185154 &nock,
186155 &cell(&Rc::new(Noun::from_bytes(b"init")), &Rc::new(Noun::SIG)),
187− );
188− let model = match model {
189− Ok(model) => model,
190− Err(tanks) => {
191− let str = ram_ttanks(&mut ctx, tanks);
192− error_1(&JsValue::from_str(&str));
193− panic!("Could not load initial model.");
194− }
195− };
156+ )
157+ .unwrap();
196158
197159 dodrio::Vdom::new(
198160 &output_el,
@@ -205,11 +167,21 @@ pub async fn main(path: &str) {
205167 .forget();
206168}
207169
208−pub async fn load_nock(path: &str) -> Result<Rc<Noun>, net::Error> {
209− let blob = Request::get(path).send().await?.binary().await?;
170+pub async fn load_nock(path: &str) -> Rc<Noun> {
171+ let window = web_sys::window().unwrap();
172+
173+ let resp = JsFuture::from(window.fetch_with_str(path)).await.unwrap();
174+ assert!(resp.is_instance_of::<web_sys::Response>());
175+ let resp: web_sys::Response = resp.dyn_into().unwrap();
176+ let blob = JsFuture::from(resp.blob().unwrap()).await.unwrap();
177+ assert!(blob.is_instance_of::<web_sys::Blob>());
178+ let blob: web_sys::Blob = blob.dyn_into().unwrap();
179+
180+ let nock =
181+ js_sys::Uint8Array::new(&JsFuture::from(blob.array_buffer()).await.unwrap()).to_vec();
210182
211− let nock = cue_bytes(&*blob);
183+ let nock = cue_bytes(&*nock);
212184 let (_typ, nok) = nock.as_cell().unwrap();
213185
214− Ok(nok.clone())
186+ nok.clone()
215187}
wock.nock

Binary file not shown.