feat: resettable cache and icon retry after time

This commit is contained in:
Michael Aaron Murphy 2025-04-02 17:21:11 +02:00
parent a28483f3d1
commit 09a76900a6
No known key found for this signature in database
GPG key ID: B2732D4240C9212C
5 changed files with 63 additions and 17 deletions

View file

@ -10,11 +10,10 @@ readme = "README.md"
keywords = ["icons", "gui", "freedesktop"]
[dependencies]
dirs = "5.0.1"
thiserror = "1.0.56"
once_cell = "1.19.0"
xdg = "2.5.2"
tracing = "0.1.41"
dirs = "5.0"
thiserror = "2.0"
xdg = "2.5"
tracing = "0.1.0"
ini_core = "0.2.0"
[dev-dependencies]

View file

@ -1,9 +1,10 @@
use once_cell::sync::Lazy;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use std::sync::Mutex;
use std::time::Instant;
pub(crate) static CACHE: Lazy<Cache> = Lazy::new(Cache::default);
pub(crate) static CACHE: LazyLock<Cache> = LazyLock::new(Cache::default);
type IconMap = BTreeMap<(String, u16, u16), CacheEntry>;
type ThemeMap = BTreeMap<String, IconMap>;
@ -13,7 +14,7 @@ pub(crate) struct Cache(Mutex<ThemeMap>);
#[derive(Debug, Clone, PartialEq)]
pub enum CacheEntry {
// We already looked for this and nothing was found, indicates we should not try to perform a lookup.
NotFound,
NotFound(Instant),
// We have this entry.
Found(PathBuf),
// We don't know this entry yet, indicate we should perform a lookup.
@ -21,6 +22,10 @@ pub enum CacheEntry {
}
impl Cache {
pub fn clear(&self) {
self.0.lock().unwrap().clear();
}
pub fn insert<P: AsRef<Path>>(
&self,
theme: &str,
@ -33,7 +38,7 @@ impl Cache {
let entry = icon_path
.as_ref()
.map(|path| CacheEntry::Found(path.as_ref().to_path_buf()))
.unwrap_or(CacheEntry::NotFound);
.unwrap_or(CacheEntry::NotFound(Instant::now()));
match theme_map.get_mut(theme) {
Some(icon_map) => {
@ -56,4 +61,16 @@ impl Cache {
.and_then(|path| path.cloned())
.unwrap_or(CacheEntry::Unknown)
}
pub fn reset_none(&self) {
let mut theme_map = self.0.lock().unwrap();
for (_theme_name, theme) in theme_map.iter_mut() {
for (_icon_data, cached_icon) in theme.iter_mut() {
if matches!(cached_icon, CacheEntry::NotFound(_)) {
*cached_icon = CacheEntry::Unknown;
}
}
}
}
}

View file

@ -57,6 +57,7 @@ use crate::cache::{CacheEntry, CACHE};
use crate::theme::{try_build_icon_path, THEMES};
use std::io::BufRead;
use std::path::PathBuf;
use std::time::Instant;
mod cache;
mod theme;
@ -189,6 +190,7 @@ impl<'a> LookupBuilder<'a> {
/// .with_size(48)
/// .find();
/// # }
#[inline]
pub fn with_size(mut self, size: u16) -> Self {
self.size = size;
self
@ -205,6 +207,7 @@ impl<'a> LookupBuilder<'a> {
/// .with_scale(2)
/// .find();
/// # }
#[inline]
pub fn with_scale(mut self, scale: u16) -> Self {
self.scale = scale;
self
@ -220,6 +223,7 @@ impl<'a> LookupBuilder<'a> {
/// .with_theme("Papirus")
/// .find();
/// # }
#[inline]
pub fn with_theme<'b: 'a>(mut self, theme: &'b str) -> Self {
self.theme = theme;
self
@ -240,6 +244,7 @@ impl<'a> LookupBuilder<'a> {
/// .with_cache()
/// .find();
/// # }
#[inline]
pub fn with_cache(mut self) -> Self {
self.cache = true;
self
@ -258,6 +263,7 @@ impl<'a> LookupBuilder<'a> {
/// .force_svg()
/// .find();
/// # }
#[inline]
pub fn force_svg(mut self) -> Self {
self.force_svg = true;
self
@ -266,7 +272,12 @@ impl<'a> LookupBuilder<'a> {
/// Execute the current lookup
/// if no icon is found in the current theme fallback to
/// `/usr/share/icons/hicolor` theme and then to `/usr/share/pixmaps`.
#[inline]
pub fn find(self) -> Option<PathBuf> {
if self.name.is_empty() {
return None;
}
// Lookup for an icon in the given theme and fallback to 'hicolor' default theme
self.lookup_in_theme()
}
@ -290,8 +301,12 @@ impl<'a> LookupBuilder<'a> {
if self.cache {
match self.cache_lookup(self.theme) {
CacheEntry::Found(icon) => return Some(icon),
CacheEntry::NotFound => return None,
_ => ()
CacheEntry::NotFound(last_check)
if last_check.duration_since(Instant::now()).as_secs() < 5 =>
{
return None
}
_ => (),
}
}
@ -364,6 +379,16 @@ impl<'a> LookupBuilder<'a> {
})
}
#[inline]
pub fn cache_clear(&mut self) {
CACHE.clear();
}
#[inline]
pub fn cache_reset_none(&mut self) {
CACHE.reset_none();
}
#[inline]
fn cache_lookup(&self, theme: &str) -> CacheEntry {
CACHE.get(theme, self.size, self.scale, self.name)

View file

@ -1,9 +1,9 @@
use crate::theme::error::ThemeError;
use crate::theme::paths::ThemePath;
use once_cell::sync::Lazy;
pub(crate) use paths::BASE_PATHS;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
mod directories;
pub mod error;
@ -12,8 +12,9 @@ mod paths;
type Result<T> = std::result::Result<T, ThemeError>;
pub static THEMES: Lazy<BTreeMap<String, Vec<Theme>>> = Lazy::new(get_all_themes);
pub static THEMES: LazyLock<BTreeMap<String, Vec<Theme>>> = LazyLock::new(get_all_themes);
#[inline]
pub fn read_ini_theme(path: &Path) -> String {
std::fs::read_to_string(path).unwrap_or_default()
}
@ -25,6 +26,7 @@ pub struct Theme {
}
impl Theme {
#[inline]
pub fn try_get_icon(
&self,
name: &str,
@ -32,11 +34,13 @@ impl Theme {
scale: u16,
force_svg: bool,
) -> Option<PathBuf> {
eprintln!("try_get_icon: {name}");
let file = read_ini_theme(&self.index);
self.try_get_icon_exact_size(file.as_str(), name, size, scale, force_svg)
.or_else(|| self.try_get_icon_closest_size(file.as_str(), name, size, scale, force_svg))
}
#[inline]
fn try_get_icon_exact_size(
&self,
file: &str,
@ -49,6 +53,7 @@ impl Theme {
.find_map(|path| try_build_icon_path(name, path, force_svg))
}
#[inline]
fn match_size<'a>(
&'a self,
file: &'a str,
@ -62,6 +67,7 @@ impl Theme {
.map(|dir| self.path().join(dir))
}
#[inline]
fn try_get_icon_closest_size(
&self,
file: &str,

View file

@ -1,13 +1,12 @@
use std::path::PathBuf;
use dirs::home_dir;
use once_cell::sync::Lazy;
use std::path::PathBuf;
use std::sync::LazyLock;
use xdg::BaseDirectories;
use crate::theme;
use crate::theme::error::ThemeError;
pub(crate) static BASE_PATHS: Lazy<Vec<PathBuf>> = Lazy::new(icon_theme_base_paths);
pub(crate) static BASE_PATHS: LazyLock<Vec<PathBuf>> = LazyLock::new(icon_theme_base_paths);
/// Look in $HOME/.icons (for backwards compatibility), in $XDG_DATA_DIRS/icons, in $XDG_DATA_DIRS/pixmaps and in /usr/share/pixmaps (in that order).
/// Paths that are not found are filtered out.