cosmic_freedesktop_icons/theme/
paths.rs

1use dirs::home_dir;
2use std::path::PathBuf;
3use std::sync::LazyLock;
4use xdg::BaseDirectories;
5
6use crate::theme;
7use crate::theme::error::ThemeError;
8
9pub(crate) static BASE_PATHS: LazyLock<Vec<PathBuf>> = LazyLock::new(icon_theme_base_paths);
10
11/// Look in $HOME/.icons (for backwards compatibility), in $XDG_DATA_DIRS/icons, in $XDG_DATA_DIRS/pixmaps and in /usr/share/pixmaps (in that order).
12/// Paths that are not found are filtered out.
13fn icon_theme_base_paths() -> Vec<PathBuf> {
14    let mut data_dirs: Vec<_> = BaseDirectories::new()
15        .map(|bd| {
16            let mut data_dirs: Vec<_> = bd
17                .get_data_dirs()
18                .into_iter()
19                .flat_map(|p| [p.join("icons"), p.join("pixmaps")])
20                .collect();
21            let data_home = bd.get_data_home();
22            data_dirs.push(data_home.join("icons"));
23            data_dirs.push(data_home.join("pixmaps"));
24            data_dirs
25        })
26        .unwrap_or_default();
27    match home_dir().map(|home| home.join(".icons")) {
28        Some(home_icon_dir) => data_dirs.push(home_icon_dir),
29        None => tracing::warn!("No $HOME directory found"),
30    }
31    data_dirs.into_iter().filter(|p| p.exists()).collect()
32}
33
34#[derive(Clone, Debug)]
35pub struct ThemePath(pub PathBuf);
36
37impl ThemePath {
38    pub(super) fn index(&self) -> theme::Result<PathBuf> {
39        let index = self.0.join("index.theme");
40
41        if !index.exists() {
42            return Err(ThemeError::ThemeIndexNotFound(index));
43        }
44
45        Ok(index)
46    }
47}
48
49#[cfg(test)]
50mod test {
51    use crate::theme::paths::icon_theme_base_paths;
52    use crate::theme::{get_all_themes, Theme};
53    use speculoos::prelude::*;
54
55    #[test]
56    fn should_get_all_themes() {
57        let themes = get_all_themes();
58        assert_that!(themes.get("hicolor")).is_some();
59    }
60
61    #[test]
62    fn should_get_theme_paths_ordered() {
63        let base_paths = icon_theme_base_paths();
64        assert_that!(base_paths).is_not_empty()
65    }
66
67    #[test]
68    fn should_read_theme_index() {
69        let themes = get_all_themes();
70        let themes: Vec<&Theme> = themes.values().flatten().collect();
71        assert_that!(themes).is_not_empty();
72    }
73}