use crate::model::cover;
use bezel::gpui::{
App, AppContext as _, Entity, Global, ImageCache, ImageCacheError, ImageCacheItem, RenderImage,
Resource, Window, hash,
};
use lru::LruCache;
use std::{num::NonZeroUsize, sync::Arc};
pub const DEFAULT_LIMIT: u64 = 200 * 1_000_000;
pub struct Cache {
pub covers: Entity<Covers>,
}
impl Global for Cache {}
pub struct Covers(LruCache<u64, ImageCacheItem>);
fn capacity(limit: u64) -> NonZeroUsize {
NonZeroUsize::new((limit / cover::RASTER_BYTES).max(1) as usize).expect("max(1) is non-zero")
}
impl Covers {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn limit(&self) -> u64 {
self.0.cap().get() as u64 * cover::RASTER_BYTES
}
pub fn set_limit(&mut self, limit: u64, window: &mut Window, cx: &mut App) {
let cap = capacity(limit);
while self.0.len() > cap.get() {
let Some((_, mut item)) = self.0.pop_lru() else {
break;
};
drop_frame(&mut item, window, cx);
}
self.0.resize(cap);
}
}
fn drop_frame(item: &mut ImageCacheItem, window: &mut Window, cx: &mut App) {
if let Some(Ok(image)) = item.get() {
cx.drop_image(image, Some(window));
}
}
impl ImageCache for Covers {
fn load(
&mut self,
resource: &Resource,
window: &mut Window,
cx: &mut App,
) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
let key = hash(resource);
if let Some(item) = self.0.get_mut(&key) {
return item.use_image(window);
}
let item = ImageCacheItem::new(resource, cx);
let loaded = item.use_image(window);
if let Some((_, mut evicted)) = self.0.push(key, item) {
drop_frame(&mut evicted, window, cx);
}
loaded
}
}
pub fn init(limit: u64, cx: &mut App) {
let covers = cx.new(|_| Covers(LruCache::new(capacity(limit))));
cx.set_global(Cache { covers });
}
pub fn covers(cx: &App) -> Entity<Covers> {
cx.global::<Cache>().covers.clone()
}