logicaffeine_compile/semantics/world.rs
1//! The page world: everything a page's run reads and writes besides the program's own values — the
2//! document, the journal of the turns it took, the page clock, the sealed test world, and the
3//! browser's storage.
4//!
5//! Each lives in a thread-local of its own module; this is where they are handled as ONE world. A
6//! program run starts from a fresh one ([`begin_run`]); a session carried onto a thread of its own
7//! takes its caller's world with it and hands it back ([`PageWorld`]); a comparison of tiers enters
8//! a fresh one per tier and restores the caller's afterwards.
9
10use std::cell::{Cell, RefCell};
11
12/// One entry of the journal: a turn that ran, the instance it ran against, what it changed, and —
13/// when it trapped — the failure that rolled it back.
14#[derive(Clone, Debug)]
15pub struct JournalEntry {
16 pub instance: String,
17 pub action: String,
18 /// `(field, before, after)` for every field the turn changed.
19 pub changes: Vec<(String, String, String)>,
20 /// The failure that rolled the turn back, if it did not commit.
21 pub failed: Option<String>,
22}
23
24thread_local! {
25 static JOURNAL: RefCell<Vec<JournalEntry>> = const { RefCell::new(Vec::new()) };
26 /// Whether the NEXT program run is a hot reload rather than a fresh start ([`arm_hot_reload`]).
27 static HOT_RELOAD: Cell<bool> = const { Cell::new(false) };
28}
29
30/// Record a turn.
31pub fn journal_push(entry: JournalEntry) {
32 JOURNAL.with(|j| j.borrow_mut().push(entry));
33}
34
35/// Every turn recorded since the world began, in order.
36pub fn journal() -> Vec<JournalEntry> {
37 JOURNAL.with(|j| j.borrow().clone())
38}
39
40/// A FRESH WORLD, the document aside (its owner resets it: [`crate::semantics::dom::reset`]): an
41/// empty journal, and a browser opened for the first time — storage is what a page left on a
42/// visitor's machine, and a run that inherited a previous run's would make every test depend on
43/// the order the tests happened to run in.
44pub fn reset() {
45 JOURNAL.with(|j| j.borrow_mut().clear());
46 HOT_RELOAD.with(|f| f.set(false));
47 crate::semantics::storage::reset();
48}
49
50/// What a program run starts from: [`reset`] — a run of a program is a fresh process — unless a hot
51/// reload was armed ([`arm_hot_reload`]): then the journal starts again and the browser's storage,
52/// where preserved state lives, is kept.
53pub fn begin_run() {
54 if HOT_RELOAD.with(|f| f.replace(false)) {
55 JOURNAL.with(|j| j.borrow_mut().clear());
56 } else {
57 reset();
58 }
59}
60
61/// A HOT RELOAD: `largo serve` rebuilds a page when its file changes and runs it again, and the
62/// run it arms keeps the browser's storage — the form half filled in stays filled in — where a cold
63/// start would begin with nothing stored. Armed by whoever performs the reload, consumed by the
64/// next run.
65pub fn arm_hot_reload() {
66 HOT_RELOAD.with(|f| f.set(true));
67}
68
69/// A page world, taken out of the thread that held it: what [`PageWorld::take`] leaves a fresh one
70/// in place of and [`PageWorld::restore`] puts back.
71#[derive(Default)]
72pub struct PageWorld {
73 dom: crate::semantics::dom::DomState,
74 journal: Vec<JournalEntry>,
75 hot_reload: bool,
76 clock: crate::semantics::drive::ClockState,
77 testworld: Option<logicaffeine_system::testworld::TestWorld>,
78 storage: crate::semantics::storage::StorageState,
79}
80
81impl PageWorld {
82 /// This thread's world, leaving a fresh one in its place.
83 pub fn take() -> Self {
84 PageWorld {
85 dom: crate::semantics::dom::take(),
86 journal: JOURNAL.with(RefCell::take),
87 hot_reload: HOT_RELOAD.with(Cell::take),
88 clock: crate::semantics::drive::take(),
89 testworld: logicaffeine_system::testworld::take(),
90 storage: crate::semantics::storage::take(),
91 }
92 }
93
94 /// Install this world as this thread's.
95 pub fn restore(self) {
96 crate::semantics::dom::restore(self.dom);
97 JOURNAL.with(|j| *j.borrow_mut() = self.journal);
98 HOT_RELOAD.with(|f| f.set(self.hot_reload));
99 crate::semantics::drive::restore(self.clock);
100 logicaffeine_system::testworld::restore(self.testworld);
101 crate::semantics::storage::restore(self.storage);
102 }
103}