2022-05-12 23:12:06 +02:00
//! # 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();
//! # }
//!```
2022-05-13 08:00:52 +02:00
//! **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();
//! # }
2022-05-12 23:12:06 +02:00
use crate ::cache ::CACHE ;
2022-05-12 15:34:02 +02:00
use crate ::theme ::{ try_build_icon_path , THEMES } ;
2022-05-12 14:41:08 +02:00
use std ::path ::PathBuf ;
2022-05-12 23:12:06 +02:00
mod cache ;
mod theme ;
2022-05-12 15:34:02 +02:00
2022-05-13 08:00:52 +02:00
/// 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 ( )
}
2022-05-12 23:12:06 +02:00
/// The lookup builder struct, holding all the lookup query parameters.
2022-05-12 15:34:02 +02:00
pub struct LookupBuilder < ' a > {
name : & ' a str ,
2022-05-13 08:00:52 +02:00
cache : bool ,
2022-05-12 15:34:02 +02:00
scale : u16 ,
size : u16 ,
theme : Option < & ' a str > ,
}
2022-05-12 23:12:06 +02:00
/// 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 )
}
2022-05-12 15:34:02 +02:00
impl < ' a > LookupBuilder < ' a > {
2022-05-12 23:12:06 +02:00
/// Restrict the lookup to the given icon size.
///
/// ## Example
/// ```rust
/// # fn main() {
/// use freedesktop_icons::lookup;
///
/// let icon = lookup("firefox")
/// .with_size(48)
/// .find();
/// # }
2022-05-12 15:34:02 +02:00
pub fn with_size ( mut self , scale : u16 ) -> Self {
self . scale = scale ;
self
2022-05-12 14:41:08 +02:00
}
2022-05-12 23:12:06 +02:00
/// Restrict the lookup to the given scale.
///
/// ## Example
/// ```rust
/// # fn main() {
/// use freedesktop_icons::lookup;
///
/// let icon = lookup("firefox")
/// .with_scale(2)
/// .find();
/// # }
2022-05-12 15:34:02 +02:00
pub fn with_scale ( mut self , size : u16 ) -> Self {
self . size = size ;
self
}
2022-05-12 23:12:06 +02:00
/// Add the given theme to the current lookup :
/// ## Example
/// ```rust
/// # fn main() {
/// use freedesktop_icons::lookup;
///
/// let icon = lookup("firefox")
/// .with_theme("Papirus")
/// .find();
/// # }
2022-05-12 15:34:02 +02:00
pub fn with_theme < ' b : ' a > ( mut self , theme : & ' b str ) -> Self {
self . theme = Some ( theme ) ;
self
}
2022-05-13 08:00:52 +02:00
/// 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
}
2022-05-12 23:12:06 +02:00
/// 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 " )
}
2022-05-12 15:34:02 +02:00
fn new < ' b : ' a > ( name : & ' b str ) -> Self {
Self {
name ,
2022-05-13 08:00:52 +02:00
cache : false ,
2022-05-12 15:34:02 +02:00
scale : 1 ,
size : 24 ,
theme : None ,
2022-05-12 14:41:08 +02:00
}
}
2022-05-12 23:12:06 +02:00
// Recursively lookup for icon in the given theme and its parents
fn lookup_in_theme ( & self , theme : & str ) -> Option < PathBuf > {
2022-05-13 08:00:52 +02:00
// 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 ;
}
2022-05-12 15:34:02 +02:00
}
2022-05-13 08:00:52 +02:00
// Then lookup in the given theme
2022-05-12 23:12:06 +02:00
THEMES . get ( theme ) . and_then ( | icon_theme | {
2022-05-13 08:00:52 +02:00
let icon = icon_theme
2022-05-12 23:12:06 +02:00
. try_get_icon ( self . name , self . size , self . scale )
. or_else ( | | {
2022-05-13 08:00:52 +02:00
// 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
2022-05-12 23:12:06 +02:00
} )
2022-05-12 15:34:02 +02:00
}
2022-05-12 14:41:08 +02:00
}
#[ cfg(test) ]
mod test {
use crate ::lookup ;
use speculoos ::prelude ::* ;
2022-05-12 15:34:02 +02:00
use std ::path ::PathBuf ;
2022-05-12 14:41:08 +02:00
#[ test ]
2022-05-12 15:34:02 +02:00
fn simple_lookup ( ) {
2022-05-12 23:12:06 +02:00
let firefox = lookup ( " firefox " ) . find ( ) ;
2022-05-12 15:34:02 +02:00
assert_that! ( firefox ) . is_some ( ) ;
}
#[ test ]
fn compare_to_linincon_with_theme ( ) {
2022-05-12 14:41:08 +02:00
let lin_wireshark = linicon ::lookup_icon ( " wireshark " )
. next ( )
. unwrap ( )
. unwrap ( )
. path ;
2022-05-12 15:34:02 +02:00
let wireshark = lookup ( " wireshark " )
. with_size ( 16 )
. with_scale ( 1 )
. with_theme ( " Papirus " )
2022-05-12 23:12:06 +02:00
. find ( ) ;
2022-05-12 14:41:08 +02:00
assert_that! ( wireshark ) . is_some ( ) . is_equal_to ( lin_wireshark )
}
#[ test ]
fn compare_to_linicon_in_pixmap ( ) {
2022-05-12 15:34:02 +02:00
let archlinux_logo = lookup ( " archlinux-logo " )
. with_size ( 16 )
. with_scale ( 1 )
. with_theme ( " Papirus " )
2022-05-12 23:12:06 +02:00
. find ( ) ;
2022-05-12 14:41:08 +02:00
assert_that! ( archlinux_logo )
. is_some ( )
2022-05-12 15:34:02 +02:00
. is_equal_to ( PathBuf ::from ( " /usr/share/pixmaps/archlinux-logo.png " ) ) ;
2022-05-12 14:41:08 +02:00
}
}