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 currently being resolved, for cycle detection and the depth
125    /// limit. A key is on this stack while its `compute` runs and while its
126    /// recorded dependencies are being revalidated.
127    active: RefCell<Vec<S::Key>>,
128    /// The most keys that may be on the `active` stack at once, if limited.
129    max_depth: Option<usize>,
130    computed: Cell<u64>,
131    validated: Cell<u64>,
132    hits: Cell<u64>,
133}
134
135impl<S: System> Database<S> {
136    /// Create an empty database for the given query system.
137    ///
138    /// The database starts at the initial revision with no inputs and an empty
139    /// cache. Seed inputs with [`set`](Self::set) before asking for derived
140    /// queries.
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// use query_lang::{Database, System, QueryError};
146    ///
147    /// struct S;
148    /// impl System for S {
149    ///     type Key = u32;
150    ///     type Value = u32;
151    ///     fn compute(&self, _db: &Database<Self>, k: &u32) -> Result<u32, QueryError> { Ok(*k) }
152    /// }
153    ///
154    /// let db = Database::new(S);
155    /// assert_eq!(db.stats().total(), 0);
156    /// ```
157    #[must_use]
158    pub fn new(system: S) -> Self {
159        Self {
160            system,
161            revision: Revision::START,
162            inputs: BTreeMap::new(),
163            memos: RefCell::new(BTreeMap::new()),
164            frames: RefCell::new(Vec::new()),
165            active: RefCell::new(Vec::new()),
166            max_depth: None,
167            computed: Cell::new(0),
168            validated: Cell::new(0),
169            hits: Cell::new(0),
170        }
171    }
172
173    /// Limit how deep one resolution may go, and return the database.
174    ///
175    /// Every query that is computing or revalidating its dependencies takes one
176    /// level, and each level uses some native stack. A long chain of queries
177    /// that each read the next one can therefore overflow the thread's stack.
178    /// With a limit set, a [`get`](Self::get) that would go deeper than `depth`
179    /// levels returns [`QueryError::TooDeep`] instead. The database stays usable
180    /// afterwards.
181    ///
182    /// There is no limit by default. Pick one that fits the stack of the thread
183    /// that drives the database and the stack your `compute` uses per call.
184    ///
185    /// # Examples
186    ///
187    /// ```
188    /// use query_lang::{Database, System, QueryError};
189    ///
190    /// struct Chain;
191    /// impl System for Chain {
192    ///     type Key = u32;
193    ///     type Value = u32;
194    ///     fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
195    ///         if *k == 0 { Ok(0) } else { Ok(db.get(&(k - 1))? + 1) }
196    ///     }
197    /// }
198    ///
199    /// let db = Database::new(Chain).with_max_depth(200);
200    /// assert_eq!(db.get(&100), Ok(100));
201    /// assert_eq!(db.get(&5_000), Err(QueryError::TooDeep));
202    /// ```
203    #[must_use]
204    pub fn with_max_depth(mut self, depth: usize) -> Self {
205        self.max_depth = Some(depth);
206        self
207    }
208
209    /// The depth limit set with [`with_max_depth`](Self::with_max_depth), if any.
210    #[must_use]
211    #[inline]
212    pub const fn max_depth(&self) -> Option<usize> {
213        self.max_depth
214    }
215
216    /// Set an input to a value, advancing the revision if the value changed.
217    ///
218    /// This is the only way a value enters the database from outside. Once set, a
219    /// key is an input: [`get`](Self::get) returns the stored value directly and
220    /// [`compute`](System::compute) is never called for it. Setting the *same*
221    /// value a key already holds is a no-op — the revision does not advance, and
222    /// nothing that depends on the input is invalidated, so re-feeding unchanged
223    /// facts costs nothing downstream. Setting a *different* value advances the
224    /// revision, which is what later marks dependent queries stale.
225    ///
226    /// Setting a key that previously held a derived (computed) value promotes it
227    /// to an input and discards the stale cached result.
228    ///
229    /// Taking `&mut self` is deliberate: mutating an input is the one operation
230    /// that can invalidate cached results, so it is kept distinct from the shared
231    /// `&self` reads that [`get`](Self::get) performs.
232    ///
233    /// # Examples
234    ///
235    /// ```
236    /// use query_lang::{Database, System, QueryError};
237    ///
238    /// struct Echo;
239    /// impl System for Echo {
240    ///     type Key = u32;
241    ///     type Value = i64;
242    ///     fn compute(&self, db: &Database<Self>, k: &u32) -> Result<i64, QueryError> {
243    ///         Ok(db.get(&(k + 1))? + 1) // reads input at k+1
244    ///     }
245    /// }
246    ///
247    /// let mut db = Database::new(Echo);
248    /// db.set(1, 41);
249    /// let r0 = db.revision();
250    ///
251    /// db.set(1, 41);              // same value
252    /// assert_eq!(db.revision(), r0); // no change, clock still
253    ///
254    /// db.set(1, 99);              // new value
255    /// assert!(db.revision() > r0);   // clock advanced
256    /// ```
257    pub fn set(&mut self, key: S::Key, value: S::Value) {
258        // If this key cached a derived value, it is becoming an input; drop the
259        // stale memo so the input and a leftover cache entry can never disagree.
260        self.memos.get_mut().remove(&key);
261
262        match self.inputs.get_mut(&key) {
263            Some(existing) if existing.value == value => {
264                // Same value: not a real change. Leave the revision untouched so
265                // dependents stay valid.
266            }
267            Some(existing) => {
268                self.revision = self.revision.next();
269                existing.value = value;
270                existing.changed_at = self.revision;
271            }
272            None => {
273                self.revision = self.revision.next();
274                let changed_at = self.revision;
275                self.inputs.insert(key, Input { value, changed_at });
276            }
277        }
278    }
279
280    /// Resolve a query to its value, computing and caching it as needed.
281    ///
282    /// If `key` is a set input, its value is returned directly. Otherwise the
283    /// query is derived: a valid cached value is reused (a hit or an early-cutoff
284    /// validation), and only a real miss or a genuinely changed dependency runs
285    /// [`compute`](System::compute). Call this both from application code and,
286    /// from inside a `compute`, to read the queries a result depends on — reads
287    /// through `get` are exactly what the engine records as dependencies.
288    ///
289    /// # Errors
290    ///
291    /// Returns [`QueryError::Cycle`] if resolving `key` requires a value that is
292    /// still being computed further up the call chain — that is, if the query
293    /// graph has a cycle.
294    ///
295    /// # Examples
296    ///
297    /// ```
298    /// use query_lang::{Database, System, QueryError};
299    ///
300    /// struct Fib;
301    /// impl System for Fib {
302    ///     type Key = u64;
303    ///     type Value = u64;
304    ///     fn compute(&self, db: &Database<Self>, n: &u64) -> Result<u64, QueryError> {
305    ///         // Memoized Fibonacci: each fib(n) is computed once and cached.
306    ///         if *n < 2 { return Ok(*n); }
307    ///         Ok(db.get(&(n - 1))?.wrapping_add(db.get(&(n - 2))?))
308    ///     }
309    /// }
310    ///
311    /// let db = Database::new(Fib);
312    /// assert_eq!(db.get(&50)?, 12586269025);
313    /// # Ok::<(), QueryError>(())
314    /// ```
315    pub fn get(&self, key: &S::Key) -> Result<S::Value, QueryError> {
316        // Record this read as a dependency of the query currently computing, if
317        // any. Reads made outside a `compute` (top-level queries) have no frame.
318        if let Some(frame) = self.frames.borrow_mut().last_mut() {
319            frame.push(key.clone());
320        }
321        let (value, _changed_at) = self.eval(key)?;
322        Ok(value)
323    }
324
325    /// The current revision of the database.
326    ///
327    /// Advances by one each time [`set`](Self::set) gives an input a new value.
328    /// Useful for asserting in tests that an operation did (or did not) change any
329    /// input, and for correlating cache behaviour with input edits in logs.
330    #[must_use]
331    #[inline]
332    pub const fn revision(&self) -> Revision {
333        self.revision
334    }
335
336    /// A snapshot of the cumulative resolution counters.
337    ///
338    /// See [`Stats`] for what each counter means. Snapshot before and after an
339    /// operation and subtract to measure exactly what that operation cost.
340    ///
341    /// # Examples
342    ///
343    /// ```
344    /// use query_lang::{Database, System, QueryError};
345    ///
346    /// struct S;
347    /// impl System for S {
348    ///     type Key = u32;
349    ///     type Value = u32;
350    ///     fn compute(&self, _db: &Database<Self>, k: &u32) -> Result<u32, QueryError> { Ok(*k) }
351    /// }
352    ///
353    /// let db = Database::new(S);
354    /// let before = db.stats();
355    /// let _ = db.get(&5)?;
356    /// let after = db.stats();
357    /// assert_eq!(after.computed - before.computed, 1);
358    /// # Ok::<(), QueryError>(())
359    /// ```
360    #[must_use]
361    pub fn stats(&self) -> Stats {
362        Stats {
363            computed: self.computed.get(),
364            validated: self.validated.get(),
365            hits: self.hits.get(),
366        }
367    }
368
369    /// A shared reference to the query system backing this database.
370    #[must_use]
371    #[inline]
372    pub const fn system(&self) -> &S {
373        &self.system
374    }
375
376    /// Ensure `key` is current and return its value together with the revision at
377    /// which that value last changed.
378    ///
379    /// This is the resolution core. Unlike [`get`](Self::get) it records no
380    /// dependency — it is called both for the requested key and, during
381    /// revalidation, for each recorded dependency, and only genuine reads through
382    /// `get` should count as dependencies.
383    fn eval(&self, key: &S::Key) -> Result<(S::Value, Revision), QueryError> {
384        // Inputs are always current at their own change stamp.
385        if let Some(input) = self.inputs.get(key) {
386            return Ok((input.value.clone(), input.changed_at));
387        }
388
389        // Fast path: a memo already verified at the current revision.
390        {
391            let memos = self.memos.borrow();
392            if let Some(memo) = memos.get(key) {
393                if memo.verified_at == self.revision {
394                    self.hits.set(self.hits.get().saturating_add(1));
395                    return Ok((memo.value.clone(), memo.changed_at));
396                }
397            }
398        }
399
400        // Stale memo: snapshot what revalidation needs, then release the borrow —
401        // checking dependencies recurses back through `eval`, which borrows again.
402        let snapshot = self
403            .memos
404            .borrow()
405            .get(key)
406            .map(|m| (m.deps.clone(), m.verified_at, m.value.clone(), m.changed_at));
407
408        if let Some((deps, verified_at, value, changed_at)) = snapshot {
409            // The key stays on the active stack while its dependencies are
410            // checked. Memos can record dependencies that form a cycle (a query
411            // that caught a `Cycle` error from a dependency and fell back to a
412            // default), and without this mark revalidating them would recurse
413            // forever.
414            let inputs_changed = {
415                let _active = self.enter(key)?;
416                let mut changed = false;
417                for dep in &deps {
418                    match self.eval(dep) {
419                        Ok((_dep_value, dep_changed_at)) if dep_changed_at <= verified_at => {}
420                        // A dependency that changed, or that now fails, means the
421                        // cached value cannot be trusted: run `compute` again and
422                        // let it decide how to handle the failure.
423                        Ok(_) | Err(_) => {
424                            changed = true;
425                            break;
426                        }
427                    }
428                }
429                changed
430            };
431            if !inputs_changed {
432                // Early cutoff: nothing this query read actually changed. Reuse
433                // the cached value; only its verification stamp moves forward.
434                if let Some(memo) = self.memos.borrow_mut().get_mut(key) {
435                    memo.verified_at = self.revision;
436                }
437                self.validated.set(self.validated.get().saturating_add(1));
438                return Ok((value, changed_at));
439            }
440            return self.recompute(key, Some((value, changed_at)));
441        }
442
443        // No memo at all: compute from scratch.
444        self.recompute(key, None)
445    }
446
447    /// Run `compute` for `key`, tracking the dependencies it reads and caching
448    /// the result. `previous`, when present, is the memo's prior value and change
449    /// stamp, used for early cutoff.
450    fn recompute(
451        &self,
452        key: &S::Key,
453        previous: Option<(S::Value, Revision)>,
454    ) -> Result<(S::Value, Revision), QueryError> {
455        // Cycle detection and the depth limit: `enter` refuses a key already on
456        // the active stack. The guards unwind both bookkeeping stacks whether
457        // `compute` succeeds, fails, or panics.
458        let (result, deps) = {
459            let _active = self.enter(key)?;
460            self.frames.borrow_mut().push(Vec::new());
461            let _frame = FrameGuard(&self.frames);
462            let result = self.system.compute(self, key);
463            let deps = self
464                .frames
465                .borrow_mut()
466                .last_mut()
467                .map(core::mem::take)
468                .unwrap_or_default();
469            (result, deps)
470        };
471
472        let value = result?;
473
474        // Early cutoff: if the recomputed value equals the old one, keep the old
475        // change stamp so dependents see "unchanged" and are not recomputed.
476        let changed_at = match previous {
477            Some((old_value, old_changed_at)) if old_value == value => old_changed_at,
478            _ => self.revision,
479        };
480
481        self.computed.set(self.computed.get().saturating_add(1));
482        let memo = Memo {
483            value: value.clone(),
484            deps,
485            changed_at,
486            verified_at: self.revision,
487        };
488        self.memos.borrow_mut().insert(key.clone(), memo);
489
490        Ok((value, changed_at))
491    }
492}
493
494impl<S: System> Database<S> {
495    /// Put `key` on the active stack, or fail if it is already there (a cycle)
496    /// or the stack is at the depth limit. The returned guard pops it again,
497    /// also when `compute` panics.
498    fn enter(&self, key: &S::Key) -> Result<ActiveGuard<'_, S::Key>, QueryError> {
499        let mut active = self.active.borrow_mut();
500        if active.iter().any(|k| k == key) {
501            return Err(QueryError::Cycle);
502        }
503        if self.max_depth.is_some_and(|max| active.len() >= max) {
504            return Err(QueryError::TooDeep);
505        }
506        active.push(key.clone());
507        Ok(ActiveGuard(&self.active))
508    }
509}
510
511/// Pops the active stack when dropped.
512struct ActiveGuard<'a, K>(&'a RefCell<Vec<K>>);
513
514impl<K> Drop for ActiveGuard<'_, K> {
515    fn drop(&mut self) {
516        self.0.borrow_mut().pop();
517    }
518}
519
520/// Pops the dependency-frame stack when dropped.
521struct FrameGuard<'a, K>(&'a RefCell<Vec<Vec<K>>>);
522
523impl<K> Drop for FrameGuard<'_, K> {
524    fn drop(&mut self) {
525        self.0.borrow_mut().pop();
526    }
527}
528
529impl<S: System> fmt::Debug for Database<S> {
530    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531        f.debug_struct("Database")
532            .field("revision", &self.revision)
533            .field("inputs", &self.inputs.len())
534            .field("memos", &self.memos.borrow().len())
535            .field("stats", &self.stats())
536            .finish()
537    }
538}
539
540#[cfg(test)]
541#[allow(clippy::unwrap_used)]
542mod tests {
543    use super::*;
544    use alloc::string::{String, ToString};
545    use core::cell::Cell as StdCell;
546
547    /// A system whose `compute` counts how many times each key ran, so tests can
548    /// assert exactly which queries recomputed.
549    struct Counting {
550        // key -> number of times computed
551        runs: StdCell<[u32; 8]>,
552    }
553
554    impl Counting {
555        fn new() -> Self {
556            Self {
557                runs: StdCell::new([0; 8]),
558            }
559        }
560
561        fn runs_of(&self, key: usize) -> u32 {
562            self.runs.get()[key]
563        }
564
565        fn bump(&self, key: usize) {
566            let mut r = self.runs.get();
567            r[key] += 1;
568            self.runs.set(r);
569        }
570    }
571
572    // Key layout: 0 = input A, 1 = input B, 2 = A*10, 3 = (A*10)+(B), 4 = sign of 2
573    impl System for Counting {
574        type Key = usize;
575        type Value = i64;
576        fn compute(&self, db: &Database<Self>, key: &usize) -> Result<i64, QueryError> {
577            self.bump(*key);
578            match key {
579                0 | 1 => Ok(0), // default when the input was never set
580                2 => Ok(db.get(&0)? * 10),
581                3 => Ok(db.get(&2)? + db.get(&1)?),
582                4 => Ok(db.get(&2)?.signum()),
583                _ => Ok(0),
584            }
585        }
586    }
587
588    #[test]
589    fn test_input_get_returns_set_value() {
590        let mut db = Database::new(Counting::new());
591        db.set(0, 7);
592        assert_eq!(db.get(&0).unwrap(), 7);
593        // Inputs never invoke compute.
594        assert_eq!(db.system().runs_of(0), 0);
595    }
596
597    #[test]
598    fn test_first_get_computes_once() {
599        let mut db = Database::new(Counting::new());
600        db.set(0, 3);
601        assert_eq!(db.get(&2).unwrap(), 30);
602        assert_eq!(db.system().runs_of(2), 1);
603        // Only key 2 computed; key 0 is a set input and never runs compute.
604        assert_eq!(db.stats().computed, 1);
605    }
606
607    #[test]
608    fn test_second_get_is_a_hit() {
609        let mut db = Database::new(Counting::new());
610        db.set(0, 3);
611        assert_eq!(db.get(&2).unwrap(), 30);
612        let before = db.stats();
613        assert_eq!(db.get(&2).unwrap(), 30);
614        let after = db.stats();
615        assert_eq!(after.hits - before.hits, 1);
616        assert_eq!(after.computed, before.computed); // no recompute
617    }
618
619    #[test]
620    fn test_input_change_recomputes_dependents() {
621        let mut db = Database::new(Counting::new());
622        db.set(0, 3);
623        assert_eq!(db.get(&2).unwrap(), 30);
624        db.set(0, 4);
625        assert_eq!(db.get(&2).unwrap(), 40);
626        assert_eq!(db.system().runs_of(2), 2);
627    }
628
629    #[test]
630    fn test_unchanged_input_set_does_not_recompute() {
631        let mut db = Database::new(Counting::new());
632        db.set(0, 3);
633        assert_eq!(db.get(&2).unwrap(), 30);
634        db.set(0, 3); // same value -> no revision bump
635        assert_eq!(db.get(&2).unwrap(), 30);
636        // Still verified at the current revision: a hit, not a recompute.
637        assert_eq!(db.system().runs_of(2), 1);
638    }
639
640    #[test]
641    fn test_multilayer_hit_after_no_change() {
642        let mut db = Database::new(Counting::new());
643        db.set(0, 3);
644        // key 4 = signum(A*10); depends on key 2, which depends on input 0.
645        assert_eq!(db.get(&4).unwrap(), 1);
646        assert_eq!(db.system().runs_of(4), 1);
647        assert_eq!(db.system().runs_of(2), 1);
648
649        // Re-setting the input to the same value does not advance the revision,
650        // so the next query is a plain hit and nothing recomputes.
651        db.set(0, 3);
652        assert_eq!(db.get(&4).unwrap(), 1);
653        assert_eq!(db.system().runs_of(4), 1);
654        assert_eq!(db.system().runs_of(2), 1);
655    }
656
657    #[test]
658    fn test_early_cutoff_when_intermediate_value_is_stable() {
659        // A system where an input change leaves an intermediate query's value
660        // unchanged, so the top query is validated, not recomputed.
661        struct AbsChain {
662            top_runs: StdCell<u32>,
663        }
664        #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
665        enum K {
666            In,
667            Abs,
668            Plus1,
669        }
670        impl System for AbsChain {
671            type Key = K;
672            type Value = i64;
673            fn compute(&self, db: &Database<Self>, key: &K) -> Result<i64, QueryError> {
674                match key {
675                    K::In => Ok(0),
676                    K::Abs => Ok(db.get(&K::In)?.abs()),
677                    K::Plus1 => {
678                        self.top_runs.set(self.top_runs.get() + 1);
679                        Ok(db.get(&K::Abs)? + 1)
680                    }
681                }
682            }
683        }
684
685        let mut db = Database::new(AbsChain {
686            top_runs: StdCell::new(0),
687        });
688        db.set(K::In, 5);
689        assert_eq!(db.get(&K::Plus1).unwrap(), 6);
690        assert_eq!(db.system().top_runs.get(), 1);
691
692        // -5 and 5 have the same abs, so Abs recomputes to the same value and
693        // Plus1 is validated (early cutoff), not recomputed.
694        db.set(K::In, -5);
695        assert_eq!(db.get(&K::Plus1).unwrap(), 6);
696        assert_eq!(db.system().top_runs.get(), 1);
697        assert!(db.stats().validated >= 1);
698    }
699
700    #[test]
701    fn test_default_value_when_input_unset() {
702        let db = Database::new(Counting::new());
703        // Input 0 was never set; compute(0) returns its default of 0.
704        assert_eq!(db.get(&2).unwrap(), 0);
705    }
706
707    #[test]
708    fn test_direct_self_cycle_is_error() {
709        struct SelfDep;
710        impl System for SelfDep {
711            type Key = u32;
712            type Value = u32;
713            fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
714                db.get(k)
715            }
716        }
717        let db = Database::new(SelfDep);
718        assert_eq!(db.get(&1), Err(QueryError::Cycle));
719    }
720
721    #[test]
722    fn test_indirect_cycle_is_error() {
723        struct PingPong;
724        impl System for PingPong {
725            type Key = u32;
726            type Value = u32;
727            fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
728                // 0 depends on 1, 1 depends on 0.
729                match k {
730                    0 => db.get(&1),
731                    _ => db.get(&0),
732                }
733            }
734        }
735        let db = Database::new(PingPong);
736        assert_eq!(db.get(&0), Err(QueryError::Cycle));
737    }
738
739    #[test]
740    fn test_state_is_clean_after_cycle_error() {
741        struct SelfDep;
742        impl System for SelfDep {
743            type Key = u32;
744            type Value = u32;
745            fn compute(&self, db: &Database<Self>, k: &u32) -> Result<u32, QueryError> {
746                if *k == 0 { db.get(&0) } else { Ok(*k) }
747            }
748        }
749        let db = Database::new(SelfDep);
750        assert_eq!(db.get(&0), Err(QueryError::Cycle));
751        // A non-cyclic query still resolves normally afterwards.
752        assert_eq!(db.get(&9).unwrap(), 9);
753    }
754
755    #[test]
756    fn test_set_promotes_derived_key_to_input() {
757        let mut db = Database::new(Counting::new());
758        db.set(0, 3);
759        assert_eq!(db.get(&2).unwrap(), 30); // key 2 derived and cached
760        db.set(2, 999); // promote key 2 to an input
761        assert_eq!(db.get(&2).unwrap(), 999);
762        // It is now an input: compute is never called for it again.
763        let runs_before = db.system().runs_of(2);
764        assert_eq!(db.get(&2).unwrap(), 999);
765        assert_eq!(db.system().runs_of(2), runs_before);
766    }
767
768    #[test]
769    fn test_branching_dependencies_tracked_precisely() {
770        // A query reads one of two inputs depending on a selector input; only the
771        // branch it actually took is a dependency.
772        #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
773        enum K {
774            Select,
775            Left,
776            Right,
777            Picked,
778        }
779        struct Switch {
780            runs: StdCell<u32>,
781        }
782        impl System for Switch {
783            type Key = K;
784            type Value = i64;
785            fn compute(&self, db: &Database<Self>, key: &K) -> Result<i64, QueryError> {
786                match key {
787                    K::Select | K::Left | K::Right => Ok(0),
788                    K::Picked => {
789                        self.runs.set(self.runs.get() + 1);
790                        if db.get(&K::Select)? == 0 {
791                            db.get(&K::Left)
792                        } else {
793                            db.get(&K::Right)
794                        }
795                    }
796                }
797            }
798        }
799        let mut db = Database::new(Switch {
800            runs: StdCell::new(0),
801        });
802        db.set(K::Select, 0);
803        db.set(K::Left, 100);
804        db.set(K::Right, 200);
805        assert_eq!(db.get(&K::Picked).unwrap(), 100);
806        assert_eq!(db.system().runs.get(), 1);
807
808        // Changing Right must NOT recompute Picked: it read Left, not Right.
809        db.set(K::Right, 999);
810        assert_eq!(db.get(&K::Picked).unwrap(), 100);
811        assert_eq!(db.system().runs.get(), 1);
812
813        // Changing Left DOES recompute Picked.
814        db.set(K::Left, 111);
815        assert_eq!(db.get(&K::Picked).unwrap(), 111);
816        assert_eq!(db.system().runs.get(), 2);
817    }
818
819    #[test]
820    fn test_string_values_work() {
821        struct Greeter;
822        impl System for Greeter {
823            type Key = u32;
824            type Value = String;
825            fn compute(&self, db: &Database<Self>, k: &u32) -> Result<String, QueryError> {
826                if *k == 0 {
827                    Ok("world".to_string())
828                } else {
829                    Ok(alloc::format!("hello, {}", db.get(&0)?))
830                }
831            }
832        }
833        let db = Database::new(Greeter);
834        assert_eq!(db.get(&1).unwrap(), "hello, world");
835    }
836
837    #[test]
838    fn test_debug_reports_shape() {
839        let mut db = Database::new(Counting::new());
840        db.set(0, 1);
841        let _ = db.get(&2).unwrap();
842        let rendered = alloc::format!("{db:?}");
843        assert!(rendered.contains("revision"));
844        assert!(rendered.contains("memos"));
845    }
846
847    #[test]
848    fn test_diamond_recomputes_shared_dep_once() {
849        // top depends on left and right, both of which depend on a shared input.
850        #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
851        enum K {
852            In,
853            Left,
854            Right,
855            Top,
856        }
857        struct Diamond {
858            in_runs: StdCell<u32>,
859        }
860        impl System for Diamond {
861            type Key = K;
862            type Value = i64;
863            fn compute(&self, db: &Database<Self>, key: &K) -> Result<i64, QueryError> {
864                match key {
865                    K::In => Ok(0),
866                    K::Left => Ok(db.get(&K::In)? + 1),
867                    K::Right => Ok(db.get(&K::In)? + 2),
868                    K::Top => {
869                        self.in_runs.set(self.in_runs.get() + 1);
870                        Ok(db.get(&K::Left)? + db.get(&K::Right)?)
871                    }
872                }
873            }
874        }
875        let mut db = Database::new(Diamond {
876            in_runs: StdCell::new(0),
877        });
878        db.set(K::In, 10);
879        assert_eq!(db.get(&K::Top).unwrap(), (10 + 1) + (10 + 2));
880        assert_eq!(db.system().in_runs.get(), 1);
881
882        db.set(K::In, 20);
883        assert_eq!(db.get(&K::Top).unwrap(), (20 + 1) + (20 + 2));
884        assert_eq!(db.system().in_runs.get(), 2);
885    }
886
887    #[test]
888    fn test_multiple_deps_each_invalidate() {
889        // Guards against a regression where deps are dropped: a query with two
890        // input deps must be invalidated by either.
891        let mut db = Database::new(Counting::new());
892        db.set(0, 1);
893        db.set(1, 2);
894        // key 3 = (A*10) + B = 10 + 2 = 12
895        assert_eq!(db.get(&3).unwrap(), 12);
896        db.set(1, 5);
897        assert_eq!(db.get(&3).unwrap(), 15);
898        // And the other dependency still invalidates it too.
899        db.set(0, 2); // A*10 becomes 20
900        assert_eq!(db.get(&3).unwrap(), 25);
901    }
902}