use bevy::prelude::*;
use std::path::{Component, Path, PathBuf};
use std::time::Duration;
#[derive(Debug, Clone, Resource)]
pub struct CacheConfig {
pub app_name: String,
pub cache_dir: PathBuf,
pub max_age: Duration,
pub max_entries: Option<usize>,
}
impl CacheConfig {
pub fn new(app_name: &str) -> Self {
Self {
app_name: app_name.to_owned(),
cache_dir: resolve_cache_dir(app_name),
max_age: Duration::from_secs(604_800), max_entries: None,
}
}
pub fn manifest_file_name(&self) -> String {
format!("{}.cache_manifest", self.app_name)
}
pub fn file_path(&self, file_name: &str) -> PathBuf {
self.cache_dir.join(file_name)
}
pub fn manifest_fs_path(&self) -> PathBuf {
self.cache_dir.join(self.manifest_file_name())
}
pub fn ensure_cache_dir(&self) -> Result<(), std::io::Error> {
std::fs::create_dir_all(&self.cache_dir)
}
pub fn validate_key(&self, key: &str) -> Result<(), crate::CacheError> {
if key.is_empty()
|| key.contains('\0')
|| key.contains('\\')
|| key.starts_with('/')
|| key.ends_with('/')
|| key.split('/').any(str::is_empty)
|| key == self.manifest_file_name()
{
return Err(crate::CacheError::InvalidKey(key.to_owned()));
}
for component in Path::new(key).components() {
match component {
Component::Normal(_) => {}
Component::CurDir
| Component::ParentDir
| Component::RootDir
| Component::Prefix(_) => {
return Err(crate::CacheError::InvalidKey(key.to_owned()));
}
}
}
Ok(())
}
}
impl Default for CacheConfig {
fn default() -> Self {
Self::new("bevy_cache")
}
}
fn resolve_cache_dir(app_name: &str) -> PathBuf {
sysdirs::cache_dir()
.unwrap_or_else(std::env::temp_dir)
.join(app_name)
}