Skip to main content

yog/app/
boot.rs

1//! **Founding the model, and the one signal it sends the worker** (DESIGN §7.2).
2//!
3//! Split off [`super`] at §12's budget on the seam that root's own doc draws:
4//! the root declares what a frame owns, and this is how one is brought into
5//! being and how it reaches the derivation thread. Both members are the same
6//! subject read from its two ends — [`AppModel::boot`] builds the [`Deriver`]
7//! and hands the caller the pair, and [`AppModel::mark_dirty`] is the frame's
8//! *only* outbound signal to it — so the root stays declaration-light and
9//! carries no coverable `impl` header (the llvm-cov phantom this module tree is
10//! arranged around).
11
12use super::{AppModel, Deriver, Roots, Snapshot};
13use crate::projects::runner::BlRunner;
14use crate::state::{DirtySet, latest_snapshot, new_snapshot_cell};
15use crate::ui_state::{Clock, UiState};
16use crate::watch::Mark;
17use std::path::PathBuf;
18use std::sync::Arc;
19
20impl AppModel {
21    /// Build the model **and** the worker's [`Deriver`], taking the first
22    /// derivation synchronously so the engine answers real content from its
23    /// first gesture rather than an empty snapshot.
24    ///
25    /// Returned as a pair rather than spawned here: the thread is the caller's
26    /// ([`Engine::boot`](crate::engine::Engine::boot) hands it a wake hook; a
27    /// test drives `step()` by hand), and a model that spawned its own thread
28    /// could not be exercised deterministically. This is the same shape the
29    /// watch bridge always had.
30    pub fn boot(
31        roots: Roots,
32        clock: Arc<dyn Clock>,
33        balls: Box<dyn BlRunner>,
34        user: Option<String>,
35    ) -> (Self, Deriver) {
36        let ui = UiState::open(roots.ui_json());
37        let dirty = DirtySet::default();
38        let cell = new_snapshot_cell(Arc::new(Snapshot::empty(clock.unix())));
39        let mut deriver = Deriver::new(
40            roots.clone(),
41            Arc::clone(&clock),
42            balls,
43            dirty.clone(),
44            Arc::clone(&cell),
45        );
46        deriver.boot();
47        let snap = latest_snapshot(&cell);
48        let model = Self {
49            snap,
50            roots,
51            ui,
52            cell,
53            clock,
54            dirty,
55            identity_user: user,
56        };
57        (model, deriver)
58    }
59
60    /// Tell the worker a root changed (§7.2). The model's *only* outbound
61    /// signal: a dispatched verb names the root it touched and the worker's
62    /// ordinary routing does the rest, so there is no second path in.
63    pub(crate) fn mark_dirty<I: IntoIterator<Item = PathBuf>>(&self, roots: I) {
64        self.dirty
65            .mark_all(roots.into_iter().map(|r| (r, Mark::Watch)));
66    }
67}