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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
//! Query definitions and related types for the QBICE engine.
//!
//! This module defines the core [`Query`] trait that all queries must
//! implement, along with supporting types for query identification and
//! execution behavior.
//!
//! # Defining Queries
//!
//! A query represents a computation with an input key and an output value.
//! To define a query:
//!
//! 1. Create a struct representing the query key
//! 2. Derive the required traits: `StableHash`, `Identifiable`, `Debug`,
//! `Clone`, `PartialEq`, `Eq`, and `Hash`
//! 3. Implement the [`Query`] trait, specifying the output `Value` type
//!
//! # Query Identification
//!
//! Each query instance is uniquely identified by a [`QueryID`], which combines:
//! - A stable type identifier (from the [`Identifiable`] trait)
//! - A 128-bit hash of the query's contents (from the [`StableHash`] trait)
//!
//! This ensures that different query types never collide, and different
//! instances of the same query type are distinguished by their content.
//!
//! # Execution Styles
//!
//! The [`ExecutionStyle`] enum controls how queries interact with the
//! dependency tracking system:
//!
//! - [`ExecutionStyle::Normal`]: Standard queries with full dependency tracking
//! - [`ExecutionStyle::Projection`]: Fast extractors for parts of other queries
//! - [`ExecutionStyle::Firewall`]: Boundary queries that limit dirty
//! propagation
//!
//! Most queries should use `Normal`. Advanced users can leverage `Projection`
//! and `Firewall` for performance optimization in complex dependency graphs.
use ;
use ;
use ;
use ;
/// The query interface of the QBICE engine.
///
/// A type implementing [`Query`] represents a query input (key) that is
/// associated with a specific output value type. The query itself only defines
/// the *what* - the actual computation is provided by an [`crate::Executor`].
///
/// # Required Traits
///
/// Query types must implement several traits:
///
/// - [`StableHash`]: For consistent hashing across program runs
/// - [`Identifiable`]: For stable type identification
/// - [`Eq`] + [`Hash`]: For use in hash maps
/// - [`Clone`]: For storing query keys
/// - [`Debug`]: For error messages and debugging
/// - [`Send`] + [`Sync`]: For thread-safe access
///
/// Most of these can be derived automatically using the [`Query`] derive macro.
///
/// # Value Type Requirements
///
/// The associated `Value` type must also satisfy certain bounds:
///
/// - `'static`: No borrowed data
/// - [`Send`] + [`Sync`]: For thread-safe caching
/// - [`Clone`]: For returning cached values
/// - [`Debug`]: For debugging and visualization
/// - [`StableHash`]: For change detection (fingerprinting)
///
/// # Performance: Cheap Cloning
///
/// **Both the query type and its value type should be cheaply cloneable.**
///
/// The engine frequently clones queries and values internally for:
/// - Storing query keys in the dependency graph
/// - Caching computed results
/// - Returning values to callers
/// - Tracking dependencies across async boundaries
///
/// For types containing heap-allocated data, consider using shared ownership:
///
/// | Instead of | Use |
/// |------------|-----|
/// | `String` | `Arc<str>` or a shared string type |
/// | `Vec<T>` | `Arc<[T]>` |
/// | `HashMap<K, V>` | `Arc<HashMap<K, V>>` |
/// | Large structs | `Arc<T>` |
/// Specifies the execution style of a query.
///
/// The execution style determines how a query participates in the dependency
/// tracking and dirty propagation system. Different styles offer trade-offs
/// between fine-grained reactivity and computational efficiency.
///
/// # Choosing an Execution Style
///
/// - **Most queries**: Use [`Normal`](ExecutionStyle::Normal) - it provides
/// standard incremental computation semantics
/// - **External data**: Use [`ExternalInput`](ExecutionStyle::ExternalInput)
/// for queries that read from files, network, or other external sources
/// - **Dirty Propagation Optimization**: Use
/// [`Firewall`](ExecutionStyle::Firewall) and
/// [`Projection`](ExecutionStyle::Projection) to optimize dirty propagation
/// in large dependency graphs
///
/// # Variants
///
/// ## Normal
///
/// Standard queries with full dependency tracking. This is the default and
/// appropriate for most use cases.
///
/// **Behavior:**
/// - Dependencies are tracked automatically
/// - Changes to dependencies cause immediate dirty propagation
/// - Results are cached and reused when dependencies haven't changed
///
/// **When to use:**
/// - Most computation queries
/// - Queries with stable, well-defined dependencies
/// - When you want automatic reactivity to changes
///
/// ## Firewall
///
/// Boundary queries that limit dirty propagation based on output changes
/// rather than input changes. This is a key optimization for reducing
/// unnecessary dirty propagation in large computation graphs.
///
/// **Behavior:**
/// - When dependencies change, the firewall is recomputed
/// - Downstream queries are marked dirty **only if** the firewall's output
/// value actually changed (based on fingerprint)
/// - Acts as an incremental **dirty propagation boundary** in the large
/// computation graph system
///
/// **When to use:**
/// - A computation that has a large graph, the dirty propagation of which
/// becomes expensive
/// - When many inputs change frequently, but the overall output is stable
///
/// **Note:** You almost always want to visualize your computation graph first
/// to identify where firewalls will be most effective. Moreover, firewalls
/// often work best when paired with projections downstream to minimize dirty
/// propagation.
///
/// ## Projection
///
/// Lightweight queries that extract data from firewall queries. Projections
/// are designed to be very fast (essentially field access) and work in
/// conjunction with firewalls to provide fine-grained access to coarse
/// computations.
///
/// **Behavior:**
/// - Must be extremely fast to execute (no expensive computation)
/// - Used internally to extract specific fields from firewall results
/// - Prevents unnecessary full recomputation when only a small part is needed
///
/// **Example pattern:**
/// ```text
/// Firewall Query: ComputeAllMetrics
/// ├─ Projection: ExtractMetricA
/// ├─ Projection: ExtractMetricB
/// └─ Projection: ExtractMetricC
/// ```
///
/// ## `ExternalInput`
///
/// Queries that interact with the outside world (files, network, system time,
/// etc.). These are leaf queries that don't depend on other queries but can
/// be explicitly refreshed during input sessions.
///
/// **Behavior:**
/// - Cannot depend on other queries (must be leaf nodes)
/// - Not automatically refreshed when other inputs change
/// - Can be explicitly refreshed via input session methods
/// - Executor may perform I/O or other impure operations
///
/// **When to use:**
/// - Reading configuration files
/// - Fetching data from network APIs
/// - Querying databases
/// - Reading system time or environment variables
/// - Any external state that should be explicitly controlled
/// A unique identifier for a query instance.
///
/// A `QueryID` uniquely identifies a specific query within the engine by
/// combining:
///
/// - A [`StableTypeID`] that identifies the query type
/// - A 128-bit hash of the query's contents
///
/// This combination ensures that:
/// - Different query types never collide (due to type ID)
/// - Different instances of the same type are distinguished (due to content
/// hash)
/// - Identification is stable across program runs (due to stable hashing)
///
/// # Hash Collision
///
/// While the probability of hash collision is astronomically low (2^-128),
/// QBICE assumes no collisions occur. In practice, this is safe for all
/// reasonable use cases.
///
/// # Internal Use
///
/// You typically don't need to work with `QueryID` directly - the engine
/// handles identification automatically. However, it's useful for:
/// - Debugging dependency graphs
/// - Custom visualization
/// - Advanced introspection