Skip to main content

goud_engine/assets/asset/
asset_state.rs

1//! Loading state tracking for assets.
2
3use std::fmt;
4
5/// Loading state of an asset.
6///
7/// Tracks the lifecycle of an asset from initial request to loaded (or failed).
8/// Used by the asset server to report loading progress.
9///
10/// # State Transitions
11///
12/// ```text
13/// NotLoaded ──▶ Loading ──▶ Loaded
14///                  │
15///                  └──▶ Failed
16/// ```
17///
18/// # FFI Safety
19///
20/// This enum is `#[repr(u8)]` for stable, FFI-compatible representation.
21///
22/// # Example
23///
24/// ```
25/// use goud_engine::assets::AssetState;
26///
27/// let state = AssetState::Loading { progress: 0.5 };
28///
29/// match state {
30///     AssetState::Loading { progress } => {
31///         println!("Loading: {}%", progress * 100.0);
32///     }
33///     AssetState::Loaded => println!("Ready!"),
34///     AssetState::Failed { .. } => println!("Error!"),
35///     _ => {}
36/// }
37/// ```
38#[repr(u8)]
39#[derive(Debug, Clone, PartialEq, Default)]
40pub enum AssetState {
41    /// Asset has not been requested for loading.
42    #[default]
43    NotLoaded = 0,
44
45    /// Asset is currently being loaded.
46    ///
47    /// Progress is a value from 0.0 to 1.0.
48    Loading {
49        /// Loading progress (0.0 to 1.0).
50        progress: f32,
51    } = 1,
52
53    /// Asset has been successfully loaded and is ready for use.
54    Loaded = 2,
55
56    /// Asset loading failed.
57    Failed {
58        /// Error message describing the failure.
59        error: String,
60    } = 3,
61
62    /// Asset was loaded but has been unloaded from memory.
63    Unloaded = 4,
64}
65
66impl AssetState {
67    /// Returns true if the asset is ready for use.
68    ///
69    /// # Example
70    ///
71    /// ```
72    /// use goud_engine::assets::AssetState;
73    ///
74    /// assert!(AssetState::Loaded.is_ready());
75    /// assert!(!AssetState::Loading { progress: 0.5 }.is_ready());
76    /// ```
77    #[inline]
78    pub fn is_ready(&self) -> bool {
79        matches!(self, AssetState::Loaded)
80    }
81
82    /// Returns true if the asset is currently loading.
83    ///
84    /// # Example
85    ///
86    /// ```
87    /// use goud_engine::assets::AssetState;
88    ///
89    /// assert!(AssetState::Loading { progress: 0.5 }.is_loading());
90    /// assert!(!AssetState::Loaded.is_loading());
91    /// ```
92    #[inline]
93    pub fn is_loading(&self) -> bool {
94        matches!(self, AssetState::Loading { .. })
95    }
96
97    /// Returns true if asset loading failed.
98    ///
99    /// # Example
100    ///
101    /// ```
102    /// use goud_engine::assets::AssetState;
103    ///
104    /// let state = AssetState::Failed { error: "File not found".to_string() };
105    /// assert!(state.is_failed());
106    /// ```
107    #[inline]
108    pub fn is_failed(&self) -> bool {
109        matches!(self, AssetState::Failed { .. })
110    }
111
112    /// Returns the loading progress if currently loading.
113    ///
114    /// Returns `None` for non-loading states.
115    ///
116    /// # Example
117    ///
118    /// ```
119    /// use goud_engine::assets::AssetState;
120    ///
121    /// let state = AssetState::Loading { progress: 0.75 };
122    /// assert_eq!(state.progress(), Some(0.75));
123    ///
124    /// assert_eq!(AssetState::Loaded.progress(), None);
125    /// ```
126    #[inline]
127    pub fn progress(&self) -> Option<f32> {
128        match self {
129            AssetState::Loading { progress } => Some(*progress),
130            _ => None,
131        }
132    }
133
134    /// Returns the error message if loading failed.
135    ///
136    /// Returns `None` for non-failed states.
137    ///
138    /// # Example
139    ///
140    /// ```
141    /// use goud_engine::assets::AssetState;
142    ///
143    /// let state = AssetState::Failed { error: "Invalid format".to_string() };
144    /// assert_eq!(state.error(), Some("Invalid format"));
145    ///
146    /// assert_eq!(AssetState::Loaded.error(), None);
147    /// ```
148    #[inline]
149    pub fn error(&self) -> Option<&str> {
150        match self {
151            AssetState::Failed { error } => Some(error),
152            _ => None,
153        }
154    }
155
156    /// Returns the discriminant as a u8 for FFI.
157    ///
158    /// # Example
159    ///
160    /// ```
161    /// use goud_engine::assets::AssetState;
162    ///
163    /// assert_eq!(AssetState::NotLoaded.discriminant(), 0);
164    /// assert_eq!(AssetState::Loading { progress: 0.0 }.discriminant(), 1);
165    /// assert_eq!(AssetState::Loaded.discriminant(), 2);
166    /// ```
167    #[inline]
168    pub fn discriminant(&self) -> u8 {
169        match self {
170            AssetState::NotLoaded => 0,
171            AssetState::Loading { .. } => 1,
172            AssetState::Loaded => 2,
173            AssetState::Failed { .. } => 3,
174            AssetState::Unloaded => 4,
175        }
176    }
177}
178
179impl fmt::Display for AssetState {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        match self {
182            AssetState::NotLoaded => write!(f, "NotLoaded"),
183            AssetState::Loading { progress } => write!(f, "Loading({:.0}%)", progress * 100.0),
184            AssetState::Loaded => write!(f, "Loaded"),
185            AssetState::Failed { error } => write!(f, "Failed({})", error),
186            AssetState::Unloaded => write!(f, "Unloaded"),
187        }
188    }
189}