use super::parsing::extract_yaml_block;
use crate::types::{CursorShaderMetadata, ShaderMetadata};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug)]
pub struct MetadataCache<T>
where
T: for<'de> serde::Deserialize<'de>,
{
cache: HashMap<String, Option<T>>,
shaders_dir: Option<PathBuf>,
}
impl<T> Default for MetadataCache<T>
where
T: for<'de> serde::Deserialize<'de>,
{
fn default() -> Self {
Self {
cache: HashMap::new(),
shaders_dir: None,
}
}
}
impl<T> MetadataCache<T>
where
T: for<'de> serde::Deserialize<'de>,
{
pub fn new() -> Self {
Self::default()
}
pub fn with_shaders_dir(shaders_dir: PathBuf) -> Self {
Self {
cache: HashMap::new(),
shaders_dir: Some(shaders_dir),
}
}
pub fn set_shaders_dir(&mut self, shaders_dir: PathBuf) {
self.shaders_dir = Some(shaders_dir);
}
pub fn get(&mut self, shader_name: &str) -> Option<&T>
where
T: std::fmt::Debug,
{
if self.cache.contains_key(shader_name) {
return self.cache.get(shader_name).and_then(|m| m.as_ref());
}
let metadata = self.load_metadata(shader_name);
self.cache.insert(shader_name.to_string(), metadata);
self.cache.get(shader_name).and_then(|m| m.as_ref())
}
pub fn get_fresh(&self, shader_name: &str) -> Option<T> {
self.load_metadata(shader_name)
}
fn load_metadata(&self, shader_name: &str) -> Option<T> {
let path = self.resolve_shader_path(shader_name)?;
let source = std::fs::read_to_string(&path)
.map_err(|e| {
log::warn!("Failed to read shader file '{}': {}", path.display(), e);
})
.ok()?;
let yaml_trimmed = extract_yaml_block(&source)?;
match serde_yaml_ng::from_str(yaml_trimmed) {
Ok(metadata) => Some(metadata),
Err(e) => {
log::warn!(
"Failed to parse shader metadata YAML from '{}': {}",
path.display(),
e
);
None
}
}
}
fn resolve_shader_path(&self, shader_name: &str) -> Option<PathBuf> {
let shader_path = PathBuf::from(shader_name);
if shader_path.is_absolute() && shader_path.exists() {
return Some(shader_path);
}
if let Some(ref shaders_dir) = self.shaders_dir {
let full_path = shaders_dir.join(shader_name);
if full_path.exists() {
return Some(full_path);
}
}
let default_path = crate::config::Config::shader_path(shader_name);
if default_path.exists() {
return Some(default_path);
}
None
}
pub fn invalidate(&mut self, shader_name: &str) {
self.cache.remove(shader_name);
log::debug!("Invalidated metadata cache for: {}", shader_name);
}
pub fn invalidate_all(&mut self) {
self.cache.clear();
log::debug!("Invalidated all metadata cache entries");
}
pub fn is_cached(&self, shader_name: &str) -> bool {
self.cache.contains_key(shader_name)
}
pub fn cache_size(&self) -> usize {
self.cache.len()
}
}
pub type ShaderMetadataCache = MetadataCache<ShaderMetadata>;
pub type CursorShaderMetadataCache = MetadataCache<CursorShaderMetadata>;