Skip to main content

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}