bevy_cache/system_param.rs
1use bevy::ecs::system::SystemParam;
2use bevy::prelude::*;
3use std::io::Read;
4use std::time::Duration;
5
6use crate::{CacheConfig, CacheEntry, CacheError, CacheManifest};
7
8/// Combined system parameter for the cache manifest and config resources.
9///
10/// This removes the need to request both `ResMut<CacheManifest>` and
11/// `Res<CacheConfig>` in every system that interacts with the cache.
12///
13/// ```rust,ignore
14/// use bevy::prelude::*;
15/// use bevy_cache::prelude::*;
16///
17/// fn cache_screenshot(
18/// mut cache: Cache,
19/// asset_server: Res<AssetServer>,
20/// ) {
21/// cache
22/// .store(
23/// "screenshots/title",
24/// "png",
25/// std::io::Cursor::new(vec![1, 2, 3]),
26/// None,
27/// )
28/// .expect("cache write failed");
29///
30/// let _handle: Handle<Image> = cache
31/// .load_cached(&asset_server, "screenshots/title")
32/// .expect("cache load failed");
33/// }
34/// ```
35#[derive(SystemParam)]
36pub struct Cache<'w> {
37 manifest: ResMut<'w, CacheManifest>,
38 config: Res<'w, CacheConfig>,
39}
40
41impl<'w> Cache<'w> {
42 /// Returns the cache configuration resource.
43 pub fn config(&self) -> &CacheConfig {
44 self.config.as_ref()
45 }
46
47 /// Returns the cache manifest resource.
48 pub fn manifest(&self) -> &CacheManifest {
49 self.manifest.as_ref()
50 }
51
52 /// Returns the cache manifest resource mutably.
53 pub fn manifest_mut(&mut self) -> &mut CacheManifest {
54 self.manifest.as_mut()
55 }
56
57 /// Store data in the cache under `key` using the given file extension.
58 pub fn store<R: Read>(
59 &mut self,
60 key: &str,
61 extension: &str,
62 reader: R,
63 max_age: Option<Duration>,
64 ) -> Result<(), CacheError> {
65 self.manifest.store(self.config.as_ref(), key, extension, reader, max_age)
66 }
67
68 /// Remove a cache entry and its backing file from disk.
69 pub fn remove(&mut self, key: &str) -> Result<(), CacheError> {
70 self.manifest.remove(self.config.as_ref(), key)
71 }
72
73 /// Check whether a key exists in the manifest.
74 pub fn contains(&self, key: &str) -> bool {
75 self.manifest.contains(key)
76 }
77
78 /// Get a manifest entry by key.
79 pub fn get(&self, key: &str) -> Option<&CacheEntry> {
80 self.manifest.get(key)
81 }
82
83 /// Return the `cache://` asset path for `key`, if present.
84 pub fn asset_path(&self, key: &str) -> Option<String> {
85 self.manifest.asset_path(key)
86 }
87
88 /// Check whether the cached file for `key` still exists on disk.
89 pub fn is_cached(&self, key: &str) -> bool {
90 self.manifest.is_cached(self.config.as_ref(), key)
91 }
92
93 /// Returns the total manifest-reported size of cached data in bytes.
94 pub fn total_size_bytes(&self) -> u64 {
95 self.manifest.total_size_bytes()
96 }
97
98 /// Load a cached asset path through the provided [`AssetServer`].
99 ///
100 /// This returns `None` when the key does not exist in the manifest.
101 pub fn load<A: Asset>(&self, asset_server: &AssetServer, key: &str) -> Option<Handle<A>> {
102 self.asset_path(key).map(|path| asset_server.load(path))
103 }
104
105 /// Load a cached asset through the provided [`AssetServer`], returning an
106 /// error when the manifest entry is missing or stale.
107 pub fn load_cached<A: Asset>(
108 &self,
109 asset_server: &AssetServer,
110 key: &str,
111 ) -> Result<Handle<A>, CacheError> {
112 self.manifest.load_cached(self.config.as_ref(), key, asset_server)
113 }
114
115 /// Returns the modified asset for `handle` when a matching
116 /// [`AssetEvent::Modified`] is present in `events`.
117 ///
118 /// This is only available with the `hot_reload` feature enabled.
119 #[cfg(feature = "hot_reload")]
120 pub fn get_if_modified<'a, A: Asset>(
121 &self,
122 handle: &Handle<A>,
123 assets: &'a Assets<A>,
124 messages: &mut MessageReader<AssetEvent<A>>,
125 ) -> Option<&'a A> {
126 for event in messages.read() {
127 if let AssetEvent::Modified { id } = event {
128 if *id == handle.id() {
129 return assets.get(handle);
130 }
131 }
132 }
133
134 None
135 }
136}