query_lang/error.rs
1//! The error a query resolution returns when it cannot complete.
2
3use core::fmt;
4
5/// The error returned from [`Database::get`](crate::Database::get) and
6/// [`System::compute`](crate::System::compute) when a query cannot be resolved.
7///
8/// The engine resolves a derived query by running its
9/// [`compute`](crate::System::compute), which in turn reads other queries. That
10/// recursion terminates only when the dependency graph is acyclic. If a query
11/// asks — directly or through a chain of other queries — for a result that is
12/// itself still being computed, there is no value to return and no way to make
13/// progress: the queries form a cycle. Rather than recurse without bound or
14/// panic, the engine unwinds the whole chain with [`QueryError::Cycle`].
15///
16/// The type is [`non_exhaustive`](https://doc.rust-lang.org/reference/attributes/type_system.html):
17/// resolution has exactly one failure mode today, and new engine-level failure
18/// modes can be added later without breaking a `match` that already handles
19/// `Cycle`.
20///
21/// # Examples
22///
23/// A query that reads itself cannot resolve:
24///
25/// ```
26/// use query_lang::{Database, System, QueryError};
27///
28/// struct SelfReferential;
29/// impl System for SelfReferential {
30/// type Key = u32;
31/// type Value = u32;
32/// fn compute(&self, db: &Database<Self>, key: &u32) -> Result<u32, QueryError> {
33/// // Asking for the very key being computed closes a cycle.
34/// db.get(key)
35/// }
36/// }
37///
38/// let db = Database::new(SelfReferential);
39/// assert_eq!(db.get(&1), Err(QueryError::Cycle));
40/// ```
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42#[non_exhaustive]
43pub enum QueryError {
44 /// A query depended on itself, directly or through a chain of other queries.
45 ///
46 /// The dependency graph must be acyclic. When it is not, the query that
47 /// closed the cycle — and every query waiting on it — resolves to this
48 /// error. The fix is in the query definitions: break the cyclic dependency,
49 /// usually by splitting the offending query so the two halves no longer wait
50 /// on each other.
51 Cycle,
52}
53
54impl fmt::Display for QueryError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 QueryError::Cycle => f.write_str("query cycle detected: a query depends on itself"),
58 }
59 }
60}
61
62impl core::error::Error for QueryError {}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use alloc::string::ToString;
68
69 #[test]
70 fn test_cycle_display_is_descriptive() {
71 assert_eq!(
72 QueryError::Cycle.to_string(),
73 "query cycle detected: a query depends on itself"
74 );
75 }
76
77 #[test]
78 fn test_error_trait_is_implemented() {
79 fn assert_error<E: core::error::Error>(_: &E) {}
80 assert_error(&QueryError::Cycle);
81 }
82
83 #[test]
84 fn test_equality() {
85 assert_eq!(QueryError::Cycle, QueryError::Cycle);
86 }
87}