feat: resettable cache and icon retry after time
This commit is contained in:
parent
a28483f3d1
commit
09a76900a6
5 changed files with 63 additions and 17 deletions
|
|
@ -10,11 +10,10 @@ readme = "README.md"
|
||||||
keywords = ["icons", "gui", "freedesktop"]
|
keywords = ["icons", "gui", "freedesktop"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dirs = "5.0.1"
|
dirs = "5.0"
|
||||||
thiserror = "1.0.56"
|
thiserror = "2.0"
|
||||||
once_cell = "1.19.0"
|
xdg = "2.5"
|
||||||
xdg = "2.5.2"
|
tracing = "0.1.0"
|
||||||
tracing = "0.1.41"
|
|
||||||
ini_core = "0.2.0"
|
ini_core = "0.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
||||||
25
src/cache.rs
25
src/cache.rs
|
|
@ -1,9 +1,10 @@
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::LazyLock;
|
||||||
use std::sync::Mutex;
|
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 IconMap = BTreeMap<(String, u16, u16), CacheEntry>;
|
||||||
type ThemeMap = BTreeMap<String, IconMap>;
|
type ThemeMap = BTreeMap<String, IconMap>;
|
||||||
|
|
||||||
|
|
@ -13,7 +14,7 @@ pub(crate) struct Cache(Mutex<ThemeMap>);
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum CacheEntry {
|
pub enum CacheEntry {
|
||||||
// We already looked for this and nothing was found, indicates we should not try to perform a lookup.
|
// 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.
|
// We have this entry.
|
||||||
Found(PathBuf),
|
Found(PathBuf),
|
||||||
// We don't know this entry yet, indicate we should perform a lookup.
|
// We don't know this entry yet, indicate we should perform a lookup.
|
||||||
|
|
@ -21,6 +22,10 @@ pub enum CacheEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cache {
|
impl Cache {
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.0.lock().unwrap().clear();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert<P: AsRef<Path>>(
|
pub fn insert<P: AsRef<Path>>(
|
||||||
&self,
|
&self,
|
||||||
theme: &str,
|
theme: &str,
|
||||||
|
|
@ -33,7 +38,7 @@ impl Cache {
|
||||||
let entry = icon_path
|
let entry = icon_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|path| CacheEntry::Found(path.as_ref().to_path_buf()))
|
.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) {
|
match theme_map.get_mut(theme) {
|
||||||
Some(icon_map) => {
|
Some(icon_map) => {
|
||||||
|
|
@ -56,4 +61,16 @@ impl Cache {
|
||||||
.and_then(|path| path.cloned())
|
.and_then(|path| path.cloned())
|
||||||
.unwrap_or(CacheEntry::Unknown)
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
src/lib.rs
29
src/lib.rs
|
|
@ -57,6 +57,7 @@ use crate::cache::{CacheEntry, CACHE};
|
||||||
use crate::theme::{try_build_icon_path, THEMES};
|
use crate::theme::{try_build_icon_path, THEMES};
|
||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
mod cache;
|
mod cache;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
@ -189,6 +190,7 @@ impl<'a> LookupBuilder<'a> {
|
||||||
/// .with_size(48)
|
/// .with_size(48)
|
||||||
/// .find();
|
/// .find();
|
||||||
/// # }
|
/// # }
|
||||||
|
#[inline]
|
||||||
pub fn with_size(mut self, size: u16) -> Self {
|
pub fn with_size(mut self, size: u16) -> Self {
|
||||||
self.size = size;
|
self.size = size;
|
||||||
self
|
self
|
||||||
|
|
@ -205,6 +207,7 @@ impl<'a> LookupBuilder<'a> {
|
||||||
/// .with_scale(2)
|
/// .with_scale(2)
|
||||||
/// .find();
|
/// .find();
|
||||||
/// # }
|
/// # }
|
||||||
|
#[inline]
|
||||||
pub fn with_scale(mut self, scale: u16) -> Self {
|
pub fn with_scale(mut self, scale: u16) -> Self {
|
||||||
self.scale = scale;
|
self.scale = scale;
|
||||||
self
|
self
|
||||||
|
|
@ -220,6 +223,7 @@ impl<'a> LookupBuilder<'a> {
|
||||||
/// .with_theme("Papirus")
|
/// .with_theme("Papirus")
|
||||||
/// .find();
|
/// .find();
|
||||||
/// # }
|
/// # }
|
||||||
|
#[inline]
|
||||||
pub fn with_theme<'b: 'a>(mut self, theme: &'b str) -> Self {
|
pub fn with_theme<'b: 'a>(mut self, theme: &'b str) -> Self {
|
||||||
self.theme = theme;
|
self.theme = theme;
|
||||||
self
|
self
|
||||||
|
|
@ -240,6 +244,7 @@ impl<'a> LookupBuilder<'a> {
|
||||||
/// .with_cache()
|
/// .with_cache()
|
||||||
/// .find();
|
/// .find();
|
||||||
/// # }
|
/// # }
|
||||||
|
#[inline]
|
||||||
pub fn with_cache(mut self) -> Self {
|
pub fn with_cache(mut self) -> Self {
|
||||||
self.cache = true;
|
self.cache = true;
|
||||||
self
|
self
|
||||||
|
|
@ -258,6 +263,7 @@ impl<'a> LookupBuilder<'a> {
|
||||||
/// .force_svg()
|
/// .force_svg()
|
||||||
/// .find();
|
/// .find();
|
||||||
/// # }
|
/// # }
|
||||||
|
#[inline]
|
||||||
pub fn force_svg(mut self) -> Self {
|
pub fn force_svg(mut self) -> Self {
|
||||||
self.force_svg = true;
|
self.force_svg = true;
|
||||||
self
|
self
|
||||||
|
|
@ -266,7 +272,12 @@ impl<'a> LookupBuilder<'a> {
|
||||||
/// Execute the current lookup
|
/// Execute the current lookup
|
||||||
/// if no icon is found in the current theme fallback to
|
/// if no icon is found in the current theme fallback to
|
||||||
/// `/usr/share/icons/hicolor` theme and then to `/usr/share/pixmaps`.
|
/// `/usr/share/icons/hicolor` theme and then to `/usr/share/pixmaps`.
|
||||||
|
#[inline]
|
||||||
pub fn find(self) -> Option<PathBuf> {
|
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
|
// Lookup for an icon in the given theme and fallback to 'hicolor' default theme
|
||||||
self.lookup_in_theme()
|
self.lookup_in_theme()
|
||||||
}
|
}
|
||||||
|
|
@ -290,8 +301,12 @@ impl<'a> LookupBuilder<'a> {
|
||||||
if self.cache {
|
if self.cache {
|
||||||
match self.cache_lookup(self.theme) {
|
match self.cache_lookup(self.theme) {
|
||||||
CacheEntry::Found(icon) => return Some(icon),
|
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]
|
#[inline]
|
||||||
fn cache_lookup(&self, theme: &str) -> CacheEntry {
|
fn cache_lookup(&self, theme: &str) -> CacheEntry {
|
||||||
CACHE.get(theme, self.size, self.scale, self.name)
|
CACHE.get(theme, self.size, self.scale, self.name)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::theme::error::ThemeError;
|
use crate::theme::error::ThemeError;
|
||||||
use crate::theme::paths::ThemePath;
|
use crate::theme::paths::ThemePath;
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
pub(crate) use paths::BASE_PATHS;
|
pub(crate) use paths::BASE_PATHS;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
mod directories;
|
mod directories;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
@ -12,8 +12,9 @@ mod paths;
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, ThemeError>;
|
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 {
|
pub fn read_ini_theme(path: &Path) -> String {
|
||||||
std::fs::read_to_string(path).unwrap_or_default()
|
std::fs::read_to_string(path).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
@ -25,6 +26,7 @@ pub struct Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
|
#[inline]
|
||||||
pub fn try_get_icon(
|
pub fn try_get_icon(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|
@ -32,11 +34,13 @@ impl Theme {
|
||||||
scale: u16,
|
scale: u16,
|
||||||
force_svg: bool,
|
force_svg: bool,
|
||||||
) -> Option<PathBuf> {
|
) -> Option<PathBuf> {
|
||||||
|
eprintln!("try_get_icon: {name}");
|
||||||
let file = read_ini_theme(&self.index);
|
let file = read_ini_theme(&self.index);
|
||||||
self.try_get_icon_exact_size(file.as_str(), name, size, scale, force_svg)
|
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))
|
.or_else(|| self.try_get_icon_closest_size(file.as_str(), name, size, scale, force_svg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn try_get_icon_exact_size(
|
fn try_get_icon_exact_size(
|
||||||
&self,
|
&self,
|
||||||
file: &str,
|
file: &str,
|
||||||
|
|
@ -49,6 +53,7 @@ impl Theme {
|
||||||
.find_map(|path| try_build_icon_path(name, path, force_svg))
|
.find_map(|path| try_build_icon_path(name, path, force_svg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn match_size<'a>(
|
fn match_size<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
file: &'a str,
|
file: &'a str,
|
||||||
|
|
@ -62,6 +67,7 @@ impl Theme {
|
||||||
.map(|dir| self.path().join(dir))
|
.map(|dir| self.path().join(dir))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn try_get_icon_closest_size(
|
fn try_get_icon_closest_size(
|
||||||
&self,
|
&self,
|
||||||
file: &str,
|
file: &str,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
use once_cell::sync::Lazy;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::LazyLock;
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
use crate::theme;
|
use crate::theme;
|
||||||
use crate::theme::error::ThemeError;
|
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).
|
/// 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.
|
/// Paths that are not found are filtered out.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue