cosmic_freedesktop_icons/theme/
parse.rs

1use crate::theme::directories::{Directory, DirectoryType};
2use crate::theme::Theme;
3
4fn icon_theme_section(file: &str) -> impl Iterator<Item = (&str, &str)> + '_ {
5    ini_core::Parser::new(file)
6        .skip_while(|item| *item != ini_core::Item::Section("Icon Theme"))
7        .take_while(|item| match item {
8            ini_core::Item::Section(value) => *value == "Icon Theme",
9            _ => true,
10        })
11        .filter_map(|item| {
12            if let ini_core::Item::Property(key, value) = item {
13                Some((key, value?))
14            } else {
15                None
16            }
17        })
18}
19
20#[derive(Debug)]
21enum DirectorySection<'a> {
22    Property(&'a str, &'a str),
23    EndSection,
24    Section(&'a str),
25}
26
27fn sections(file: &str) -> impl Iterator<Item = DirectorySection> {
28    ini_core::Parser::new(file).filter_map(move |item| match item {
29        ini_core::Item::Property(key, Some(value)) => Some(DirectorySection::Property(key, value)),
30        ini_core::Item::Section(section) => Some(DirectorySection::Section(section)),
31        ini_core::Item::SectionEnd => Some(DirectorySection::EndSection),
32        _ => None,
33    })
34}
35
36impl Theme {
37    pub(super) fn get_all_directories<'a>(
38        &'a self,
39        file: &'a str,
40    ) -> impl Iterator<Item = Directory<'a>> + 'a {
41        let mut iterator = sections(file);
42
43        std::iter::from_fn(move || {
44            let mut name = "";
45            let mut size = None;
46            let mut max_size = None;
47            let mut min_size = None;
48            let mut threshold = None;
49            let mut scale = None;
50            // let mut context = None;
51            let mut dtype = DirectoryType::default();
52
53            #[allow(clippy::while_let_on_iterator)]
54            while let Some(event) = iterator.next() {
55                match event {
56                    DirectorySection::Property(key, value) => {
57                        if name.is_empty() || name == "Icon Theme" {
58                            continue;
59                        }
60
61                        match key {
62                            "Size" => size = str::parse(value).ok(),
63                            "Scale" => scale = str::parse(value).ok(),
64                            // "Context" => context = Some(value),
65                            "Type" => dtype = DirectoryType::from(value),
66                            "MaxSize" => max_size = str::parse(value).ok(),
67                            "MinSize" => min_size = str::parse(value).ok(),
68                            "Threshold" => threshold = str::parse(value).ok(),
69                            _ => (),
70                        }
71                    }
72
73                    DirectorySection::Section(new_name) => {
74                        name = new_name;
75                        size = None;
76                        max_size = None;
77                        min_size = None;
78                        threshold = None;
79                        scale = None;
80                        dtype = DirectoryType::default();
81                    }
82
83                    DirectorySection::EndSection => {
84                        if name.is_empty() || name == "Icon Theme" {
85                            continue;
86                        }
87
88                        let size = size.take()?;
89
90                        return Some(Directory {
91                            name,
92                            size,
93                            scale: scale.unwrap_or(1),
94                            // context,
95                            type_: dtype,
96                            maxsize: max_size.unwrap_or(size),
97                            minsize: min_size.unwrap_or(size),
98                            threshold: threshold.unwrap_or(2),
99                        });
100                    }
101                }
102            }
103
104            None
105        })
106    }
107
108    pub fn inherits<'a>(&self, file: &'a str) -> Vec<&'a str> {
109        icon_theme_section(file)
110            .find(|&(key, _)| key == "Inherits")
111            .map(|(_, parents)| {
112                parents
113                    .split(',')
114                    // Filtering out 'hicolor' since we are going to fallback there anyway
115                    .filter(|parent| parent != &"hicolor")
116                    .collect()
117            })
118            .unwrap_or_default()
119    }
120}
121
122#[cfg(test)]
123mod test {
124    use crate::THEMES;
125    use speculoos::prelude::*;
126
127    #[test]
128    fn should_get_theme_parents() {
129        for theme in THEMES.get("Arc").unwrap() {
130            let file = crate::theme::read_ini_theme(&theme.index).ok().unwrap();
131            let file = std::str::from_utf8(file.as_ref()).ok().unwrap();
132            let parents = theme.inherits(file);
133
134            assert_that!(parents).does_not_contain("hicolor");
135
136            assert_that!(parents).is_equal_to(vec![
137                "Moka",
138                "Faba",
139                "elementary",
140                "Adwaita",
141                "gnome",
142            ]);
143        }
144    }
145}