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}