feat: add some benches to compare with linicon
This commit is contained in:
parent
6515a02881
commit
e40fbfa916
6 changed files with 124 additions and 62 deletions
|
|
@ -14,4 +14,9 @@ once_cell = "1.10.0"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
speculoos = "0.9.0"
|
speculoos = "0.9.0"
|
||||||
anyhow = "1.0.57"
|
anyhow = "1.0.57"
|
||||||
linicon = "2.3.0"
|
linicon = "2.3.0"
|
||||||
|
criterion = "0.3"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "simple_lookup"
|
||||||
|
harness = false
|
||||||
17
benches/simple_lookup.rs
Normal file
17
benches/simple_lookup.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
use freedesktop_icons::lookup;
|
||||||
|
|
||||||
|
pub fn simple_lookup(c: &mut Criterion) {
|
||||||
|
c.bench_function("lookup firefox", |b| {
|
||||||
|
b.iter(|| lookup("firefox").find_one())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simple_lookup_linicon(c: &mut Criterion) {
|
||||||
|
c.bench_function("lookup firefox linicon", |b| {
|
||||||
|
b.iter(|| linicon::lookup_icon("firefox").next().unwrap().unwrap())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, simple_lookup, simple_lookup_linicon);
|
||||||
|
criterion_main!(benches);
|
||||||
105
src/lib.rs
105
src/lib.rs
|
|
@ -1,54 +1,117 @@
|
||||||
use crate::theme::{try_build_icon_path, FALL_BACK_THEMES, THEMES};
|
use crate::theme::{try_build_icon_path, THEMES};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
|
|
||||||
pub fn lookup(name: &str, size: u16, scale: u16, theme: &str) -> Option<PathBuf> {
|
pub fn lookup(name: &str) -> LookupBuilder {
|
||||||
if let Some(theme) = THEMES.get(theme) {
|
LookupBuilder::new(name)
|
||||||
let icon = theme.try_get_icon(name, size, scale);
|
}
|
||||||
if icon.is_some() {
|
|
||||||
return icon;
|
pub struct LookupBuilder<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
scale: u16,
|
||||||
|
size: u16,
|
||||||
|
theme: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LookupBuilder<'a> {
|
||||||
|
pub fn with_size(mut self, scale: u16) -> Self {
|
||||||
|
self.scale = scale;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_scale(mut self, size: u16) -> Self {
|
||||||
|
self.size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_theme<'b: 'a>(mut self, theme: &'b str) -> Self {
|
||||||
|
self.theme = Some(theme);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new<'b: 'a>(name: &'b str) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
scale: 1,
|
||||||
|
size: 24,
|
||||||
|
theme: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for theme in FALL_BACK_THEMES.iter() {
|
pub fn find_one(self) -> Option<PathBuf> {
|
||||||
let icon = theme.try_get_icon(name, size, scale);
|
let name = self.name;
|
||||||
if icon.is_some() {
|
let size = self.size;
|
||||||
return icon;
|
let scale = self.scale;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try_build_icon_path(name, "/usr/share/pixmaps")
|
// 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
|
||||||
|
try_build_icon_path(name, "/usr/share/pixmaps")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::lookup;
|
use crate::lookup;
|
||||||
use speculoos::prelude::*;
|
use speculoos::prelude::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_to_linincon() {
|
fn simple_lookup() {
|
||||||
|
let firefox = lookup("firefox").find_one();
|
||||||
|
assert_that!(firefox).is_some();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compare_to_linincon_with_theme() {
|
||||||
let lin_wireshark = linicon::lookup_icon("wireshark")
|
let lin_wireshark = linicon::lookup_icon("wireshark")
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.path;
|
.path;
|
||||||
|
|
||||||
let wireshark = lookup("wireshark", 16, 1, "Papirus");
|
let wireshark = lookup("wireshark")
|
||||||
|
.with_size(16)
|
||||||
|
.with_scale(1)
|
||||||
|
.with_theme("Papirus")
|
||||||
|
.find_one();
|
||||||
|
|
||||||
assert_that!(wireshark).is_some().is_equal_to(lin_wireshark)
|
assert_that!(wireshark).is_some().is_equal_to(lin_wireshark)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_to_linicon_in_pixmap() {
|
fn compare_to_linicon_in_pixmap() {
|
||||||
let archlinux_logo = linicon::lookup_icon("archlinux-logo").next();
|
let archlinux_logo = lookup("archlinux-logo")
|
||||||
|
.with_size(16)
|
||||||
assert_that!(archlinux_logo).is_some();
|
.with_scale(1)
|
||||||
|
.with_theme("Papirus")
|
||||||
let archlinux_logo = lookup("archlinux-logo", 16, 1, "Papirus");
|
.find_one();
|
||||||
|
|
||||||
assert_that!(archlinux_logo)
|
assert_that!(archlinux_logo)
|
||||||
.is_some()
|
.is_some()
|
||||||
.has_file_name("/usr/share/pixmaps/archlinux-logo.png");
|
.is_equal_to(PathBuf::from("/usr/share/pixmaps/archlinux-logo.png"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::theme::error::ThemeError;
|
use crate::theme::error::ThemeError;
|
||||||
use crate::theme::paths::{ThemePath, FALLBACK_PATHS};
|
use crate::theme::paths::ThemePath;
|
||||||
use ini::Ini;
|
use ini::Ini;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use paths::BASE_PATHS;
|
use paths::BASE_PATHS;
|
||||||
|
|
@ -17,9 +17,6 @@ type Result<T> = std::result::Result<T, ThemeError>;
|
||||||
pub static THEMES: Lazy<BTreeMap<String, Theme>> =
|
pub static THEMES: Lazy<BTreeMap<String, Theme>> =
|
||||||
Lazy::new(|| get_all_themes().expect("Failed to get theme paths"));
|
Lazy::new(|| get_all_themes().expect("Failed to get theme paths"));
|
||||||
|
|
||||||
pub static FALL_BACK_THEMES: Lazy<Vec<Theme>> =
|
|
||||||
Lazy::new(|| fallback_themes().expect("Failed to get theme paths"));
|
|
||||||
|
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
path: ThemePath,
|
path: ThemePath,
|
||||||
index: Ini,
|
index: Ini,
|
||||||
|
|
@ -28,7 +25,7 @@ pub struct Theme {
|
||||||
impl Theme {
|
impl Theme {
|
||||||
pub fn try_get_icon(&self, name: &str, size: u16, scale: u16) -> Option<PathBuf> {
|
pub fn try_get_icon(&self, name: &str, size: u16, scale: u16) -> Option<PathBuf> {
|
||||||
self.try_get_icon_exact_size(name, size, scale)
|
self.try_get_icon_exact_size(name, size, scale)
|
||||||
.or(self.try_get_icon_closest_size(name, size, scale))
|
.or_else(|| self.try_get_icon_closest_size(name, size, scale))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_get_icon_exact_size(&self, name: &str, size: u16, scale: u16) -> Option<PathBuf> {
|
fn try_get_icon_exact_size(&self, name: &str, size: u16, scale: u16) -> Option<PathBuf> {
|
||||||
|
|
@ -89,7 +86,7 @@ pub(super) fn try_build_icon_path<P: AsRef<Path>>(name: &str, path: P) -> Option
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iter through the base paths and get all theme directories
|
// Iter through the base paths and get all theme directories
|
||||||
fn get_all_themes() -> Result<BTreeMap<String, Theme>> {
|
pub fn get_all_themes() -> Result<BTreeMap<String, Theme>> {
|
||||||
let mut icon_themes = BTreeMap::new();
|
let mut icon_themes = BTreeMap::new();
|
||||||
for theme_base_dir in BASE_PATHS.iter() {
|
for theme_base_dir in BASE_PATHS.iter() {
|
||||||
for entry in theme_base_dir.read_dir()? {
|
for entry in theme_base_dir.read_dir()? {
|
||||||
|
|
@ -103,20 +100,6 @@ fn get_all_themes() -> Result<BTreeMap<String, Theme>> {
|
||||||
Ok(icon_themes)
|
Ok(icon_themes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fallback_themes() -> Result<Vec<Theme>> {
|
|
||||||
let mut icon_themes = vec![];
|
|
||||||
for theme_base_dir in FALLBACK_PATHS.iter() {
|
|
||||||
for entry in theme_base_dir.read_dir()? {
|
|
||||||
let entry = entry?;
|
|
||||||
if let Some(theme) = Theme::from_path(entry.path()) {
|
|
||||||
icon_themes.push(theme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(icon_themes)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn theme_names() -> Vec<&'static str> {
|
pub fn theme_names() -> Vec<&'static str> {
|
||||||
THEMES
|
THEMES
|
||||||
.values()
|
.values()
|
||||||
|
|
@ -124,7 +107,7 @@ pub fn theme_names() -> Vec<&'static str> {
|
||||||
.filter_map(|index| {
|
.filter_map(|index| {
|
||||||
index
|
index
|
||||||
.section(Some("Icon Theme"))
|
.section(Some("Icon Theme"))
|
||||||
.and_then(|section| section.get("Name").map(|s| s))
|
.and_then(|section| section.get("Name"))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,14 @@ use ini::Properties;
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
pub(super) fn get_all_directories(&self) -> Vec<Directory> {
|
pub(super) fn get_all_directories(&self) -> Vec<Directory> {
|
||||||
let dir_names = self.directories().unwrap_or(vec![]);
|
self.directories()
|
||||||
let mut dirs = vec![];
|
.unwrap_or_default()
|
||||||
for dir in dir_names {
|
.iter()
|
||||||
let dir = self.get_directory(dir);
|
.filter_map(|name| self.get_directory(name))
|
||||||
if let Some(dir) = dir {
|
.collect()
|
||||||
dirs.push(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dirs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use me
|
||||||
fn scaled_directories(&self) -> Option<Vec<&str>> {
|
fn scaled_directories(&self) -> Option<Vec<&str>> {
|
||||||
self.get_icon_theme_section()
|
self.get_icon_theme_section()
|
||||||
.and_then(|props| props.get("ScaledDirectories"))
|
.and_then(|props| props.get("ScaledDirectories"))
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,7 @@ use ini::Ini;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub(crate) static BASE_PATHS: Lazy<Vec<PathBuf>> = Lazy::new(|| icon_theme_base_paths());
|
pub(crate) static BASE_PATHS: Lazy<Vec<PathBuf>> = Lazy::new(icon_theme_base_paths);
|
||||||
pub(crate) static FALLBACK_PATHS: Lazy<Vec<PathBuf>> = Lazy::new(|| {
|
|
||||||
vec![
|
|
||||||
data_dir()
|
|
||||||
.expect("Failed to get DATA_DIR")
|
|
||||||
.join("icons/hicolor"),
|
|
||||||
PathBuf::from("usr/share/icons/hicolor"),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Look in $HOME/.icons (for backwards compatibility), in $XDG_DATA_DIRS/icons and in /usr/share/pixmaps (in that order).
|
/// Look in $HOME/.icons (for backwards compatibility), in $XDG_DATA_DIRS/icons and in /usr/share/pixmaps (in that order).
|
||||||
/// Paths that are not found are filtered out.
|
/// Paths that are not found are filtered out.
|
||||||
|
|
@ -56,6 +48,12 @@ mod test {
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use speculoos::prelude::*;
|
use speculoos::prelude::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_get_all_themes() {
|
||||||
|
let themes = get_all_themes().unwrap();
|
||||||
|
assert_that!(themes.get("hicolor")).is_some();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_get_theme_paths_ordered() {
|
fn should_get_theme_paths_ordered() {
|
||||||
let base_paths = icon_theme_base_paths();
|
let base_paths = icon_theme_base_paths();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue