query_lang/lib.rs
1//! # query_lang
2//!
3//! An incremental computation engine: a database that caches the results of
4//! derived queries, tracks what each result was computed from, and recomputes
5//! only what a change actually affects.
6//!
7//! A compiler runs the same computation over and over as source is edited —
8//! parse a file, resolve its names, type-check its definitions, lower it to IR.
9//! Most edits touch a fraction of that work, yet a naive compiler redoes all of
10//! it on every keystroke. The query model, from Rust's own `salsa` and
11//! rust-analyzer, turns each step into a *query*: a pure function whose result is
12//! memoized and whose dependencies are recorded as it runs. When an input
13//! changes, the engine invalidates only the queries that transitively read it,
14//! and reuses everything else. query-lang is that engine, and nothing more — it
15//! owns no compiler, no IR, no syntax; it is generic over the queries a consumer
16//! defines.
17//!
18//! ## The model
19//!
20//! Three pieces:
21//!
22//! - A [`System`] is the definition of your queries: a [`Key`](System::Key) type
23//! that names a query, a [`Value`](System::Value) type it produces, and a
24//! [`compute`](System::compute) function that derives one from the other.
25//! - A [`Database`] holds the [`System`], stores your **inputs**, and caches the
26//! **derived** results. It is the engine: you set inputs and get results, and
27//! it handles caching, dependency tracking, and invalidation.
28//! - A [`get`](Database::get) resolves a query. Called from application code it
29//! returns a result; called from inside a `compute` it reads a dependency, and
30//! the engine records that edge automatically.
31//!
32//! An input is any key whose value you [`set`](Database::set) directly; every
33//! other key is derived through `compute`. One `Key` type names both, so a query
34//! reads an input and another query the same way.
35//!
36//! ## Example
37//!
38//! An input string, a query that parses it to a number, and a query that squares
39//! that number. Editing the input recomputes the chain; an edit that leaves the
40//! parsed number unchanged stops at the parse via early cutoff.
41//!
42//! ```
43//! use query_lang::{Database, System, QueryError};
44//!
45//! #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
46//! enum Key {
47//! Source, // an input: the raw text
48//! Parsed, // = Source parsed as i64
49//! Squared, // = Parsed * Parsed
50//! }
51//!
52//! struct Pipeline;
53//! impl System for Pipeline {
54//! type Key = Key;
55//! type Value = i64;
56//! fn compute(&self, db: &Database<Self>, key: &Key) -> Result<i64, QueryError> {
57//! match key {
58//! // `Source` is an input; this default only applies if it was unset.
59//! Key::Source => Ok(0),
60//! Key::Parsed => Ok(db.get(&Key::Source)?),
61//! Key::Squared => {
62//! let n = db.get(&Key::Parsed)?;
63//! Ok(n * n)
64//! }
65//! }
66//! }
67//! }
68//!
69//! let mut db = Database::new(Pipeline);
70//! db.set(Key::Source, 12);
71//! assert_eq!(db.get(&Key::Squared)?, 144);
72//! assert_eq!(db.stats().computed, 2); // Source is a set input; Parsed and Squared ran
73//!
74//! // Ask again with no edit: a hit, nothing recomputes.
75//! assert_eq!(db.get(&Key::Squared)?, 144);
76//! assert_eq!(db.stats().hits, 1);
77//! # Ok::<(), QueryError>(())
78//! ```
79//!
80//! ## Features
81//!
82//! - `std` (default) — the standard library. Without it the crate is
83//! `#![no_std]` and needs only `alloc`; the engine itself uses no operating-
84//! system facilities either way.
85//! - `serde` — derives `serde::Serialize` for [`Revision`] and [`Stats`] so a
86//! database's version and cache metrics can be logged or inspected.
87//!
88//! ## Stability
89//!
90//! The public surface is frozen and stable as of `1.0.0`: it follows Semantic
91//! Versioning, with no breaking changes before `2.0`. The full surface, the
92//! semantic guarantees, and the SemVer promise are catalogued in
93//! [`docs/API.md`](https://github.com/jamesgober/query-lang/blob/main/docs/API.md#stability).
94
95#![cfg_attr(not(feature = "std"), no_std)]
96#![cfg_attr(docsrs, feature(doc_cfg))]
97#![deny(missing_docs)]
98#![forbid(unsafe_code)]
99#![deny(
100 clippy::unwrap_used,
101 clippy::expect_used,
102 clippy::panic,
103 clippy::todo,
104 clippy::unimplemented,
105 clippy::unreachable,
106 clippy::dbg_macro,
107 clippy::print_stdout,
108 clippy::print_stderr
109)]
110
111extern crate alloc;
112
113mod database;
114mod error;
115mod revision;
116mod stats;
117mod system;
118
119pub use database::Database;
120pub use error::QueryError;
121pub use revision::Revision;
122pub use stats::Stats;
123pub use system::System;
124
125/// Compiles and runs the `rust` code blocks in `README.md` and `docs/API.md` as
126/// part of `cargo test`, so the published examples cannot drift from the API.
127///
128/// Present only while collecting doctests (`#[cfg(doctest)]`); it is not part of
129/// the public surface and does not appear in the built library or its docs.
130#[cfg(doctest)]
131#[doc = include_str!("../README.md")]
132#[doc = include_str!("../docs/API.md")]
133pub struct MarkdownDocTests;