From 8de66cc58e9db0bdb9825768d2ee9aa1af4aec9d Mon Sep 17 00:00:00 2001 From: Paul Delafosse Date: Thu, 12 May 2022 23:12:06 +0200 Subject: [PATCH] feat: add cache and gtk benches comparison --- Cargo.toml | 1 + benches/simple_lookup.rs | 31 ++++++-- src/cache.rs | 35 +++++++++ src/lib.rs | 162 ++++++++++++++++++++++++++++++--------- src/theme/mod.rs | 6 +- 5 files changed, 190 insertions(+), 45 deletions(-) create mode 100644 src/cache.rs diff --git a/Cargo.toml b/Cargo.toml index bfd0aab..8c89a34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ once_cell = "1.10.0" speculoos = "0.9.0" anyhow = "1.0.57" linicon = "2.3.0" +gtk4 = "0.4.7" criterion = "0.3" [[bench]] diff --git a/benches/simple_lookup.rs b/benches/simple_lookup.rs index 3fa2a4d..8298cde 100644 --- a/benches/simple_lookup.rs +++ b/benches/simple_lookup.rs @@ -1,10 +1,9 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, criterion_main, Criterion}; use freedesktop_icons::lookup; +use gtk4::{IconLookupFlags, IconTheme, TextDirection}; pub fn simple_lookup(c: &mut Criterion) { - c.bench_function("lookup firefox", |b| { - b.iter(|| lookup("firefox").find_one()) - }); + c.bench_function("lookup firefox", |b| b.iter(|| lookup("firefox").find())); } pub fn simple_lookup_linicon(c: &mut Criterion) { @@ -13,5 +12,27 @@ pub fn simple_lookup_linicon(c: &mut Criterion) { }); } -criterion_group!(benches, simple_lookup, simple_lookup_linicon); +pub fn simple_lookup_gtk(c: &mut Criterion) { + c.bench_function("lookup firefox gkt", |b| { + gtk4::init().unwrap(); + let theme = IconTheme::new(); + b.iter(|| { + theme.lookup_icon( + "firefox", + &[], + 24, + 1, + TextDirection::None, + IconLookupFlags::empty(), + ) + }) + }); +} + +criterion_group!( + benches, + simple_lookup, + simple_lookup_linicon, + simple_lookup_gtk +); criterion_main!(benches); diff --git a/src/cache.rs b/src/cache.rs new file mode 100644 index 0000000..f348b07 --- /dev/null +++ b/src/cache.rs @@ -0,0 +1,35 @@ +use once_cell::sync::Lazy; +use std::collections::BTreeMap; +use std::path::PathBuf; +use std::sync::Mutex; + +pub(crate) static CACHE: Lazy = Lazy::new(Cache::default); + +#[derive(Default)] +pub(crate) struct Cache(Mutex>>); + +impl Cache { + pub fn insert(&self, theme: &str, size: u16, scale: u16, icon_name: &str, icon_path: &PathBuf) { + let mut theme_map = self.0.lock().unwrap(); + + match theme_map.get_mut(theme) { + Some(icon_map) => { + icon_map.insert((icon_name.to_string(), size, scale), icon_path.clone()); + } + None => { + let mut icon_map = BTreeMap::new(); + icon_map.insert((icon_name.to_string(), size, scale), icon_path.clone()); + theme_map.insert(theme.to_string(), icon_map); + } + } + } + + pub fn get(&self, theme: &str, size: u16, scale: u16, icon_name: &str) -> Option { + let theme_map = self.0.lock().unwrap(); + + theme_map + .get(theme) + .map(|icon_map| icon_map.get(&(icon_name.to_string(), size, scale))) + .and_then(|path| path.cloned()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 401f5b9..e7bbbdd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,47 @@ +//! # freedesktop-incons +//! +//! This crate provides a [freedesktop icon](https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#implementation_notes) lookup implementation. +//! +//! It exposes a single [`lookup`] function to find icon based on their, `name`, `theme`, `size` and `scale`. +//! +//! ## Example +//! +//! **Simple lookup:** +//! +//! The following snippet get an icon from the default 'hicolor' theme +//! with the default scale (`1`) and the default size (`24`). +//! +//! ```rust +//! # fn main() { +//! use freedesktop_icons::lookup; +//! +//! let icon = lookup("firefox").find(); +//! # } +//!``` +//! +//! **Complex lookup:** +//! +//! If you have specific requirement for your lookup you can use the provided builder functions: +//! +//! ```rust +//! # fn main() { +//! use freedesktop_icons::lookup; +//! +//! let icon = lookup("firefox") +//! .with_size(48) +//! .with_scale(2) +//! .with_theme("Arc") +//! .find(); +//! # } +//!``` +use crate::cache::CACHE; use crate::theme::{try_build_icon_path, THEMES}; use std::path::PathBuf; -pub mod theme; - -pub fn lookup(name: &str) -> LookupBuilder { - LookupBuilder::new(name) -} +mod cache; +mod theme; +/// The lookup builder struct, holding all the lookup query parameters. pub struct LookupBuilder<'a> { name: &'a str, scale: u16, @@ -14,22 +49,86 @@ pub struct LookupBuilder<'a> { theme: Option<&'a str>, } +/// Build an icon lookup for the given icon name. +/// +/// ## Example +/// ```rust +/// # fn main() { +/// use freedesktop_icons::lookup; +/// +/// let icon = lookup("firefox").find(); +/// # } +pub fn lookup(name: &str) -> LookupBuilder { + LookupBuilder::new(name) +} + impl<'a> LookupBuilder<'a> { + /// Restrict the lookup to the given icon size. + /// + /// ## Example + /// ```rust + /// # fn main() { + /// use freedesktop_icons::lookup; + /// + /// let icon = lookup("firefox") + /// .with_size(48) + /// .find(); + /// # } pub fn with_size(mut self, scale: u16) -> Self { self.scale = scale; self } + /// Restrict the lookup to the given scale. + /// + /// ## Example + /// ```rust + /// # fn main() { + /// use freedesktop_icons::lookup; + /// + /// let icon = lookup("firefox") + /// .with_scale(2) + /// .find(); + /// # } pub fn with_scale(mut self, size: u16) -> Self { self.size = size; self } + /// Add the given theme to the current lookup : + /// ## Example + /// ```rust + /// # fn main() { + /// use freedesktop_icons::lookup; + /// + /// let icon = lookup("firefox") + /// .with_theme("Papirus") + /// .find(); + /// # } pub fn with_theme<'b: 'a>(mut self, theme: &'b str) -> Self { self.theme = Some(theme); 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`. + pub fn find(self) -> Option { + // Lookup for an icon in the given theme and fallback to 'hicolor' default theme + let icon = self + .theme + .and_then(|theme| self.lookup_in_theme(theme)) + .or_else(|| self.lookup_in_theme("hicolor")); + + // Return the icon if found + if icon.is_some() { + return icon; + }; + + // Last chance + try_build_icon_path(self.name, "/usr/share/pixmaps") + } + fn new<'b: 'a>(name: &'b str) -> Self { Self { name, @@ -39,37 +138,26 @@ impl<'a> LookupBuilder<'a> { } } - pub fn find_one(self) -> Option { - let name = self.name; - let size = self.size; - let scale = self.scale; - - // We have a theme name, lookup -> fallback - exit - if let Some(theme) = self.theme.and_then(|theme| THEMES.get(theme)) { - let icon = theme.try_get_icon(name, size, scale); - if icon.is_some() { - return icon; - } - - // hicolor fallback - if let Some(fallback) = THEMES.get("hicolor") { - let icon = fallback.try_get_icon(name, size, scale); - if icon.is_some() { - return icon; - } - } - } else { - // No theme let's look everywhere - for theme in THEMES.values() { - let icon = theme.try_get_icon(name, size, scale); - if icon.is_some() { - return icon; - } - } + // Recursively lookup for icon in the given theme and its parents + fn lookup_in_theme(&self, theme: &str) -> Option { + let cached = CACHE.get(theme, self.size, self.scale, self.name); + if cached.is_some() { + return cached; } - // Last chance - try_build_icon_path(name, "/usr/share/pixmaps") + THEMES.get(theme).and_then(|icon_theme| { + 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 + }) + }) } } @@ -81,7 +169,7 @@ mod test { #[test] fn simple_lookup() { - let firefox = lookup("firefox").find_one(); + let firefox = lookup("firefox").find(); assert_that!(firefox).is_some(); } @@ -97,7 +185,7 @@ mod test { .with_size(16) .with_scale(1) .with_theme("Papirus") - .find_one(); + .find(); assert_that!(wireshark).is_some().is_equal_to(lin_wireshark) } @@ -108,7 +196,7 @@ mod test { .with_size(16) .with_scale(1) .with_theme("Papirus") - .find_one(); + .find(); assert_that!(archlinux_logo) .is_some() diff --git a/src/theme/mod.rs b/src/theme/mod.rs index 67c1356..022d452 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -7,10 +7,10 @@ use std::collections::BTreeMap; use std::fmt::{Debug, Formatter}; use std::path::{Path, PathBuf}; -pub mod directories; +mod directories; pub mod error; -pub mod parse; -pub mod paths; +mod parse; +mod paths; type Result = std::result::Result;