yog/app/mod.rs
1//! Top-level app state and the render entry points.
2//!
3//! [`Args`] is the bare binary's own interface; [`AppModel`] is what one
4//! running yog holds of its derivation, and since
5//! bl-ee0a that is deliberately very little: the `ui.json` document ([`UiState`]
6//! — durable pins/collapsed/seen, written through at the gesture), the
7//! per-instance [`Focus`] (RAM, §13.1), and an `Arc<`[`Snapshot`]`>` — the
8//! latest *completed* derivation. Rendering reads that snapshot and nothing
9//! else.
10//!
11//! **The frame derives nothing** (§7.2). Enumeration, the watchers, the dirty
12//! routing, both sweeps, every `GitTree` re-derivation, the ball and ops
13//! fetches and the liveness probes all live on [`Deriver`], which one
14//! [`Worker`](derive::worker::Worker) thread drives. The frame's per-frame duty
15//! is [`AppModel::refresh`]: take the newest snapshot if there is one, adopt an
16//! externally-changed `ui.json`, hold the §6 acknowledgement. When the frame
17//! *causes* a change (a dispatched verb, a toggled filter) it says so by
18//! marking the affected root dirty — the same vocabulary the watchers use, so
19//! there is one path into the derivation and no request channel beside it.
20//!
21//! The impl is split for the 300-line budget: [`derive`] holds the worker's
22//! pass, [`focus`] the tab-bar / conversation / attention / seen-acknowledgement
23//! surface, [`view`] the read surface a frame paints. This root stays
24//! declaration-light so the `pub mod` list carries no coverable `impl` header
25//! (llvm-cov phantom).
26
27mod balls;
28/// **Founding the model, and the one signal it sends the worker** (§7.2) — the
29/// pair a caller gets, the first derivation taken synchronously, and
30/// [`AppModel::mark_dirty`]. Split off this root at §12's budget on the seam
31/// the doc above already draws: what a frame *owns* is declared here, how it is
32/// brought into being and how it talks to the worker is there.
33mod boot;
34pub mod cadence;
35mod derive;
36mod dirty;
37mod drift;
38mod grace;
39mod roots;
40mod snapshot;
41/// The model's read surface (§7.2) — split from this root at the cap.
42mod view;
43
44use crate::binding::Workspace;
45use crate::fs_watcher::RootKind;
46use crate::state::{DirtySet, SnapshotCell};
47use crate::ui_state::{Clock, UiState};
48use clap::Parser;
49use std::path::PathBuf;
50use std::sync::Arc;
51
52pub use self::cadence::Cadence;
53pub use self::derive::Deriver;
54pub use self::derive::worker::Worker;
55/// The §7.2 instrumentation lines the `workspaces` answer carries (bl-b4b5):
56/// how late the derivation is, and what grew in it. Re-exported here because
57/// the boundary says them and this module derives them — one wording, read at
58/// the chokepoint rather than restated for a seat.
59pub use self::drift::stale_label;
60pub use self::grace::WoundGrace;
61pub use self::roots::Roots;
62/// The §3.1 enumeration standing in for the derivation's cached copy at the
63/// boundary's intake (bl-6c9e) — re-exported here because the engine builds a
64/// gesture's environment with it and this module owns the derivation.
65pub(crate) use self::snapshot::addressable;
66pub use self::snapshot::growth_label;
67pub use self::snapshot::{Growth, Snapshot};
68
69/// **The bare binary's own interface: a name, a version, and no flags**
70/// (bl-7942). It carried `--workspace`, the window's startup focus, and the
71/// focus went with the window (REMOTE §7). It stays a clap type so `--version`
72/// and the top-level `--help` header are rendered from the manifest rather
73/// than typed — the roster under that header is [`multiplex::help::COMMANDS`],
74/// which is where every verb yog answers to actually lives.
75#[derive(Parser, Debug, Clone, PartialEq, Eq)]
76#[command(
77 version,
78 about = "yog: the server for litany loops — the world, the balls and the conversations, \
79 behind one control boundary"
80)]
81pub struct Args {}
82
83/// **What one running yog holds of its own derivation** (§15 Y11, §7.2 as
84/// rewritten by bl-ee0a): the roots, the durable `ui.json`, and an
85/// `Arc<`[`Snapshot`]`>` — the latest *completed* derivation. Everything that
86/// keeps it fresh belongs to [`Deriver`] on another thread.
87///
88/// It was *what a frame owns* until bl-7942, and the fields that left were all
89/// one seat's: the focus, the optimistic echo folded over the derivation for
90/// paint, the window's wire channels, its follow lane and its act path. What a
91/// seat is looking at, and what it has fired and not yet heard back about, are
92/// the seat's own (REMOTE §7, §12) and cross the boundary as gestures.
93pub struct AppModel {
94 roots: Roots,
95 ui: UiState,
96 /// The worker's derivation, untouched — the one thing every read is over.
97 /// There is no second, optimistically-folded copy any more: the fold was
98 /// the paint path's, and §7.2 already said in as many words that *nothing
99 /// yog does, and nothing a reader is told, is ever decided by a fact that
100 /// is only optimistic*.
101 pub(crate) snap: Arc<Snapshot>,
102 /// Where the worker publishes.
103 cell: SnapshotCell,
104 /// The injected time source (§7.2), shared with the worker.
105 clock: Arc<dyn Clock>,
106 /// How a caller tells the worker it changed something (§7.2).
107 dirty: DirtySet,
108 /// `$USER` fallback for the claim identity (§4.1) — read from the env
109 /// snapshot at the process boundary, never live here (the xdg discipline).
110 identity_user: Option<String>,
111}
112
113/// The roots the model watches (§7.1): every enumerated workspace, the three
114/// enumeration roots (the flat names root, litany workspaces + replays), and the
115/// yog state root. Missing roots are tolerated —
116/// [`WatchSet::reconcile`](crate::watch::WatchSet::reconcile) skips one that fails to arm.
117fn desired_watches(roots: &Roots, workspaces: &[Workspace]) -> Vec<(PathBuf, RootKind)> {
118 let mut desired = vec![
119 (roots.names(), RootKind::NamesRoot),
120 (roots.workspaces(), RootKind::WorkspacesRoot),
121 (roots.replays(), RootKind::WorkspacesRoot),
122 (roots.yog_state.clone(), RootKind::YogState),
123 (roots.balls_clones.clone(), RootKind::BallsClones),
124 ];
125 desired.extend(
126 workspaces
127 .iter()
128 .map(|w| (w.path.clone(), RootKind::Workspace)),
129 );
130 desired
131}
132
133#[cfg(test)]
134mod tests;