Skip to main content

goud_engine/assets/storage/
entry.rs

1//! [`AssetEntry`]: individual asset entry with metadata.
2
3use crate::assets::{Asset, AssetPath, AssetState};
4
5// =============================================================================
6// AssetEntry
7// =============================================================================
8
9/// An individual asset entry in storage with metadata.
10///
11/// `AssetEntry` wraps an asset value together with its loading state,
12/// optional path association, and other metadata.
13///
14/// # Example
15///
16/// ```
17/// use goud_engine::assets::{Asset, AssetEntry, AssetState, AssetPath};
18///
19/// struct Texture { width: u32 }
20/// impl Asset for Texture {}
21///
22/// // Create a loaded entry
23/// let entry = AssetEntry::loaded(Texture { width: 256 });
24/// assert!(entry.is_loaded());
25/// assert_eq!(entry.asset().unwrap().width, 256);
26///
27/// // Create entry with path
28/// let entry = AssetEntry::with_path(
29///     Texture { width: 512 },
30///     AssetPath::new("textures/player.png"),
31/// );
32/// assert_eq!(entry.path().map(|p| p.as_str()), Some("textures/player.png"));
33/// ```
34#[derive(Debug, Clone)]
35pub struct AssetEntry<A: Asset> {
36    /// The asset data, if loaded.
37    asset: Option<A>,
38
39    /// Current loading state.
40    state: AssetState,
41
42    /// Optional path this asset was loaded from.
43    path: Option<AssetPath<'static>>,
44}
45
46impl<A: Asset> AssetEntry<A> {
47    /// Creates a new entry in the `NotLoaded` state.
48    ///
49    /// Use this when you want to reserve a handle before the asset is loaded.
50    #[inline]
51    pub fn empty() -> Self {
52        Self {
53            asset: None,
54            state: AssetState::NotLoaded,
55            path: None,
56        }
57    }
58
59    /// Creates a new entry with a loading state.
60    ///
61    /// # Arguments
62    ///
63    /// * `progress` - Initial loading progress (0.0 to 1.0)
64    #[inline]
65    pub fn loading(progress: f32) -> Self {
66        Self {
67            asset: None,
68            state: AssetState::Loading { progress },
69            path: None,
70        }
71    }
72
73    /// Creates a new entry with a loaded asset.
74    #[inline]
75    pub fn loaded(asset: A) -> Self {
76        Self {
77            asset: Some(asset),
78            state: AssetState::Loaded,
79            path: None,
80        }
81    }
82
83    /// Creates a new entry with a loaded asset and path.
84    pub fn with_path(asset: A, path: AssetPath<'static>) -> Self {
85        Self {
86            asset: Some(asset),
87            state: AssetState::Loaded,
88            path: Some(path),
89        }
90    }
91
92    /// Creates a new failed entry with an error message.
93    #[inline]
94    pub fn failed(error: impl Into<String>) -> Self {
95        Self {
96            asset: None,
97            state: AssetState::Failed {
98                error: error.into(),
99            },
100            path: None,
101        }
102    }
103
104    /// Returns a reference to the asset if loaded.
105    #[inline]
106    pub fn asset(&self) -> Option<&A> {
107        self.asset.as_ref()
108    }
109
110    /// Returns a mutable reference to the asset if loaded.
111    #[inline]
112    pub fn asset_mut(&mut self) -> Option<&mut A> {
113        self.asset.as_mut()
114    }
115
116    /// Takes the asset out of this entry, leaving `None`.
117    #[inline]
118    pub fn take_asset(&mut self) -> Option<A> {
119        let asset = self.asset.take();
120        if asset.is_some() {
121            self.state = AssetState::Unloaded;
122        }
123        asset
124    }
125
126    /// Returns a reference to the current state.
127    #[inline]
128    pub fn state(&self) -> &AssetState {
129        &self.state
130    }
131
132    /// Returns the path this asset was loaded from, if any.
133    #[inline]
134    pub fn path(&self) -> Option<&AssetPath<'static>> {
135        self.path.as_ref()
136    }
137
138    /// Sets the path for this entry.
139    #[inline]
140    pub fn set_path(&mut self, path: AssetPath<'static>) {
141        self.path = Some(path);
142    }
143
144    /// Clears the path for this entry.
145    #[inline]
146    pub fn clear_path(&mut self) {
147        self.path = None;
148    }
149
150    /// Returns `true` if the asset is fully loaded.
151    #[inline]
152    pub fn is_loaded(&self) -> bool {
153        self.state.is_ready() && self.asset.is_some()
154    }
155
156    /// Returns `true` if the asset is currently loading.
157    #[inline]
158    pub fn is_loading(&self) -> bool {
159        self.state.is_loading()
160    }
161
162    /// Returns `true` if loading failed.
163    #[inline]
164    pub fn is_failed(&self) -> bool {
165        self.state.is_failed()
166    }
167
168    /// Sets the asset and marks as loaded.
169    pub fn set_loaded(&mut self, asset: A) {
170        self.asset = Some(asset);
171        self.state = AssetState::Loaded;
172    }
173
174    /// Updates the loading progress.
175    pub fn set_progress(&mut self, progress: f32) {
176        self.state = AssetState::Loading { progress };
177    }
178
179    /// Marks the entry as failed.
180    pub fn set_failed(&mut self, error: impl Into<String>) {
181        self.asset = None;
182        self.state = AssetState::Failed {
183            error: error.into(),
184        };
185    }
186
187    /// Marks the entry as unloaded and removes the asset.
188    pub fn set_unloaded(&mut self) {
189        self.asset = None;
190        self.state = AssetState::Unloaded;
191    }
192}
193
194impl<A: Asset> Default for AssetEntry<A> {
195    #[inline]
196    fn default() -> Self {
197        Self::empty()
198    }
199}