Skip to main content

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}