formualizer_eval/engine/
debug_views.rs1use super::vertex::{VertexId, VertexKind};
2use super::vertex_store::VertexStore;
3use std::fmt;
4
5pub struct VertexView<'s> {
9 store: &'s VertexStore,
10 id: VertexId,
11}
12
13impl<'s> VertexView<'s> {
14 #[inline]
15 pub fn new(store: &'s VertexStore, id: VertexId) -> Self {
16 Self { store, id }
17 }
18
19 #[inline]
20 pub fn id(&self) -> VertexId {
21 self.id
22 }
23
24 #[inline]
25 pub fn row(&self) -> u32 {
26 self.store.coord(self.id).row()
27 }
28
29 #[inline]
30 pub fn col(&self) -> u32 {
31 self.store.coord(self.id).col()
32 }
33
34 #[inline]
35 pub fn sheet_id(&self) -> u16 {
36 self.store.sheet_id(self.id)
37 }
38
39 #[inline]
40 pub fn is_dirty(&self) -> bool {
41 self.store.is_dirty(self.id)
42 }
43
44 #[inline]
45 pub fn is_volatile(&self) -> bool {
46 self.store.is_volatile(self.id)
47 }
48
49 #[inline]
50 pub fn is_deleted(&self) -> bool {
51 self.store.is_deleted(self.id)
52 }
53
54 #[inline]
55 pub fn kind(&self) -> VertexKind {
56 self.store.kind(self.id)
57 }
58
59 #[inline]
60 pub fn value_ref(&self) -> u32 {
61 self.store.value_ref(self.id)
62 }
63
64 #[inline]
65 pub fn edge_offset(&self) -> u32 {
66 self.store.edge_offset(self.id)
67 }
68
69 #[inline]
70 pub fn flags(&self) -> u8 {
71 self.store.flags(self.id)
72 }
73}
74
75impl<'s> fmt::Debug for VertexView<'s> {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 f.debug_struct("VertexView")
78 .field("id", &self.id)
79 .field("row", &self.row())
80 .field("col", &self.col())
81 .field("sheet_id", &self.sheet_id())
82 .field("kind", &self.kind())
83 .field("dirty", &self.is_dirty())
84 .field("volatile", &self.is_volatile())
85 .field("deleted", &self.is_deleted())
86 .field("value_ref", &format!("0x{:08x}", self.value_ref()))
87 .field("edge_offset", &self.edge_offset())
88 .finish()
89 }
90}
91
92impl<'s> fmt::Display for VertexView<'s> {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 use crate::reference::Coord;
96 let col_letters = Coord::col_to_letters(self.col());
97 let row_1based = self.row() + 1;
98
99 if self.sheet_id() == 0 {
100 write!(f, "{col_letters}{row_1based}")
101 } else {
102 write!(f, "Sheet{}!{}{}", self.sheet_id(), col_letters, row_1based)
103 }
104 }
105}
106
107pub struct VertexViewMut<'s> {
109 store: &'s mut VertexStore,
110 id: VertexId,
111}
112
113impl<'s> VertexViewMut<'s> {
114 #[inline]
115 pub fn new(store: &'s mut VertexStore, id: VertexId) -> Self {
116 Self { store, id }
117 }
118
119 #[inline]
120 pub fn id(&self) -> VertexId {
121 self.id
122 }
123
124 #[inline]
126 pub fn row(&self) -> u32 {
127 self.store.coord(self.id).row()
128 }
129
130 #[inline]
131 pub fn col(&self) -> u32 {
132 self.store.coord(self.id).col()
133 }
134
135 #[inline]
136 pub fn sheet_id(&self) -> u16 {
137 self.store.sheet_id(self.id)
138 }
139
140 #[inline]
141 pub fn is_dirty(&self) -> bool {
142 self.store.is_dirty(self.id)
143 }
144
145 #[inline]
146 pub fn is_volatile(&self) -> bool {
147 self.store.is_volatile(self.id)
148 }
149
150 #[inline]
151 pub fn kind(&self) -> VertexKind {
152 self.store.kind(self.id)
153 }
154
155 #[inline]
157 pub fn set_kind(&mut self, kind: VertexKind) {
158 self.store.set_kind(self.id, kind);
159 }
160
161 #[inline]
162 pub fn set_dirty(&mut self, dirty: bool) {
163 self.store.set_dirty(self.id, dirty);
164 }
165
166 #[inline]
167 pub fn set_volatile(&mut self, volatile: bool) {
168 self.store.set_volatile(self.id, volatile);
169 }
170
171 #[inline]
172 pub fn set_value_ref(&mut self, value_ref: u32) {
173 self.store.set_value_ref(self.id, value_ref);
174 }
175
176 #[inline]
177 pub fn set_edge_offset(&mut self, offset: u32) {
178 self.store.set_edge_offset(self.id, offset);
179 }
180}
181
182impl<'s> fmt::Debug for VertexViewMut<'s> {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 f.debug_struct("VertexViewMut")
185 .field("id", &self.id)
186 .field("row", &self.row())
187 .field("col", &self.col())
188 .field("sheet_id", &self.sheet_id())
189 .field("kind", &self.kind())
190 .field("dirty", &self.is_dirty())
191 .field("volatile", &self.is_volatile())
192 .finish()
193 }
194}
195
196impl VertexStore {
198 #[inline]
200 pub fn view(&self, id: VertexId) -> VertexView {
201 VertexView::new(self, id)
202 }
203
204 #[inline]
206 pub fn view_mut(&mut self, id: VertexId) -> VertexViewMut {
207 VertexViewMut::new(self, id)
208 }
209
210 pub fn debug_vertex(&self, id: VertexId) -> String {
212 let view = self.view(id);
213 format!("{view:?}")
214 }
215
216 pub fn debug_range(&self, start: VertexId, count: usize) -> Vec<String> {
218 (0..count)
219 .map(|i| {
220 let id = VertexId(start.0 + i as u32);
221 self.debug_vertex(id)
222 })
223 .collect()
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use crate::engine::packed_coord::PackedCoord;
231
232 #[test]
233 fn test_vertex_view_access() {
234 let mut store = VertexStore::new();
235 let id = store.allocate(PackedCoord::new(5, 10), 2, 0x03);
236
237 let view = store.view(id);
238 assert_eq!(view.row(), 5);
239 assert_eq!(view.col(), 10);
240 assert_eq!(view.sheet_id(), 2);
241 assert!(view.is_dirty());
242 assert!(view.is_volatile());
243 }
244
245 #[test]
246 fn test_debug_output() {
247 let mut store = VertexStore::new();
248 let id = store.allocate(PackedCoord::new(1, 1), 0, 0x01);
249 store.set_kind(id, VertexKind::Cell);
250
251 let view = store.view(id);
252 let debug = format!("{view:?}");
253 assert!(debug.contains("row: 1"));
254 assert!(debug.contains("col: 1"));
255 assert!(debug.contains("Cell"));
256 }
257
258 #[test]
259 fn test_mutable_view() {
260 let mut store = VertexStore::new();
261 let id = store.allocate(PackedCoord::new(0, 0), 0, 0);
262
263 {
264 let mut view = store.view_mut(id);
265 view.set_dirty(true);
266 view.set_volatile(true);
267 view.set_value_ref(42);
268 }
269
270 assert!(store.is_dirty(id));
271 assert!(store.is_volatile(id));
272 assert_eq!(store.value_ref(id), 42);
273 }
274
275 #[test]
276 fn test_view_lifetime() {
277 let mut store = VertexStore::new();
278 let id1 = store.allocate(PackedCoord::new(0, 0), 0, 0);
279 let id2 = store.allocate(PackedCoord::new(1, 1), 0, 0);
280
281 let view1 = store.view(id1);
283 let view2 = store.view(id2);
284
285 assert_eq!(view1.row(), 0);
286 assert_eq!(view2.row(), 1);
287 }
288
289 #[test]
290 fn test_view_display() {
291 let mut store = VertexStore::new();
292 let id = store.allocate(PackedCoord::new(0, 5), 1, 0x01);
293 store.set_kind(id, VertexKind::Cell);
294
295 let view = store.view(id);
296 let display = format!("{view}");
297 assert!(display.contains("Sheet1!F1")); }
299
300 #[test]
301 fn test_zero_cost_abstraction() {
302 let mut store = VertexStore::new();
305 let id = store.allocate(PackedCoord::new(100, 200), 5, 0x07);
306
307 let view = store.view(id);
309
310 let row1 = view.row();
312 let row2 = view.row();
313 assert_eq!(row1, row2);
314 assert_eq!(row1, 100);
315
316 assert_eq!(view.col(), 200);
318 assert_eq!(view.sheet_id(), 5);
319 assert!(view.is_dirty());
320 assert!(view.is_volatile());
321 assert!(view.is_deleted());
322 }
323}