query_flow/db.rs
1//! Database trait for query execution.
2
3use std::sync::Arc;
4
5use crate::asset::AssetKey;
6use crate::loading::AssetLoadingState;
7use crate::query::Query;
8use crate::QueryError;
9
10/// Database trait that provides query execution and asset access.
11///
12/// This trait is implemented by both [`QueryRuntime`](crate::QueryRuntime) and
13/// the internal `QueryContext`, allowing queries to work with either.
14///
15/// - `QueryRuntime::query()` / `QueryRuntime::asset()`: No dependency tracking
16/// - `QueryContext::query()` / `QueryContext::asset()`: With dependency tracking
17pub trait Db {
18 /// Execute a query, returning the cached result if available.
19 fn query<Q: Query>(&self, query: Q) -> Result<Arc<Q::Output>, QueryError>;
20
21 /// Access an asset by key.
22 ///
23 /// Returns the asset value if ready, or `Err(QueryError::Suspend)` if still loading.
24 /// Use this with the `?` operator for automatic suspension on loading.
25 ///
26 /// # Example
27 ///
28 /// ```
29 /// use query_flow::{asset_key, query, Db, DurabilityLevel, QueryError, QueryRuntime};
30 ///
31 /// #[asset_key(asset = String)]
32 /// struct SourceFile(String);
33 ///
34 /// #[query]
35 /// fn line_count(db: &impl Db, name: String) -> Result<usize, QueryError> {
36 /// let text = db.asset(SourceFile(name))?; // Suspends if loading
37 /// Ok(text.lines().count())
38 /// }
39 ///
40 /// let runtime = QueryRuntime::new();
41 /// runtime.resolve_asset(
42 /// SourceFile("a".into()),
43 /// "one\ntwo\n".into(),
44 /// DurabilityLevel::Volatile,
45 /// );
46 /// assert_eq!(*runtime.query(LineCount::new("a".into())).unwrap(), 2);
47 /// ```
48 fn asset<K: AssetKey>(&self, key: K) -> Result<Arc<K::Asset>, QueryError>;
49
50 /// Access an asset's loading state by key.
51 ///
52 /// Unlike [`asset()`](Self::asset), this method returns the full loading state,
53 /// allowing you to check if an asset is loading without triggering suspension.
54 ///
55 /// # Example
56 ///
57 /// ```
58 /// use query_flow::{asset_key, query, Db, DurabilityLevel, QueryError, QueryRuntime};
59 ///
60 /// #[asset_key(asset = String)]
61 /// struct SourceFile(String);
62 ///
63 /// #[query]
64 /// fn describe(db: &impl Db, name: String) -> Result<String, QueryError> {
65 /// let state = db.asset_state(SourceFile(name))?;
66 /// if state.is_loading() {
67 /// // Handle loading case explicitly, without suspending.
68 /// Ok("loading".to_string())
69 /// } else {
70 /// let value = state.get().unwrap();
71 /// Ok(format!("{} bytes", value.len()))
72 /// }
73 /// }
74 ///
75 /// let runtime = QueryRuntime::new();
76 /// runtime.resolve_asset(
77 /// SourceFile("a".into()),
78 /// "hello".into(),
79 /// DurabilityLevel::Volatile,
80 /// );
81 /// assert_eq!(*runtime.query(Describe::new("a".into())).unwrap(), "5 bytes");
82 /// ```
83 fn asset_state<K: AssetKey>(&self, key: K) -> Result<AssetLoadingState<K>, QueryError>;
84
85 /// List all executed queries of a specific type.
86 fn list_queries<Q: Query>(&self) -> Vec<Q>;
87
88 /// List all resolved asset keys of a specific type.
89 fn list_asset_keys<K: AssetKey>(&self) -> Vec<K>;
90}