feat: add cache and gtk benches comparison
This commit is contained in:
parent
e40fbfa916
commit
8de66cc58e
5 changed files with 190 additions and 45 deletions
|
|
@ -15,6 +15,7 @@ once_cell = "1.10.0"
|
||||||
speculoos = "0.9.0"
|
speculoos = "0.9.0"
|
||||||
anyhow = "1.0.57"
|
anyhow = "1.0.57"
|
||||||
linicon = "2.3.0"
|
linicon = "2.3.0"
|
||||||
|
gtk4 = "0.4.7"
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
|
|
|
||||||
|
|
@ -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 freedesktop_icons::lookup;
|
||||||
|
use gtk4::{IconLookupFlags, IconTheme, TextDirection};
|
||||||
|
|
||||||
pub fn simple_lookup(c: &mut Criterion) {
|
pub fn simple_lookup(c: &mut Criterion) {
|
||||||
c.bench_function("lookup firefox", |b| {
|
c.bench_function("lookup firefox", |b| b.iter(|| lookup("firefox").find()));
|
||||||
b.iter(|| lookup("firefox").find_one())
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simple_lookup_linicon(c: &mut Criterion) {
|
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);
|
criterion_main!(benches);
|
||||||
|
|
|
||||||
35
src/cache.rs
Normal file
35
src/cache.rs
Normal file
|
|
@ -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<Cache> = Lazy::new(Cache::default);
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct Cache(Mutex<BTreeMap<String, BTreeMap<(String, u16, u16), PathBuf>>>);
|
||||||
|
|
||||||
|
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<PathBuf> {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
162
src/lib.rs
162
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 crate::theme::{try_build_icon_path, THEMES};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub mod theme;
|
mod cache;
|
||||||
|
mod theme;
|
||||||
pub fn lookup(name: &str) -> LookupBuilder {
|
|
||||||
LookupBuilder::new(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// The lookup builder struct, holding all the lookup query parameters.
|
||||||
pub struct LookupBuilder<'a> {
|
pub struct LookupBuilder<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
scale: u16,
|
scale: u16,
|
||||||
|
|
@ -14,22 +49,86 @@ pub struct LookupBuilder<'a> {
|
||||||
theme: Option<&'a str>,
|
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> {
|
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 {
|
pub fn with_size(mut self, scale: u16) -> Self {
|
||||||
self.scale = scale;
|
self.scale = scale;
|
||||||
self
|
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 {
|
pub fn with_scale(mut self, size: u16) -> Self {
|
||||||
self.size = size;
|
self.size = size;
|
||||||
self
|
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 {
|
pub fn with_theme<'b: 'a>(mut self, theme: &'b str) -> Self {
|
||||||
self.theme = Some(theme);
|
self.theme = Some(theme);
|
||||||
self
|
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<PathBuf> {
|
||||||
|
// 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 {
|
fn new<'b: 'a>(name: &'b str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
|
|
@ -39,37 +138,26 @@ impl<'a> LookupBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_one(self) -> Option<PathBuf> {
|
// Recursively lookup for icon in the given theme and its parents
|
||||||
let name = self.name;
|
fn lookup_in_theme(&self, theme: &str) -> Option<PathBuf> {
|
||||||
let size = self.size;
|
let cached = CACHE.get(theme, self.size, self.scale, self.name);
|
||||||
let scale = self.scale;
|
if cached.is_some() {
|
||||||
|
return cached;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last chance
|
THEMES.get(theme).and_then(|icon_theme| {
|
||||||
try_build_icon_path(name, "/usr/share/pixmaps")
|
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]
|
#[test]
|
||||||
fn simple_lookup() {
|
fn simple_lookup() {
|
||||||
let firefox = lookup("firefox").find_one();
|
let firefox = lookup("firefox").find();
|
||||||
assert_that!(firefox).is_some();
|
assert_that!(firefox).is_some();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,7 +185,7 @@ mod test {
|
||||||
.with_size(16)
|
.with_size(16)
|
||||||
.with_scale(1)
|
.with_scale(1)
|
||||||
.with_theme("Papirus")
|
.with_theme("Papirus")
|
||||||
.find_one();
|
.find();
|
||||||
|
|
||||||
assert_that!(wireshark).is_some().is_equal_to(lin_wireshark)
|
assert_that!(wireshark).is_some().is_equal_to(lin_wireshark)
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +196,7 @@ mod test {
|
||||||
.with_size(16)
|
.with_size(16)
|
||||||
.with_scale(1)
|
.with_scale(1)
|
||||||
.with_theme("Papirus")
|
.with_theme("Papirus")
|
||||||
.find_one();
|
.find();
|
||||||
|
|
||||||
assert_that!(archlinux_logo)
|
assert_that!(archlinux_logo)
|
||||||
.is_some()
|
.is_some()
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ use std::collections::BTreeMap;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub mod directories;
|
mod directories;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod parse;
|
mod parse;
|
||||||
pub mod paths;
|
mod paths;
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, ThemeError>;
|
type Result<T> = std::result::Result<T, ThemeError>;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue