query_flow/
error.rs

1//! Error types for query execution.
2
3use std::fmt;
4use std::sync::Arc;
5
6use crate::asset::PendingAsset;
7use crate::key::FullCacheKey;
8
9/// Query errors including both system-level and user errors.
10///
11/// User errors can be propagated using the `?` operator, which automatically
12/// converts any `Into<anyhow::Error>` type into `QueryError::UserError`.
13#[derive(Debug, Clone)]
14pub enum QueryError {
15    /// Query is waiting for async loading to complete.
16    ///
17    /// This is returned when a dependency is still loading via a background task.
18    /// Use `runtime.query_async()` to wait for loading to complete, or handle
19    /// explicitly in your query logic.
20    ///
21    /// The `asset` field contains information about the pending asset, which can
22    /// be downcast to the original key type using `asset.key::<K>()`.
23    Suspend {
24        /// The pending asset that caused the suspension.
25        asset: PendingAsset,
26    },
27
28    /// Dependency cycle detected.
29    ///
30    /// The query graph contains a cycle, which would cause infinite recursion.
31    /// The `path` contains a debug representation of the cycle.
32    Cycle {
33        /// Debug representation of the queries forming the cycle.
34        path: Vec<String>,
35    },
36
37    /// Query execution was cancelled.
38    Cancelled,
39
40    /// A required dependency is missing.
41    MissingDependency {
42        /// Description of the missing dependency.
43        description: String,
44    },
45
46    /// Dependencies were removed during query execution.
47    ///
48    /// This can happen if another thread removes queries or assets
49    /// while this query is being registered.
50    DependenciesRemoved {
51        /// Keys that were not found during registration.
52        missing_keys: Vec<FullCacheKey>,
53    },
54
55    /// User-defined error.
56    ///
57    /// This variant allows user errors to be propagated through the query system
58    /// using the `?` operator. Any type implementing `Into<anyhow::Error>` can be
59    /// converted to this variant.
60    ///
61    /// Unlike system errors (Suspend, Cycle, etc.), UserError results are cached
62    /// and participate in early cutoff optimization.
63    UserError(Arc<anyhow::Error>),
64}
65
66impl fmt::Display for QueryError {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        match self {
69            QueryError::Suspend { asset } => {
70                write!(f, "query suspended: waiting for {}", asset.debug_repr())
71            }
72            QueryError::Cycle { path } => {
73                write!(f, "dependency cycle detected: {}", path.join(" -> "))
74            }
75            QueryError::Cancelled => write!(f, "query cancelled"),
76            QueryError::MissingDependency { description } => {
77                write!(f, "missing dependency: {}", description)
78            }
79            QueryError::DependenciesRemoved { missing_keys } => {
80                write!(
81                    f,
82                    "dependencies removed during execution: {:?}",
83                    missing_keys
84                )
85            }
86            QueryError::UserError(e) => write!(f, "user error: {}", e),
87        }
88    }
89}
90
91impl<T: Into<anyhow::Error>> From<T> for QueryError {
92    fn from(err: T) -> Self {
93        QueryError::UserError(Arc::new(err.into()))
94    }
95}