fret_runtime/
asset_reload.rs1use crate::ui_host::GlobalsHost;
2
3#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
8pub struct AssetReloadEpoch(pub u64);
9
10impl AssetReloadEpoch {
11 pub fn bump(&mut self) {
12 self.0 = self.0.wrapping_add(1);
13 }
14}
15
16#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
22pub struct AssetReloadSupport {
23 pub file_watch: bool,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum AssetReloadBackendKind {
29 PollMetadata,
30 NativeWatcher,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum AssetReloadFallbackReason {
36 WatcherInstallFailed,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct AssetReloadStatus {
48 pub configured_backend: AssetReloadBackendKind,
49 pub active_backend: AssetReloadBackendKind,
50 pub fallback_reason: Option<AssetReloadFallbackReason>,
51 pub fallback_message: Option<String>,
52}
53
54pub fn asset_reload_epoch(host: &impl GlobalsHost) -> Option<AssetReloadEpoch> {
55 host.global::<AssetReloadEpoch>().copied()
56}
57
58pub fn bump_asset_reload_epoch(host: &mut impl GlobalsHost) {
59 host.with_global_mut(AssetReloadEpoch::default, |epoch, _host| {
60 epoch.bump();
61 });
62}
63
64pub fn asset_reload_support(host: &impl GlobalsHost) -> Option<AssetReloadSupport> {
65 host.global::<AssetReloadSupport>().copied()
66}
67
68pub fn set_asset_reload_support(host: &mut impl GlobalsHost, support: AssetReloadSupport) {
69 host.set_global(support);
70}
71
72pub fn asset_reload_status(host: &impl GlobalsHost) -> Option<AssetReloadStatus> {
73 host.global::<AssetReloadStatus>().cloned()
74}
75
76pub fn set_asset_reload_status(host: &mut impl GlobalsHost, status: AssetReloadStatus) {
77 host.set_global(status);
78}
79
80#[cfg(test)]
81mod tests {
82 use std::any::{Any, TypeId};
83 use std::collections::HashMap;
84
85 use super::{
86 AssetReloadBackendKind, AssetReloadEpoch, AssetReloadFallbackReason, AssetReloadStatus,
87 AssetReloadSupport, asset_reload_epoch, asset_reload_status, asset_reload_support,
88 bump_asset_reload_epoch, set_asset_reload_status, set_asset_reload_support,
89 };
90 use crate::ui_host::GlobalsHost;
91
92 #[derive(Default)]
93 struct TestHost {
94 globals: HashMap<TypeId, Box<dyn Any>>,
95 }
96
97 impl GlobalsHost for TestHost {
98 fn set_global<T: Any>(&mut self, value: T) {
99 self.globals.insert(TypeId::of::<T>(), Box::new(value));
100 }
101
102 fn global<T: Any>(&self) -> Option<&T> {
103 self.globals.get(&TypeId::of::<T>())?.downcast_ref::<T>()
104 }
105
106 fn with_global_mut<T: Any, R>(
107 &mut self,
108 init: impl FnOnce() -> T,
109 f: impl FnOnce(&mut T, &mut Self) -> R,
110 ) -> R {
111 let type_id = TypeId::of::<T>();
112 let mut value = match self.globals.remove(&type_id) {
113 None => init(),
114 Some(v) => *v.downcast::<T>().expect("global type id must match"),
115 };
116 let out = f(&mut value, self);
117 self.globals.insert(type_id, Box::new(value));
118 out
119 }
120 }
121
122 #[test]
123 fn bump_asset_reload_epoch_initializes_and_increments_global() {
124 let mut host = TestHost::default();
125 assert_eq!(asset_reload_epoch(&host), None);
126
127 bump_asset_reload_epoch(&mut host);
128 assert_eq!(asset_reload_epoch(&host), Some(AssetReloadEpoch(1)));
129
130 bump_asset_reload_epoch(&mut host);
131 assert_eq!(asset_reload_epoch(&host), Some(AssetReloadEpoch(2)));
132 }
133
134 #[test]
135 fn set_asset_reload_support_publishes_host_support_flags() {
136 let mut host = TestHost::default();
137 assert_eq!(asset_reload_support(&host), None);
138
139 set_asset_reload_support(&mut host, AssetReloadSupport { file_watch: true });
140 assert_eq!(
141 asset_reload_support(&host),
142 Some(AssetReloadSupport { file_watch: true })
143 );
144 }
145
146 #[test]
147 fn set_asset_reload_status_publishes_active_backend_and_fallback() {
148 let mut host = TestHost::default();
149 assert_eq!(asset_reload_status(&host), None);
150
151 set_asset_reload_status(
152 &mut host,
153 AssetReloadStatus {
154 configured_backend: AssetReloadBackendKind::NativeWatcher,
155 active_backend: AssetReloadBackendKind::PollMetadata,
156 fallback_reason: Some(AssetReloadFallbackReason::WatcherInstallFailed),
157 fallback_message: Some("watch root missing".to_string()),
158 },
159 );
160
161 assert_eq!(
162 asset_reload_status(&host),
163 Some(AssetReloadStatus {
164 configured_backend: AssetReloadBackendKind::NativeWatcher,
165 active_backend: AssetReloadBackendKind::PollMetadata,
166 fallback_reason: Some(AssetReloadFallbackReason::WatcherInstallFailed),
167 fallback_message: Some("watch root missing".to_string()),
168 })
169 );
170 }
171}