Skip to main content

bevy_cache/
system_param.rs

1use bevy::ecs::system::SystemParam;
2use bevy::prelude::*;
3use std::time::Duration;
4use std::{io::Read, path::Path};
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    pub fn cache_dir(&self) -> &Path {
43        self.config.cache_dir.as_path()
44    }
45
46    /// Returns the cache configuration resource.
47    pub fn config(&self) -> &CacheConfig {
48        self.config.as_ref()
49    }
50
51    /// Returns the cache manifest resource.
52    pub fn manifest(&self) -> &CacheManifest {
53        self.manifest.as_ref()
54    }
55
56    /// Returns the cache manifest resource mutably.
57    pub fn manifest_mut(&mut self) -> &mut CacheManifest {
58        self.manifest.as_mut()
59    }
60
61    /// Store data in the cache under `key` using the given file extension.
62    pub fn store<R: Read>(
63        &mut self,
64        key: &str,
65        extension: &str,
66        reader: R,
67        max_age: Option<Duration>,
68    ) -> Result<(), CacheError> {
69        self.manifest
70            .store(self.config.as_ref(), key, extension, reader, max_age)
71    }
72
73    /// Remove a cache entry and its backing file from disk.
74    pub fn remove(&mut self, key: &str) -> Result<(), CacheError> {
75        self.manifest.remove(self.config.as_ref(), key)
76    }
77
78    /// Check whether a key exists in the manifest.
79    pub fn contains(&self, key: &str) -> bool {
80        self.manifest.contains(key)
81    }
82
83    /// Get a manifest entry by key.
84    pub fn get(&self, key: &str) -> Option<&CacheEntry> {
85        self.manifest.get(key)
86    }
87
88    /// Return the `cache://` asset path for `key`, if present.
89    pub fn asset_path(&self, key: &str) -> Option<String> {
90        self.manifest.asset_path(key)
91    }
92
93    /// Check whether the cached file for `key` still exists on disk.
94    pub fn is_cached(&self, key: &str) -> bool {
95        self.manifest.is_cached(self.config.as_ref(), key)
96    }
97
98    /// Returns the total manifest-reported size of cached data in bytes.
99    pub fn total_size_bytes(&self) -> u64 {
100        self.manifest.total_size_bytes()
101    }
102
103    /// Load a cached asset path through the provided [`AssetServer`].
104    ///
105    /// This returns `None` when the key does not exist in the manifest.
106    pub fn load<A: Asset>(&self, asset_server: &AssetServer, key: &str) -> Option<Handle<A>> {
107        self.asset_path(key).map(|path| asset_server.load(path))
108    }
109
110    /// Load a cached asset through the provided [`AssetServer`], returning an
111    /// error when the manifest entry is missing or stale.
112    pub fn load_cached<A: Asset>(
113        &self,
114        asset_server: &AssetServer,
115        key: &str,
116    ) -> Result<Handle<A>, CacheError> {
117        self.manifest
118            .load_cached(self.config.as_ref(), key, asset_server)
119    }
120
121    /// Returns the modified asset for `handle` when a matching
122    /// [`AssetEvent::Modified`] is present in `events`.
123    ///
124    /// This is only available with the `hot_reload` feature enabled.
125    #[cfg(feature = "hot_reload")]
126    pub fn get_if_modified<'a, A: Asset>(
127        &self,
128        handle: &Handle<A>,
129        assets: &'a Assets<A>,
130        messages: &'a mut MessageReader<AssetEvent<A>>,
131    ) -> Option<&'a A> {
132        for event in messages.read() {
133            if let AssetEvent::Modified { id } = event {
134                if *id == handle.id() {
135                    return assets.get(handle);
136                }
137            }
138        }
139
140        None
141    }
142}