query_lang/database.rs
1//! The incremental engine: input storage, memoization, and validation.
2
3use alloc::collections::BTreeMap;
4use alloc::vec::Vec;
5use core::cell::{Cell, RefCell};
6use core::fmt;
7
8use crate::error::QueryError;
9use crate::revision::Revision;
10use crate::stats::Stats;
11use crate::system::System;
12
13/// A stored input: a value a consumer placed into the database directly.
14struct Input<V> {
15 value: V,
16 /// The revision at which this input last took a new value.
17 changed_at: Revision,
18}
19
20/// The memo table: derived key to its cached result and dependency record.
21type MemoMap<S> = BTreeMap<<S as System>::Key, Memo<<S as System>::Key, <S as System>::Value>>;
22
23/// A memoized derived query: its cached value, what it read, and when.
24struct Memo<K, V> {
25 value: V,
26 /// The queries this value was computed from, in read order. Re-examined to
27 /// decide whether a stale memo can be reused.
28 deps: Vec<K>,
29 /// The revision at which `value` last became a *different* value. Early
30 /// cutoff keeps this stamp old when a recomputation produces the same value.
31 changed_at: Revision,
32 /// The revision at which this memo was last confirmed current.
33 verified_at: Revision,
34}
35
36/// An incremental query database: the store of inputs and the cache of derived
37/// results, with automatic dependency tracking and invalidation.
38///
39/// This is the engine. A consumer defines its queries once by implementing
40/// [`System`], hands the system to [`new`](Self::new), seeds the base facts with
41/// [`set`](Self::set), and asks for results with [`get`](Self::get). Everything
42/// between — remembering what each query read, noticing when an input makes a
43/// cached result stale, recomputing only what actually changed — is the
44/// database's job.
45///
46/// # How it stays correct and fast
47///
48/// The database holds a [`Revision`] clock that advances by one each time an
49/// input takes a new value. Every cached query records two stamps: when it was
50/// last *verified* against the clock, and when its value last *changed*. Asking
51/// for a query takes one of three paths, counted in [`Stats`]:
52///
53/// - **Hit** — the query was already verified at the current revision; its value
54/// is returned without touching its dependencies.
55/// - **Validated** — the query is stale, but re-examining its recorded
56/// dependencies shows none of them changed since the query was last verified,
57/// so the cached value is reused. When a dependency *did* recompute but to the
58/// same value, its change stamp stays old and dependents are validated rather
59/// than recomputed. This *early cutoff* is what stops a local edit from
60/// cascading through the whole graph.
61/// - **Computed** — a genuine miss, or a dependency that truly changed; the
62/// query's [`compute`](System::compute) runs and the new value is cached.
63///
64/// Because dependencies are recorded during computation rather than declared up
65/// front, a query that branches on its inputs is tracked exactly: it depends on
66/// what it actually read on the last run, and nothing more.
67///
68/// # Single-threaded by design
69///
70/// A `Database` is not `Sync`: query resolution walks a shared cache and a
71/// dependency stack through interior mutability, which is correct and allocation-
72/// light on one thread and carries no atomic overhead. Drive one database from
73/// one thread; run independent databases on separate threads for parallelism.
74///
75/// # Examples
76///
77/// A three-layer computation — an input, a query over it, and a query over that
78/// — recomputes only along the path an edit touches:
79///
80/// ```
81/// use query_lang::{Database, System, QueryError};
82///
83/// #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
84/// enum Key {
85/// Width, // an input
86/// Doubled, // = Width * 2
87/// Labeled, // = "w=" + Doubled
88/// }
89///
90/// struct Layout;
91/// impl System for Layout {
92/// type Key = Key;
93/// type Value = String;
94/// fn compute(&self, db: &Database<Self>, key: &Key) -> Result<String, QueryError> {
95/// match key {
96/// Key::Width => Ok("0".into()),
97/// Key::Doubled => {
98/// let w: i64 = db.get(&Key::Width)?.parse().unwrap_or(0);
99/// Ok((w * 2).to_string())
100/// }
101/// Key::Labeled => Ok(format!("w={}", db.get(&Key::Doubled)?)),
102/// }
103/// }
104/// }
105///
106/// let mut db = Database::new(Layout);
107/// db.set(Key::Width, "10".into());
108/// assert_eq!(db.get(&Key::Labeled)?, "w=20");
109/// assert_eq!(db.stats().computed, 2); // Width is a set input; Doubled and Labeled ran
110///
111/// // Re-ask without changing anything: a free hit, no recomputation.
112/// assert_eq!(db.get(&Key::Labeled)?, "w=20");
113/// assert_eq!(db.stats().hits, 1);
114/// # Ok::<(), QueryError>(())
115/// ```
116pub struct Database<S: System> {
117 system: S,
118 revision: Revision,
119 inputs: BTreeMap<S::Key, Input<S::Value>>,
120 memos: RefCell<MemoMap<S>>,
121 /// Dependency-collection stack. Each active `compute` pushes a frame; every
122 /// [`get`](Self::get) call appends the key it read to the top frame.
123 frames: RefCell<Vec<Vec<S::Key>>>,
124 /// The keys whose `compute` is currently in progress, for cycle detection.
125 active: RefCell<Vec<S::Key>>,
126 computed: Cell<u64>,
127 validated: Cell<u64>,
128 hits: Cell<u64>,
129}
130
131impl<S: System> Database<S> {
132 /// Create an empty database for the given query system.
133 ///
134 /// The database starts at the initial revision with no inputs and an empty
135 /// cache. Seed inputs with [`set`](Self::set) before asking for derived
136 /// queries.
137 ///
138 /// # Examples
139 ///
140 /// ```
141 /// use query_lang::{Database, System, QueryError};
142 ///
143 /// struct S;
144 /// impl System for S {
145 /// type Key = u32;
146 /// type Value = u32;
147 /// fn compute(&self, _db: &Database<Self>, k: &u32) -> Result<u32, QueryError> { Ok(*k) }
148 /// }
149 ///
150 /// let db = Database::new(S);
151 /// assert_eq!(db.stats().total(), 0);
152 /// ```
153 #[must_use]
154 pub fn new(system: S) -> Self {
155 Self {
156 system,
157 revision: Revision::START,
158 inputs: BTreeMap::new(),
159 memos: RefCell::new(BTreeMap::new()),
160 frames: RefCell::new(Vec::new()),
161 active: RefCell::new(Vec::new()),
162 computed: Cell::new(0),
163 validated: Cell::new(0),
164 hits: Cell::new(0),
165 }
166 }
167
168 /// Set an input to a value, advancing the revision if the value changed.
169 ///
170 /// This is the only way a value enters the database from outside. Once set, a
171 /// key is an input: [`get`](Self::get) returns the stored value directly and
172 /// [`compute`](System::compute) is never called for it. Setting the *same*
173 /// value a key already holds is a no-op — the revision does not advance, and
174 /// nothing that depends on the input is invalidated, so re-feeding unchanged
175 /// facts costs nothing downstream. Setting a *different* value advances the
176 /// revision, which is what later marks dependent queries stale.
177 ///
178 /// Setting a key that previously held a derived (computed) value promotes it
179 /// to an input and discards the stale cached result.
180 ///
181 /// Taking `&mut self` is deliberate: mutating an input is the one operation
182 /// that can invalidate cached results, so it is kept distinct from the shared
183 /// `&self` reads that [`get`](Self::get) performs.
184 ///
185 /// # Examples
186 ///
187 /// ```
188 /// use query_lang::{Database, System, QueryError};
189 ///
190 /// struct Echo;
191 /// impl System for Echo {
192 /// type Key = u32;
193 /// type Value = i64;
194 /// fn compute(&self, db: &Database<Self>, k: &u32) -> Result<i64, QueryError> {
195 /// Ok(db.get(&(k + 1))? + 1) // reads input at k+1
196 /// }
197 /// }
198 ///
199 /// let mut db = Database::new(Echo);
200 /// db.set(1, 41);
201 /// let r0 = db.revision();
202 ///
203 /// db.set(1, 41); // same value
204 /// assert_eq!(db.revision(), r0); // no change, clock still
205 ///
206 /// db.set(1, 99); // new value
207 /// assert!(db.revision() > r0); // clock advanced
208 /// ```
209 pub fn set(&mut self, key: S::Key, value: S::Value) {
210 // If this key cached a derived value, it is becoming an input; drop the
211 // stale memo so the input and a leftover cache entry can never disagree.
212 self.memos.get_mut().remove(&key);
213
214 match self.inputs.get_mut(&key) {
215 Some(existing) if existing.value == value => {
216 // Same value: not a real change. Leave the revision untouched so
217 // dependents stay valid.
218 }
219 Some(existing) => {
220 self.revision = self.revision.next();
221 existing.value = value;
222 existing.changed_at = self.revision;
223 }
224 None => {
225 self.revision = self.revision.next();
226 let changed_at = self.revision;
227 self.inputs.insert(key, Input { value, changed_at });
228 }
229 }
230 }
231
232 /// Resolve a query to its value, computing and caching it as needed.
233 ///
234 /// If `key` is a set input, its value is returned directly. Otherwise the
235 /// query is derived: a valid cached value is reused (a hit or an early-cutoff
236 /// validation), and only a real miss or a genuinely changed dependency runs
237 /// [`compute`](System::compute). Call this both from application code and,
238 /// from inside a `compute`, to read the queries a result depends on — reads
239 /// through `get` are exactly what the engine records as dependencies.
240 ///
241 /// # Errors
242 ///
243 /// Returns [`QueryError::Cycle`] if resolving `key` requires a value that is
244 /// still being computed further up the call chain — that is, if the query
245 /// graph has a cycle.
246 ///
247 /// # Examples
248 ///
249 /// ```
250 /// use query_lang::{Database, System, QueryError};
251 ///
252 /// struct Fib;
253 /// impl System for Fib {
254 /// type Key = u64;
255 /// type Value = u64;
256 /// fn compute(&self, db: &Database<Self>, n: &u64) -> Result<u64, QueryError> {
257 /// // Memoized Fibonacci: each fib(n) is computed once and cached.
258 /// if *n < 2 { return Ok(*n); }
259 /// Ok(db.get(&(n - 1))?.wrapping_add(db.get(&(n - 2))?))
260 /// }
261 /// }
262 ///
263 /// let db = Database::new(Fib);
264 /// assert_eq!(db.get(&50)?, 12586269025);
265 /// # Ok::<(), QueryError>(())
266 /// ```
267 pub fn get(&self, key: &S::Key) -> Result<S::Value, QueryError> {
268 // Record this read as a dependency of the query currently computing, if
269 // any. Reads made outside a `compute` (top-level queries) have no frame.
270 if let Some(frame) = self.frames.borrow_mut().last_mut() {
271 frame.push(key.clone());
272 }
273 let (value, _changed_at) = self.eval(key)?;
274 Ok(value)
275 }
276
277 /// The current revision of the database.
278 ///
279 /// Advances by one each time [`set`](Self::set) gives an input a new value.
280 /// Useful for asserting in tests that an operation did (or did not) change any
281 /// input, and for correlating cache behaviour with input edits in logs.
282 #[must_use]
283 #[inline]
284 pub const fn revision(&self) -> Revision {
285 self.revision
286 }
287
288 /// A snapshot of the cumulative resolution counters.
289 ///
290 /// See [`Stats`] for what each counter means. Snapshot before and after an
291 /// operation and subtract to measure exactly what that operation cost.
292 ///
293 /// # Examples
294 ///
295 /// ```
296 /// use query_lang::{Database, System, QueryError};
297 ///
298 /// struct S;
299 /// impl System for S {
300 /// type Key = u32;
301 /// type Value = u32;
302 /// fn compute(&self, _db: &Database<Self>, k: &u32) -> Result<u32, QueryError> { Ok(*k) }
303 /// }
304 ///
305 /// let db = Database::new(S);
306 /// let before = db.stats();
307 /// let _ = db.get(&5)?;
308 /// let after = db.stats();
309 /// assert_eq!(after.computed - before.computed, 1);
310 /// # Ok::<(), QueryError>(())
311 /// ```
312 #[must_use]
313 pub fn stats(&self) -> Stats {
314 Stats {
315 computed: self.computed.get(),
316 validated: self.validated.get(),
317 hits: self.hits.get(),
318 }
319 }
320
321 /// A shared reference to the query system backing this database.
322 #[must_use]
323 #[inline]
324 pub const fn system(&self) -> &S {
325 &self.system
326 }
327
328 /// Ensure `key` is current and return its value together with the revision at
329 /// which that value last changed.
330 ///
331 /// This is the resolution core. Unlike [`get`](Self::get) it records no
332 /// dependency — it is called both for the requested key and, during
333 /// revalidation, for each recorded dependency, and only genuine reads through
334 /// `get` should count as dependencies.
335 fn eval(&self, key: &S::Key) -> Result<(S::Value, Revision), QueryError> {
336 // Inputs are always current at their own change stamp.
337 if let Some(input) = self.inputs.get(key) {
338 return Ok((input.value.clone(), input.changed_at));
339 }
340
341 // Fast path: a memo already verified at the current revision.
342 {
343 let memos = self.memos.borrow();
344 if let Some(memo) = memos.get(key) {
345 if memo.verified_at == self.revision {
346 self.hits.set(self.hits.get().saturating_add(1));
347 return Ok((memo.value.clone(), memo.changed_at));
348 }
349 }
350 }
351
352 // Stale memo: snapshot what revalidation needs, then release the borrow —
353 // checking dependencies recurses back through `eval`, which borrows again.
354 let snapshot = self
355 .memos
356 .borrow()
357 .get(key)
358 .map(|m| (m.deps.clone(), m.verified_at, m.value.clone(), m.changed_at));
359
360 if let Some((deps, verified_at, value, changed_at)) = snapshot {
361 let mut inputs_changed = false;
362 for dep in &deps {
363 let (_dep_value, dep_changed_at) = self.eval(dep)?;
364 if dep_changed_at > verified_at {
365 inputs_changed = true;
366 break;
367 }
368 }
369 if !inputs_changed {
370 // Early cutoff: nothing this query read actually changed. Reuse
371 // the cached value; only its verification stamp moves forward.
372 if let Some(memo) = self.memos.borrow_mut().get_mut(key) {
373 memo.verified_at = self.revision;
374 }
375 self.validated.set(self.validated.get().saturating_add(1));
376 return Ok((value, changed_at));
377 }
378 return self.recompute(key, Some((value, changed_at)));
379 }
380
381 // No memo at all: compute from scratch.
382 self.recompute(key, None)
383 }
384
385 /// Run `compute` for `key`, tracking the dependencies it reads and caching
386 /// the result. `previous`, when present, is the memo's prior value and change
387 /// stamp, used for early cutoff.
388 fn recompute(
389 &self,
390 key: &S::Key,
391 previous: Option<(S::Value, Revision)>,
392 ) -> Result<(S::Value, Revision), QueryError> {
393 // Cycle detection: re-entering a key already on the active stack means
394 // the query graph is cyclic.
395 if self.active.borrow().iter().any(|active| active == key) {
396 return Err(QueryError::Cycle);
397 }
398
399 self.active.borrow_mut().push(key.clone());
400 self.frames.borrow_mut().push(Vec::new());
401
402 let result = self.system.compute(self, key);
403
404 // Unwind the bookkeeping stacks whether compute succeeded or failed.
405 let deps = self.frames.borrow_mut().pop().unwrap_or_default();
406 self.active.borrow_mut().pop();
407
408 let value = result?;
409
410 // Early cutoff: if the recomputed value equals the old one, keep the old
411 // change stamp so dependents see "unchanged" and are not recomputed.
412 let changed_at = match previous {
413 Some((old_value, old_changed_at)) if old_value == value => old_changed_at,
414 _ => self.revision,
415 };
416
417 self.computed.set(self.computed.get().saturating_add(1));
418 let memo = Memo {
419 value: value.clone(),
420 deps,
421 changed_at,
422 verified_at: self.revision,
423 };
424 self.memos.borrow_mut().insert(key.clone(), memo);
425
426 Ok((value, changed_at))
427 }
428}
429
430impl<S: System> fmt::Debug for Database<S> {
431 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432 f.debug_struct("Database")
433 .field("revision", &self.revision)
434 .field("inputs", &self.inputs.len())
435 .field("memos", &self.memos.borrow().len())
436 .field("stats", &self.stats())
437 .finish()
438 }
439}
440
441#[cfg(test)]
442#[allow(clippy::unwrap_used)]
443mod tests {
444 use super::*;
445 use alloc::string::{String, ToString};
446 use core::cell::Cell as StdCell;
447
448 /// A system whose `compute` counts how many times each key ran, so tests can
449 /// assert exactly which queries recomputed.
450 struct Counting {
451 // key -> number of times computed
452 runs: StdCell<[u32; 8]>,
453 }
454
455 impl Counting {
456 fn new() -> Self {
457 Self {
458 runs: StdCell::new([0; 8]),
459 }
460 }
461
462 fn runs_of(&self, key: usize) -> u32 {
463 self.runs.get()[key]
464 }
465
466 fn bump(&self, key: usize) {
467 let mut r = self.runs.get();
468 r[key] += 1;
469 self.runs.set(r);
470 }
471 }
472
473 // Key layout: 0 = input A, 1 = input B, 2 = A*10, 3 = (A*10)+(B), 4 = sign of 2
474 impl System for Counting {
475 type Key = usize;
476 type Value = i64;
477 fn compute(&self, db: &Database<Self>, key: &usize) -> Result<i64, QueryError> {
478 self.bump(*key);
479 match key {
480 0 | 1 => Ok(0), // default when the input was never set
481 2 => Ok(db.get(&0)? * 10),
482 3 => Ok(db.get(&2)? + db.get(&1)?),
483 4 => Ok(db.get(&2)?.signum()),
484 _ => Ok(0),
485 }
486 }
487 }
488
489 #[test]
490 fn test_input_get_returns_set_value() {
491 let mut db = Database::new(Counting::new());
492 db.set(0, 7);
493 assert_eq!(db.get(&0).unwrap(), 7);
494 // Inputs never invoke compute.
495 assert_eq!(db.system().runs_of(0), 0);
496 }
497
498 #[test]
499 fn test_first_get_computes_once() {
500 let mut db = Database::new(Counting::new());
501 db.set(0, 3);
502 assert_eq!(db.get(&2).unwrap(), 30);
503 assert_eq!(db.system().runs_of(2), 1);
504 // Only key 2 computed; key 0 is a set input and never runs compute.
505 assert_eq!(db.stats().computed, 1);
506 }
507
508 #[test]
509 fn test_second_get_is_a_hit() {
510 let mut db = Database::new(Counting::new());
511 db.set(0, 3);
512 assert_eq!(db.get(&2).unwrap(), 30);
513 let before = db.stats();
514 assert_eq!(db.get(&2).unwrap(), 30);
515 let after = db.stats();
516 assert_eq!(after.hits - before.hits, 1);
517 assert_eq!(after.computed, before.computed); // no recompute
518 }
519
520 #[test]
521 fn test_input_change_recomputes_dependents() {
522 let mut db = Database::new(Counting::new());
523 db.set(0, 3);
524 assert_eq!(db.get(&2).unwrap(), 30);
525 db.set(0, 4);
526 assert_eq!(db.get(&2).unwrap(), 40);
527 assert_eq!(db.system().runs_of(2), 2);
528 }
529
530 #[test]
531 fn test_unchanged_input_set_does_not_recompute() {
532 let mut db = Database::new(Counting::new());
533 db.set(0, 3);
534 assert_eq!(db.get(&2).unwrap(), 30);
535 db.set(0, 3); // same value -> no revision bump
536 assert_eq!(db.get(&2).unwrap(), 30);
537 // Still verified at the current revision: a hit, not a recompute.
538 assert_eq!(db.system().runs_of(2), 1);
539 }
540
541 #[test]
542 fn test_multilayer_hit_after_no_change() {
543 let mut db = Database::new(Counting::new());
544 db.set(0, 3);
545 // key 4 = signum(A*10); depends on key 2, which depends on input 0.
546 assert_eq!(db.get(&4).unwrap(), 1);
547 assert_eq!(db.system().runs_of(4), 1);
548 assert_eq!(db.system().runs_of(2), 1);
549
550 // Re-setting the input to the same value does not advance the revision,
551 // so the next query is a plain hit and nothing recomputes.
552 db.set(0, 3);
553 assert_eq!(db.get(&4).unwrap(), 1);
554 assert_eq!(db.system().runs_of(4), 1);
555 assert_eq!(db.system().runs_of(2), 1);
556 }
557
558 #[test]
559 fn test_early_cutoff_when_intermediate_value_is_stable() {
560 // A system where an input change leaves an intermediate query's value
561 // unchanged, so the top query is validated, not recomputed.
562 struct AbsChain {
563 top_runs: StdCell<u32>,
564 }
565 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
566 enum K {
567 In,
568 Abs,
569 Plus1,
570 }
571 impl System for AbsChain {
572 type Key = K;
573 type Value = i64;
574 fn compute(&self, db: &Database<Self>, key: &K) -> Result<i64, QueryError> {
575 match key {
576 K::In => Ok(0),
577 K::Abs => Ok(db.get(&K::In)?.abs()),
578 K::Plus1 => {
579 self.top_runs.set(self.top_runs.get() + 1);
580 Ok(db.get(&K::Abs)? + 1)
581 }
582 }
583 }
584 }
585
586 let mut db = Database::new(AbsChain {
587 top_runs: StdCell::new(0),
588 });
589 db.set(K::In, 5);
590 assert_eq!(db.get(&K::Plus1).unwrap(), 6);
591 assert_eq!(db.system().top_runs.get(), 1);
592
593 // -5 and 5 have the same abs, so Abs recomputes to the same value and
594 // Plus1 is validated (early cutoff), not recomputed.
595 db.set(K::In, -5);
596 assert_eq!(db.get(&K::Plus1).unwrap(), 6);
597 assert_eq!(db.system().top_runs.get(), 1);
598 assert!(db.stats().validated >= 1);
599 }
600
601 #[test]
602 fn test_default_value_when_input_unset() {
603 let db = Database::new(Counting::new());
604 // Input 0 was never set; compute(0) returns its default of 0.
605 assert_eq!(db.get(&2).unwrap(), 0);
606 }
607
608 #[test]
609 fn test_direct_self_cycle_is_error() {
610 struct SelfDep;
611 impl System for SelfDep {
612 type Key = u32;
613 type Value = u32;
614 fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
615 db.get(k)
616 }
617 }
618 let db = Database::new(SelfDep);
619 assert_eq!(db.get(&1), Err(QueryError::Cycle));
620 }
621
622 #[test]
623 fn test_indirect_cycle_is_error() {
624 struct PingPong;
625 impl System for PingPong {
626 type Key = u32;
627 type Value = u32;
628 fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
629 // 0 depends on 1, 1 depends on 0.
630 match k {
631 0 => db.get(&1),
632 _ => db.get(&0),
633 }
634 }
635 }
636 let db = Database::new(PingPong);
637 assert_eq!(db.get(&0), Err(QueryError::Cycle));
638 }
639
640 #[test]
641 fn test_state_is_clean_after_cycle_error() {
642 struct SelfDep;
643 impl System for SelfDep {
644 type Key = u32;
645 type Value = u32;
646 fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
647 if *k == 0 { db.get(&0) } else { Ok(*k) }
648 }
649 }
650 let db = Database::new(SelfDep);
651 assert_eq!(db.get(&0), Err(QueryError::Cycle));
652 // A non-cyclic query still resolves normally afterwards.
653 assert_eq!(db.get(&9).unwrap(), 9);
654 }
655
656 #[test]
657 fn test_set_promotes_derived_key_to_input() {
658 let mut db = Database::new(Counting::new());
659 db.set(0, 3);
660 assert_eq!(db.get(&2).unwrap(), 30); // key 2 derived and cached
661 db.set(2, 999); // promote key 2 to an input
662 assert_eq!(db.get(&2).unwrap(), 999);
663 // It is now an input: compute is never called for it again.
664 let runs_before = db.system().runs_of(2);
665 assert_eq!(db.get(&2).unwrap(), 999);
666 assert_eq!(db.system().runs_of(2), runs_before);
667 }
668
669 #[test]
670 fn test_branching_dependencies_tracked_precisely() {
671 // A query reads one of two inputs depending on a selector input; only the
672 // branch it actually took is a dependency.
673 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
674 enum K {
675 Select,
676 Left,
677 Right,
678 Picked,
679 }
680 struct Switch {
681 runs: StdCell<u32>,
682 }
683 impl System for Switch {
684 type Key = K;
685 type Value = i64;
686 fn compute(&self, db: &Database<Self>, key: &K) -> Result<i64, QueryError> {
687 match key {
688 K::Select | K::Left | K::Right => Ok(0),
689 K::Picked => {
690 self.runs.set(self.runs.get() + 1);
691 if db.get(&K::Select)? == 0 {
692 db.get(&K::Left)
693 } else {
694 db.get(&K::Right)
695 }
696 }
697 }
698 }
699 }
700 let mut db = Database::new(Switch {
701 runs: StdCell::new(0),
702 });
703 db.set(K::Select, 0);
704 db.set(K::Left, 100);
705 db.set(K::Right, 200);
706 assert_eq!(db.get(&K::Picked).unwrap(), 100);
707 assert_eq!(db.system().runs.get(), 1);
708
709 // Changing Right must NOT recompute Picked: it read Left, not Right.
710 db.set(K::Right, 999);
711 assert_eq!(db.get(&K::Picked).unwrap(), 100);
712 assert_eq!(db.system().runs.get(), 1);
713
714 // Changing Left DOES recompute Picked.
715 db.set(K::Left, 111);
716 assert_eq!(db.get(&K::Picked).unwrap(), 111);
717 assert_eq!(db.system().runs.get(), 2);
718 }
719
720 #[test]
721 fn test_string_values_work() {
722 struct Greeter;
723 impl System for Greeter {
724 type Key = u32;
725 type Value = String;
726 fn compute(&self, db: &Database<Self>, k: &u32) -> Result<String, QueryError> {
727 if *k == 0 {
728 Ok("world".to_string())
729 } else {
730 Ok(alloc::format!("hello, {}", db.get(&0)?))
731 }
732 }
733 }
734 let db = Database::new(Greeter);
735 assert_eq!(db.get(&1).unwrap(), "hello, world");
736 }
737
738 #[test]
739 fn test_debug_reports_shape() {
740 let mut db = Database::new(Counting::new());
741 db.set(0, 1);
742 let _ = db.get(&2).unwrap();
743 let rendered = alloc::format!("{db:?}");
744 assert!(rendered.contains("revision"));
745 assert!(rendered.contains("memos"));
746 }
747
748 #[test]
749 fn test_diamond_recomputes_shared_dep_once() {
750 // top depends on left and right, both of which depend on a shared input.
751 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
752 enum K {
753 In,
754 Left,
755 Right,
756 Top,
757 }
758 struct Diamond {
759 in_runs: StdCell<u32>,
760 }
761 impl System for Diamond {
762 type Key = K;
763 type Value = i64;
764 fn compute(&self, db: &Database<Self>, key: &K) -> Result<i64, QueryError> {
765 match key {
766 K::In => Ok(0),
767 K::Left => Ok(db.get(&K::In)? + 1),
768 K::Right => Ok(db.get(&K::In)? + 2),
769 K::Top => {
770 self.in_runs.set(self.in_runs.get() + 1);
771 Ok(db.get(&K::Left)? + db.get(&K::Right)?)
772 }
773 }
774 }
775 }
776 let mut db = Database::new(Diamond {
777 in_runs: StdCell::new(0),
778 });
779 db.set(K::In, 10);
780 assert_eq!(db.get(&K::Top).unwrap(), (10 + 1) + (10 + 2));
781 assert_eq!(db.system().in_runs.get(), 1);
782
783 db.set(K::In, 20);
784 assert_eq!(db.get(&K::Top).unwrap(), (20 + 1) + (20 + 2));
785 assert_eq!(db.system().in_runs.get(), 2);
786 }
787
788 #[test]
789 fn test_multiple_deps_each_invalidate() {
790 // Guards against a regression where deps are dropped: a query with two
791 // input deps must be invalidated by either.
792 let mut db = Database::new(Counting::new());
793 db.set(0, 1);
794 db.set(1, 2);
795 // key 3 = (A*10) + B = 10 + 2 = 12
796 assert_eq!(db.get(&3).unwrap(), 12);
797 db.set(1, 5);
798 assert_eq!(db.get(&3).unwrap(), 15);
799 // And the other dependency still invalidates it too.
800 db.set(0, 2); // A*10 becomes 20
801 assert_eq!(db.get(&3).unwrap(), 25);
802 }
803}