blockworx/document/model.rs
1//! The flat document model.
2//!
3//! A [`Document`] is a single map of every block keyed by a globally-unique
4//! [`RectId`], plus the [`top_id`](Document::top_id) of the root block. It
5//! replaces the old nested `Block` tree: a [`Block`] now records its children
6//! only by id (see [`Block::children`]), and those children live here in
7//! [`Document::blocks`]. The on-disk form is therefore flat and hand-editable —
8//! every block is a top-level entry under its id, listing its children by id.
9
10use std::ops::{Deref, DerefMut};
11use std::sync::atomic::{AtomicU64, Ordering};
12
13use super::Block;
14use crate::presentation::store::{IdMap, IdMapExt};
15use crate::store::RectId;
16
17/// Identifies one *value* of the block map. A fresh stamp is minted process-wide
18/// on every mutable access, and a clone carries its source's stamp, so equal
19/// generations imply identical content. Caches derived from the document (the
20/// spatial index) compare it instead of relying on callers to invalidate them.
21#[derive(Clone, Copy, PartialEq, Eq, Debug)]
22pub struct Generation(u64);
23
24impl Generation {
25 fn next() -> Self {
26 static NEXT: AtomicU64 = AtomicU64::new(0);
27 Self(NEXT.fetch_add(1, Ordering::Relaxed))
28 }
29}
30
31/// The document's block map. Handing out `&mut` — the only way to reach any
32/// block, and therefore any shape or route — is what stamps a new
33/// [`Generation`], so a mutation cannot fail to be observable to a cache.
34#[derive(Clone, Debug)]
35pub struct Blocks {
36 map: IdMap<RectId, Block>,
37 generation: Generation,
38}
39
40impl Blocks {
41 pub fn generation(&self) -> Generation {
42 self.generation
43 }
44}
45
46/// Content equality only: two maps holding the same blocks are equal whether or
47/// not they were reached through a mutable borrow (undo history compares states
48/// for change, and a borrow that changed nothing is not a change).
49impl PartialEq for Blocks {
50 fn eq(&self, other: &Self) -> bool {
51 self.map == other.map
52 }
53}
54
55impl Default for Blocks {
56 fn default() -> Self {
57 IdMap::default().into()
58 }
59}
60
61impl From<IdMap<RectId, Block>> for Blocks {
62 fn from(map: IdMap<RectId, Block>) -> Self {
63 Self {
64 map,
65 generation: Generation::next(),
66 }
67 }
68}
69
70impl Deref for Blocks {
71 type Target = IdMap<RectId, Block>;
72 fn deref(&self) -> &Self::Target {
73 &self.map
74 }
75}
76
77impl DerefMut for Blocks {
78 fn deref_mut(&mut self) -> &mut Self::Target {
79 self.generation = Generation::next();
80 &mut self.map
81 }
82}
83
84/// The whole document: a flat, globally-keyed map of blocks plus the root id.
85#[derive(Clone, Debug, PartialEq)]
86pub struct Document {
87 /// Display name, when the document carries one of its own. `None` falls back
88 /// to the name of the directory or file holding it. Part of the document (and
89 /// so of the undo state) because a rename is an edit, not a view setting.
90 pub name: Option<String>,
91 /// Id of the root (top) block within [`Self::blocks`].
92 pub top_id: RectId,
93 /// Every block in the document, keyed by its globally-unique id. Parent →
94 /// child links are recorded in each block's [`Block::children`] set, not by
95 /// nesting.
96 pub blocks: Blocks,
97}
98
99impl Default for Document {
100 fn default() -> Self {
101 // One empty "top" block, as the old single-`Block` default was.
102 let mut blocks = Blocks::default();
103 let top_id = blocks.insert_value(Block::default());
104 Self {
105 name: None,
106 top_id,
107 blocks,
108 }
109 }
110}
111
112impl Document {
113 /// The stamp of the current block map (see [`Generation`]).
114 pub fn generation(&self) -> Generation {
115 self.blocks.generation()
116 }
117
118 /// Insert `block` into the flat map under a fresh document-global id and
119 /// record it as a child of `parent` (appended after `parent`'s existing
120 /// children), returning the new id. The parent link is skipped if `parent`
121 /// is not present.
122 pub fn add_child(&mut self, parent: RectId, block: Block) -> RectId {
123 let id = self.blocks.insert_value(block);
124 if let Some(p) = self.blocks.get_mut(&parent) {
125 p.children.insert(id);
126 }
127 id
128 }
129
130 pub fn block(&self, id: RectId) -> Option<&Block> {
131 self.blocks.get(&id)
132 }
133 pub fn block_mut(&mut self, id: RectId) -> Option<&mut Block> {
134 self.blocks.get_mut(&id)
135 }
136
137 /// Iterate block `id`'s children (id + `Block`) in stored order, resolving
138 /// each child id through the flat map and skipping any dangling id (so a
139 /// hand-edited document degrades gracefully).
140 pub fn child_blocks(&self, id: RectId) -> impl Iterator<Item = (RectId, &Block)> + '_ {
141 self.blocks.get(&id).into_iter().flat_map(move |b| {
142 b.children
143 .iter()
144 .filter_map(move |&cid| self.blocks.get(&cid).map(|cb| (cid, cb)))
145 })
146 }
147
148 /// All transitive descendant block ids of `id` (excluding `id` itself). Used
149 /// to remove or copy a whole subtree from the flat map.
150 pub fn descendants(&self, id: RectId) -> Vec<RectId> {
151 let mut out = Vec::new();
152 let mut stack: Vec<RectId> = self
153 .blocks
154 .get(&id)
155 .map(|b| b.children.iter().copied().collect())
156 .unwrap_or_default();
157 while let Some(cid) = stack.pop() {
158 out.push(cid);
159 if let Some(b) = self.blocks.get(&cid) {
160 stack.extend(b.children.iter().copied());
161 }
162 }
163 out
164 }
165}