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/// ```ignore
20/// #[query]
21/// fn process_file(db: &impl Db, path: FilePath) -> Result<Output, QueryError> {
22///     // Most common: just use db.asset() which suspends automatically
23///     let content = db.asset(path)?;
24///     Ok(process(&content))
25/// }
26///
27/// #[query]
28/// fn check_loading(db: &impl Db, path: FilePath) -> Result<bool, QueryError> {
29///     // Use asset_state() when you need to check loading status explicitly
30///     let state = db.asset_state(path)?;
31///     Ok(state.is_loading())
32/// }
33/// ```
34pub struct AssetLoadingState<K: AssetKey> {
35    value: Option<Arc<K::Asset>>,
36    key: K,
37}
38
39impl<K: AssetKey> AssetLoadingState<K> {
40    /// Create a loading state (asset not yet available).
41    pub fn loading(key: K) -> Self {
42        Self { value: None, key }
43    }
44
45    /// Create a ready state with the asset value.
46    pub fn ready(key: K, value: Arc<K::Asset>) -> Self {
47        Self {
48            value: Some(value),
49            key,
50        }
51    }
52
53    /// Check if the resource is still loading.
54    pub fn is_loading(&self) -> bool {
55        self.value.is_none()
56    }
57
58    /// Check if the resource is ready.
59    pub fn is_ready(&self) -> bool {
60        self.value.is_some()
61    }
62
63    /// Get the value if ready, None if loading.
64    pub fn get(&self) -> Option<&Arc<K::Asset>> {
65        self.value.as_ref()
66    }
67
68    /// Get the value if ready, None if loading (consuming version).
69    pub fn into_inner(self) -> Option<Arc<K::Asset>> {
70        self.value
71    }
72
73    /// Convert to Result - Loading becomes Err(QueryError::Suspend).
74    ///
75    /// This method is used internally by [`Db::asset()`](crate::Db::asset).
76    /// You can also use it when working with [`Db::asset_state()`](crate::Db::asset_state).
77    ///
78    /// # Example
79    ///
80    /// ```ignore
81    /// fn query(&self, db: &impl Db) -> Result<MyOutput, QueryError> {
82    ///     // Preferred: use db.asset() directly
83    ///     let data = db.asset(key)?;
84    ///
85    ///     // Alternative: use asset_state() + suspend()
86    ///     let state = db.asset_state(key)?;
87    ///     let data = state.suspend()?;
88    ///
89    ///     Ok(process(&data))
90    /// }
91    /// ```
92    pub fn suspend(self) -> Result<Arc<K::Asset>, QueryError> {
93        match self.value {
94            None => Err(QueryError::Suspend {
95                asset: PendingAsset::new(self.key),
96            }),
97            Some(v) => Ok(v),
98        }
99    }
100
101    /// Get a reference to the key.
102    pub fn key(&self) -> &K {
103        &self.key
104    }
105}
106
107impl<K: AssetKey> std::fmt::Debug for AssetLoadingState<K>
108where
109    K::Asset: std::fmt::Debug,
110{
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        match &self.value {
113            None => write!(f, "AssetLoadingState::Loading({:?})", self.key),
114            Some(v) => write!(f, "AssetLoadingState::Ready({:?}, {:?})", self.key, v),
115        }
116    }
117}