Skip to main content

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}