Skip to main content

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}