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