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}