Introduce IconLocations as better "next step" type after SearchDirectories

This commit is contained in:
Ridan Vandenbergh 2025-06-18 16:32:04 +02:00
parent c7ee1e7cad
commit b989389b63
No known key found for this signature in database
3 changed files with 70 additions and 33 deletions

View file

@ -61,7 +61,5 @@
mod icon; mod icon;
mod search_dir; mod search_dir;
pub mod theme; pub mod theme;
mod theme_graph;
pub use search_dir::*; pub use search_dir::*;
pub use theme::*;

View file

@ -1,6 +1,8 @@
use crate::icon::IconFile; use crate::icon::IconFile;
use crate::theme::{ThemeDescriptor, ThemeParseError};
use std::collections::HashMap; use std::collections::HashMap;
use std::path::{Path, PathBuf}; use std::ffi::{OsStr, OsString};
use std::path::PathBuf;
/// Icons and icon themes are looked for in a set of directories. /// Icons and icon themes are looked for in a set of directories.
/// ///
@ -18,7 +20,7 @@ use std::path::{Path, PathBuf};
/// use icon::SearchDirectories; /// use icon::SearchDirectories;
/// ///
/// let dirs = SearchDirectories::default(); /// let dirs = SearchDirectories::default();
/// let (files, themes) = dirs.search_icons_and_theme_folders(); /// // TODO
/// ``` /// ```
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SearchDirectories { pub struct SearchDirectories {
@ -50,13 +52,7 @@ impl SearchDirectories {
extra_dirs.into() extra_dirs.into()
} }
pub fn search_icons_and_theme_folders(&self) -> (Vec<IconFile>, HashMap<String, Vec<PathBuf>>) { pub fn find_icon_locations(&self) -> IconLocations {
fn theme_name_from_path(path: &Path) -> Option<&str> {
let theme_name = path.components().nth_back(1); // get the second-to-last component (which should be the theme name)
theme_name?.as_os_str().to_str()
}
// "Each theme is stored as subdirectories of the base directories" // "Each theme is stored as subdirectories of the base directories"
let (files, dirs) = self let (files, dirs) = self
@ -79,17 +75,45 @@ impl SearchDirectories {
// searching the base directories in order is used" // searching the base directories in order is used"
// For each theme name, create a list of directories where it may be found: // For each theme name, create a list of directories where it may be found:
let mut theme_folders: HashMap<String, Vec<PathBuf>> = HashMap::new(); let mut themes_directories: HashMap<OsString, Vec<PathBuf>> = HashMap::new();
for (_, dir) in dirs { for (_, dir) in dirs {
let theme_name = dir.file_name().to_string_lossy().into_owned(); let theme_name = dir.file_name();
theme_folders themes_directories
.entry(theme_name) .entry(theme_name)
.or_default() .or_default()
.push(dir.path()); .push(dir.path());
} }
(files, theme_folders) IconLocations {
standalone_icons: files,
themes_directories,
}
}
}
#[derive(Debug)]
pub struct IconLocations {
pub standalone_icons: Vec<IconFile>,
pub themes_directories: HashMap<OsString, Vec<PathBuf>>,
}
impl IconLocations {
pub fn theme<S>(&self, internal_name: S) -> std::io::Result<ThemeDescriptor<'_>>
where
S: AsRef<OsStr>,
{
let internal_name = internal_name.as_ref();
let theme = self
.themes_directories
.get(internal_name)
.ok_or_else(|| std::io::Error::other(ThemeParseError::NotAnIconTheme))?;
ThemeDescriptor::new_from_folders(
internal_name.to_string_lossy().into_owned(),
theme.clone()
)
} }
} }
@ -134,6 +158,7 @@ impl Default for SearchDirectories {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::search_dir::SearchDirectories; use crate::search_dir::SearchDirectories;
use crate::IconLocations;
// these tests assume certain applications are installed on the system they are ran on. // these tests assume certain applications are installed on the system they are ran on.
@ -141,12 +166,27 @@ mod test {
fn test_find_htop_icon_outside_icontheme() { fn test_find_htop_icon_outside_icontheme() {
let dirs = SearchDirectories::default(); let dirs = SearchDirectories::default();
let (icons, _indexes) = dirs.search_icons_and_theme_folders(); let IconLocations {
standalone_icons,
themes_directories,
} = dirs.find_icon_locations();
println!("{:?}", themes_directories);
assert!( assert!(
icons standalone_icons
.iter() .iter()
.any(|i| i.path.file_name().and_then(|s| s.to_str()) == Some("htop.png")) .any(|i| i.path.file_name().and_then(|s| s.to_str()) == Some("htop.png"))
) )
} }
#[test]
fn test_find_adwaita() {
let dirs = SearchDirectories::default();
let locations = dirs.find_icon_locations();
let descriptor = locations.theme("Adwaita").unwrap();
assert_eq!(descriptor.index.name, "Adwaita");
}
} }

View file

@ -9,7 +9,7 @@ pub type OwnedDirectoryIndex = DirectoryIndex<'static>;
pub struct ThemeDescriptor<'a> { pub struct ThemeDescriptor<'a> {
pub internal_name: String, pub internal_name: String,
pub base_dirs: Vec<Cow<'a, Path>>, pub base_dirs: Vec<PathBuf>,
pub index_location: PathBuf, pub index_location: PathBuf,
pub index: ThemeIndex<'a>, pub index: ThemeIndex<'a>,
// additional groups? // additional groups?
@ -45,20 +45,19 @@ impl ThemeDescriptor<'_> {
Ok(Self { Ok(Self {
internal_name, internal_name,
base_dirs: folders.into_iter().map(Into::into).collect(), base_dirs: folders,
index_location, index_location,
index, index,
}) })
} }
pub fn into_owned(self) -> OwnedThemeDescriptor {
theme_into_owned(self)
}
} }
fn theme_into_owned(theme: ThemeDescriptor) -> OwnedThemeDescriptor { fn theme_into_owned(theme: ThemeDescriptor) -> OwnedThemeDescriptor {
let base_dirs = theme let base_dirs = theme.base_dirs;
.base_dirs
.into_iter()
.map(Cow::into_owned)
.map(Into::into)
.collect();
let index = theme.index.into_owned(); let index = theme.index.into_owned();
OwnedThemeDescriptor { OwnedThemeDescriptor {