query_flow/loading.rs
1//! Loading state for async resource handling.
2
3use std::sync::Arc;
4
5use crate::asset::{AssetKey, PendingAsset};
6use crate::QueryError;
7
8/// Loading state with asset key information for error reporting.
9///
10/// This is returned by [`Db::asset_state()`](crate::Db::asset_state) and provides
11/// information about whether an asset is loading or ready.
12///
13/// For most use cases, prefer [`Db::asset()`](crate::Db::asset) which automatically
14/// suspends on loading. Use `asset_state()` when you need to explicitly check
15/// the loading state without triggering suspension.
16///
17/// # Example
18///
19/// ```
20/// use query_flow::{asset_key, query, Db, DurabilityLevel, QueryError, QueryRuntime};
21///
22/// #[asset_key(asset = String)]
23/// struct FilePath(String);
24///
25/// #[query]
26/// fn process_file(db: &impl Db, path: FilePath) -> Result<usize, QueryError> {
27/// // Most common: just use db.asset() which suspends automatically
28/// let content = db.asset(path)?;
29/// Ok(content.len())
30/// }
31///
32/// #[query]
33/// fn check_loading(db: &impl Db, path: FilePath) -> Result<bool, QueryError> {
34/// // Use asset_state() when you need to check loading status explicitly
35/// let state = db.asset_state(path)?;
36/// Ok(state.is_loading())
37/// }
38///
39/// let runtime = QueryRuntime::new();
40/// runtime.resolve_asset(
41/// FilePath("a".into()),
42/// "hello".into(),
43/// DurabilityLevel::Volatile,
44/// );
45/// assert_eq!(*runtime.query(ProcessFile::new(FilePath("a".into()))).unwrap(), 5);
46/// assert!(!*runtime.query(CheckLoading::new(FilePath("a".into()))).unwrap());
47/// ```
48pub struct AssetLoadingState<K: AssetKey> {
49 value: Option<Arc<K::Asset>>,
50 key: K,
51}
52
53impl<K: AssetKey> AssetLoadingState<K> {
54 /// Create a loading state (asset not yet available).
55 pub fn loading(key: K) -> Self {
56 Self { value: None, key }
57 }
58
59 /// Create a ready state with the asset value.
60 pub fn ready(key: K, value: Arc<K::Asset>) -> Self {
61 Self {
62 value: Some(value),
63 key,
64 }
65 }
66
67 /// Check if the resource is still loading.
68 pub fn is_loading(&self) -> bool {
69 self.value.is_none()
70 }
71
72 /// Check if the resource is ready.
73 pub fn is_ready(&self) -> bool {
74 self.value.is_some()
75 }
76
77 /// Get the value if ready, None if loading.
78 pub fn get(&self) -> Option<&Arc<K::Asset>> {
79 self.value.as_ref()
80 }
81
82 /// Get the value if ready, None if loading (consuming version).
83 pub fn into_inner(self) -> Option<Arc<K::Asset>> {
84 self.value
85 }
86
87 /// Convert to Result - Loading becomes Err(QueryError::Suspend).
88 ///
89 /// This method is used internally by [`Db::asset()`](crate::Db::asset).
90 /// You can also use it when working with [`Db::asset_state()`](crate::Db::asset_state).
91 ///
92 /// # Example
93 ///
94 /// ```
95 /// use query_flow::{asset_key, query, Db, DurabilityLevel, QueryError, QueryRuntime};
96 ///
97 /// #[asset_key(asset = String)]
98 /// struct FilePath(String);
99 ///
100 /// #[query]
101 /// fn byte_len(db: &impl Db, path: FilePath) -> Result<usize, QueryError> {
102 /// // Preferred: use db.asset() directly
103 /// let data = db.asset(path.clone())?;
104 ///
105 /// // Alternative: use asset_state() + suspend()
106 /// let state = db.asset_state(path)?;
107 /// let data2 = state.suspend()?;
108 ///
109 /// assert_eq!(data, data2);
110 /// Ok(data.len())
111 /// }
112 ///
113 /// let runtime = QueryRuntime::new();
114 /// runtime.resolve_asset(
115 /// FilePath("a".into()),
116 /// "hello".into(),
117 /// DurabilityLevel::Volatile,
118 /// );
119 /// assert_eq!(*runtime.query(ByteLen::new(FilePath("a".into()))).unwrap(), 5);
120 /// ```
121 pub fn suspend(self) -> Result<Arc<K::Asset>, QueryError> {
122 match self.value {
123 None => Err(QueryError::Suspend {
124 asset: PendingAsset::new(self.key),
125 }),
126 Some(v) => Ok(v),
127 }
128 }
129
130 /// Get a reference to the key.
131 pub fn key(&self) -> &K {
132 &self.key
133 }
134}
135
136impl<K: AssetKey> std::fmt::Debug for AssetLoadingState<K>
137where
138 K::Asset: std::fmt::Debug,
139{
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 match &self.value {
142 None => write!(f, "AssetLoadingState::Loading({:?})", self.key),
143 Some(v) => write!(f, "AssetLoadingState::Ready({:?}, {:?})", self.key, v),
144 }
145 }
146}