1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! Query trait definition.
use Arc;
use crateDb;
use crateCacheKey;
use crateQueryError;
/// A query that can be executed and cached.
///
/// Queries are the fundamental unit of computation in query-flow. Each query:
/// - Is itself the cache key (implements `Hash + Eq`)
/// - Produces an output value
/// - Can depend on other queries via `db.query()`
///
/// # Sync by Design
///
/// The `query` method is intentionally synchronous. This avoids the "function
/// coloring" problem where async infects the entire call stack. For async
/// operations, use the suspense pattern with `AssetLoadingState`.
///
/// # Error Handling
///
/// The `query` method returns `Result<Output, QueryError>` where:
/// - `QueryError` represents system errors (Suspend, Cycle, Cancelled)
/// - User domain errors should be wrapped in `Output`, e.g., `type Output = Result<T, MyError>`
///
/// This means fallible queries return `Ok(Ok(value))` on success and `Ok(Err(error))` on user error.
///
/// # Example
///
/// ```ignore
/// use query_flow::{Query, Db, QueryError};
///
/// // Simple infallible query
/// #[derive(Clone, Debug, Hash, PartialEq, Eq)]
/// struct Add { a: i32, b: i32 }
///
/// impl Query for Add {
/// type Output = i32;
///
/// fn query(self, _db: &impl Db) -> Result<Arc<Self::Output>, QueryError> {
/// Ok(Arc::new(self.a + self.b))
/// }
///
/// fn output_eq(old: &Self::Output, new: &Self::Output) -> bool {
/// old == new
/// }
/// }
///
/// // Fallible query with user errors
/// #[derive(Clone, Debug, Hash, PartialEq, Eq)]
/// struct ParseInt { input: String }
///
/// impl Query for ParseInt {
/// type Output = Result<i32, std::num::ParseIntError>;
///
/// fn query(self, _db: &impl Db) -> Result<Arc<Self::Output>, QueryError> {
/// Ok(Arc::new(self.input.parse())) // Ok(Arc(Ok(n))) or Ok(Arc(Err(parse_error)))
/// }
///
/// fn output_eq(old: &Self::Output, new: &Self::Output) -> bool {
/// old == new
/// }
/// }
/// ```
/// Convenience trait for query output types.
///
/// This trait combines the bounds needed for a type to be used as a query output:
/// `PartialEq + Send + Sync + 'static`.
///
/// - `PartialEq` is required for the default `output_eq` comparison (early cutoff optimization)
/// - `Send + Sync + 'static` allows the output to be cached and shared across threads
///
/// # When to Use
///
/// Use `QueryOutput` for generic type parameters that appear only in query output:
///
/// ```ignore
/// #[query]
/// fn parse<T: QueryOutput + FromStr>(db: &impl Db, text: String) -> Result<T, QueryError>
/// where
/// T::Err: Display,
/// {
/// text.parse().map_err(|e| anyhow!("{}", e).into())
/// }
/// ```
///
/// # When Not to Use
///
/// If you're using `#[query(output_eq = none)]` or a custom `output_eq` function,
/// you don't need `PartialEq`. In that case, use raw bounds instead:
///
/// ```ignore
/// #[query(output_eq = none)]
/// fn create<T: Send + Sync + 'static>(db: &impl Db) -> Result<T, QueryError> { ... }
/// ```