Skip to main content

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}