Skip to main content

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}