feat: improve cache, add ScaledDirectories lookup and parent lookup
This commit is contained in:
parent
bc903c1bcb
commit
73702f6049
3 changed files with 133 additions and 37 deletions
110
src/lib.rs
110
src/lib.rs
|
|
@ -34,6 +34,22 @@
|
|||
//! .find();
|
||||
//! # }
|
||||
//!```
|
||||
//! **Cache:**
|
||||
//!
|
||||
//! If your application is going to repeat the same icon lookups multiple time
|
||||
//! you can use the internal cache to improve performance.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # fn main() {
|
||||
//! use freedesktop_icons::lookup;
|
||||
//!
|
||||
//! let icon = lookup("firefox")
|
||||
//! .with_size(48)
|
||||
//! .with_scale(2)
|
||||
//! .with_theme("Arc")
|
||||
//! .with_cache()
|
||||
//! .find();
|
||||
//! # }
|
||||
use crate::cache::CACHE;
|
||||
use crate::theme::{try_build_icon_path, THEMES};
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -41,9 +57,36 @@ use std::path::PathBuf;
|
|||
mod cache;
|
||||
mod theme;
|
||||
|
||||
/// Return the list of installed themes on the system
|
||||
///
|
||||
/// ## Example
|
||||
/// ```rust
|
||||
/// # fn main() {
|
||||
/// use freedesktop_icons::list_themes;
|
||||
///
|
||||
/// let themes: Vec<&str> = list_themes();
|
||||
///
|
||||
/// assert_eq!(themes, vec![
|
||||
/// "Adwaita", "Arc", "Breeze Light", "HighContrast", "Papirus", "Papirus-Dark",
|
||||
/// "Papirus-Light", "Breeze", "Breeze Dark", "Breeze", "ePapirus", "ePapirus-Dark", "Hicolor"
|
||||
/// ])
|
||||
/// # }
|
||||
pub fn list_themes() -> Vec<&'static str> {
|
||||
THEMES
|
||||
.values()
|
||||
.map(|path| &path.index)
|
||||
.filter_map(|index| {
|
||||
index
|
||||
.section(Some("Icon Theme"))
|
||||
.and_then(|section| section.get("Name"))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The lookup builder struct, holding all the lookup query parameters.
|
||||
pub struct LookupBuilder<'a> {
|
||||
name: &'a str,
|
||||
cache: bool,
|
||||
scale: u16,
|
||||
size: u16,
|
||||
theme: Option<&'a str>,
|
||||
|
|
@ -110,6 +153,26 @@ impl<'a> LookupBuilder<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Store the result of the lookup in cache, subsequent
|
||||
/// lookup will first try to retrieve get the cached icon.
|
||||
/// This can drastically increase lookup performances for application
|
||||
/// that repeat the same lookups, an application launcher for instance.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```rust
|
||||
/// # fn main() {
|
||||
/// use freedesktop_icons::lookup;
|
||||
///
|
||||
/// let icon = lookup("firefox")
|
||||
/// .with_scale(2)
|
||||
/// .with_cache()
|
||||
/// .find();
|
||||
/// # }
|
||||
pub fn with_cache(mut self) -> Self {
|
||||
self.cache = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute the current lookup
|
||||
/// if no icon is found in the current theme fallback to
|
||||
/// `/usr/shar/hicolor` theme and then to `/usr/share/pixmaps`.
|
||||
|
|
@ -132,6 +195,7 @@ impl<'a> LookupBuilder<'a> {
|
|||
fn new<'b: 'a>(name: &'b str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
cache: false,
|
||||
scale: 1,
|
||||
size: 24,
|
||||
theme: None,
|
||||
|
|
@ -140,23 +204,45 @@ impl<'a> LookupBuilder<'a> {
|
|||
|
||||
// Recursively lookup for icon in the given theme and its parents
|
||||
fn lookup_in_theme(&self, theme: &str) -> Option<PathBuf> {
|
||||
let cached = CACHE.get(theme, self.size, self.scale, self.name);
|
||||
if cached.is_some() {
|
||||
return cached;
|
||||
// If cache is activated, attempt to get the icon there first
|
||||
if self.cache {
|
||||
let cached = self.cache_lookup(theme);
|
||||
if cached.is_some() {
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
// Then lookup in the given theme
|
||||
THEMES.get(theme).and_then(|icon_theme| {
|
||||
icon_theme
|
||||
let icon = icon_theme
|
||||
.try_get_icon(self.name, self.size, self.scale)
|
||||
.or_else(|| {
|
||||
icon_theme
|
||||
.inherits()
|
||||
.and_then(|parent| self.lookup_in_theme(parent))
|
||||
})
|
||||
.map(|icon| {
|
||||
CACHE.insert(theme, self.size, self.scale, self.name, &icon);
|
||||
icon
|
||||
})
|
||||
// Fallback to the parent themes recursively
|
||||
icon_theme.inherits().into_iter().find_map(|parent| {
|
||||
THEMES.get(parent).and_then(|parent| {
|
||||
parent.try_get_icon(self.name, self.size, self.scale)
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
if self.cache {
|
||||
self.store(theme, icon)
|
||||
} else {
|
||||
icon
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cache_lookup(&self, theme: &str) -> Option<PathBuf> {
|
||||
CACHE.get(theme, self.size, self.scale, self.name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn store(&self, theme: &str, icon: Option<PathBuf>) -> Option<PathBuf> {
|
||||
icon.map(|icon| {
|
||||
CACHE.insert(theme, self.size, self.scale, self.name, &icon);
|
||||
icon
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ pub static THEMES: Lazy<BTreeMap<String, Theme>> =
|
|||
Lazy::new(|| get_all_themes().expect("Failed to get theme paths"));
|
||||
|
||||
pub struct Theme {
|
||||
path: ThemePath,
|
||||
index: Ini,
|
||||
pub path: ThemePath,
|
||||
pub index: Ini,
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
|
|
@ -30,18 +30,15 @@ impl Theme {
|
|||
|
||||
fn try_get_icon_exact_size(&self, name: &str, size: u16, scale: u16) -> Option<PathBuf> {
|
||||
self.match_size(size, scale)
|
||||
.iter()
|
||||
.find_map(|path| try_build_icon_path(name, path))
|
||||
}
|
||||
|
||||
fn match_size(&self, size: u16, scale: u16) -> Vec<PathBuf> {
|
||||
fn match_size(&self, size: u16, scale: u16) -> impl Iterator<Item = PathBuf> + '_ {
|
||||
let dirs = self.get_all_directories();
|
||||
|
||||
dirs.iter()
|
||||
.filter(|directory| directory.match_size(size, scale))
|
||||
dirs.filter(move |directory| directory.match_size(size, scale))
|
||||
.map(|dir| dir.name)
|
||||
.map(|dir| self.path().join(dir))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn try_get_icon_closest_size(&self, name: &str, size: u16, scale: u16) -> Option<PathBuf> {
|
||||
|
|
@ -53,8 +50,7 @@ impl Theme {
|
|||
fn closest_match_size(&self, size: u16, scale: u16) -> Vec<PathBuf> {
|
||||
let dirs = self.get_all_directories();
|
||||
|
||||
dirs.iter()
|
||||
.filter(|directory| directory.directory_size_distance(size, scale) < i16::MAX)
|
||||
dirs.filter(|directory| directory.directory_size_distance(size, scale) < i16::MAX)
|
||||
.map(|dir| dir.name)
|
||||
.map(|dir| self.path().join(dir))
|
||||
.collect()
|
||||
|
|
@ -100,18 +96,6 @@ pub(super) fn get_all_themes() -> Result<BTreeMap<String, Theme>> {
|
|||
Ok(icon_themes)
|
||||
}
|
||||
|
||||
pub fn theme_names() -> Vec<&'static str> {
|
||||
THEMES
|
||||
.values()
|
||||
.map(|path| &path.index)
|
||||
.filter_map(|index| {
|
||||
index
|
||||
.section(Some("Icon Theme"))
|
||||
.and_then(|section| section.get("Name"))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
fn from_path<P: AsRef<Path>>(path: P) -> Option<Self> {
|
||||
let path = path.as_ref();
|
||||
|
|
|
|||
|
|
@ -3,11 +3,15 @@ use crate::theme::Theme;
|
|||
use ini::Properties;
|
||||
|
||||
impl Theme {
|
||||
pub(super) fn get_all_directories(&self) -> Vec<Directory> {
|
||||
pub(super) fn get_all_directories(&self) -> impl Iterator<Item = Directory> {
|
||||
self.directories()
|
||||
.iter()
|
||||
.into_iter()
|
||||
.filter_map(|name| self.get_directory(name))
|
||||
.collect()
|
||||
.chain(
|
||||
self.scaled_directories()
|
||||
.into_iter()
|
||||
.filter_map(|name| self.get_directory(name)),
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: use me
|
||||
|
|
@ -22,9 +26,11 @@ impl Theme {
|
|||
self.index.section(Some("Icon Theme"))
|
||||
}
|
||||
|
||||
pub fn inherits(&self) -> Option<&str> {
|
||||
pub fn inherits(&self) -> Vec<&str> {
|
||||
self.get_icon_theme_section()
|
||||
.and_then(|props| props.get("Inherits"))
|
||||
.map(|parents| parents.split(',').collect())
|
||||
.unwrap_or(vec![])
|
||||
}
|
||||
|
||||
fn directories(&self) -> Vec<&str> {
|
||||
|
|
@ -69,3 +75,23 @@ impl Theme {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::THEMES;
|
||||
use speculoos::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn should_get_theme_parents() {
|
||||
let theme = THEMES.get("Arc").unwrap();
|
||||
let parents = theme.inherits();
|
||||
assert_that!(parents).is_equal_to(vec![
|
||||
"Moka",
|
||||
"Faba",
|
||||
"elementary",
|
||||
"Adwaita",
|
||||
"gnome",
|
||||
"hicolor",
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue