icon_loader/
theme_name_provider.rs1pub mod error;
2
3use std::{borrow::ToOwned, error::Error as StdError};
4
5use error::{Error, Result};
6
7pub enum ThemeNameProvider {
12 #[cfg(feature = "kde")]
14 KDE,
15
16 #[cfg(feature = "gtk")]
18 GTK,
19
20 User(String),
22
23 Custom(
25 Box<dyn Fn() -> std::result::Result<String, Box<dyn StdError + Send + Sync>> + Send + Sync>,
26 ),
27}
28
29impl ThemeNameProvider {
30 pub fn user(string: impl Into<String>) -> Self {
32 ThemeNameProvider::User(string.into())
33 }
34
35 pub fn custom<F, S, E>(f: F) -> Self
37 where
38 F: Fn() -> std::result::Result<S, E> + Send + Sync + 'static,
39 S: Into<String>,
40 E: StdError + Send + Sync + 'static,
41 {
42 ThemeNameProvider::Custom(Box::new(move || f().map(Into::into).map_err(Into::into)))
43 }
44
45 pub(crate) fn theme_name(&self) -> Result<String> {
46 match self {
47 #[cfg(feature = "kde")]
48 ThemeNameProvider::KDE => {
49 let base_dirs = xdg::BaseDirectories::new();
50
51 if base_dirs.find_config_file("kdeglobals").is_none() {
52 return Err(Error::ConfigNotFound);
53 }
54
55 for config_path in base_dirs.find_config_files("kdeglobals") {
56 let config = ini::Ini::load_from_file(config_path)?;
57
58 for (category, properties) in config.iter() {
59 if let Some("Icons") = category {
60 for (key, value) in properties.iter() {
61 if key == "Theme" {
62 return Ok(value.to_string());
63 }
64 }
65 }
66 }
67 }
68
69 Err(Error::ConfigMissingThemeName)
70 }
71
72 #[cfg(feature = "gtk")]
73 ThemeNameProvider::GTK => {
74 let base_dirs = xdg::BaseDirectories::new();
75
76 if base_dirs.find_config_file("gtk-3.0/settings.ini").is_none() {
77 return Err(Error::ConfigNotFound);
78 }
79
80 for config_path in base_dirs.find_config_files("gtk-3.0/settings.ini") {
81 let config = ini::Ini::load_from_file(config_path)?;
82
83 for (category, properties) in config.iter() {
84 if let Some("Settings") = category {
85 for (key, value) in properties.iter() {
86 if key == "gtk-icon-theme-name" {
87 return Ok(value.to_string());
88 }
89 }
90 }
91 }
92 }
93
94 Err(Error::ConfigMissingThemeName)
95 }
96
97 ThemeNameProvider::User(string) => Ok(string.clone()),
98 ThemeNameProvider::Custom(func) => func().map_err(|source| Error::Custom { source }),
99 }
100 }
101}
102
103impl std::fmt::Debug for ThemeNameProvider {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 match self {
106 #[cfg(feature = "kde")]
107 ThemeNameProvider::KDE => write!(f, "ThemeNameProvider::KDE"),
108
109 #[cfg(feature = "gtk")]
110 ThemeNameProvider::GTK => write!(f, "ThemeNameProvider::GTK"),
111
112 ThemeNameProvider::User(string) => write!(f, "ThemeNameProvider::User({})", string),
113 ThemeNameProvider::Custom(_) => write!(f, "ThemeNameProvider::Custom"),
114 }
115 }
116}
117
118impl Default for ThemeNameProvider {
119 fn default() -> Self {
120 ThemeNameProvider::User(String::from("hicolor"))
121 }
122}
123
124impl PartialEq for ThemeNameProvider {
125 fn eq(&self, other: &Self) -> bool {
126 use std::mem::discriminant;
127
128 if discriminant(self) != discriminant(other) {
129 return false;
130 }
131
132 if let ThemeNameProvider::Custom(_) = self {
133 return false;
134 }
135
136 if let (ThemeNameProvider::User(string), ThemeNameProvider::User(other_string)) =
137 (self, other)
138 {
139 return string == other_string;
140 }
141
142 true
143 }
144}
145
146impl<F, S, E> From<F> for ThemeNameProvider
147where
148 F: Fn() -> std::result::Result<S, E> + Send + Sync + 'static,
149 S: Into<String>,
150 E: StdError + Send + Sync + 'static,
151{
152 fn from(f: F) -> Self {
153 ThemeNameProvider::custom(f)
154 }
155}
156
157impl From<&str> for ThemeNameProvider {
158 fn from(string: &str) -> Self {
159 ThemeNameProvider::User(string.to_owned())
160 }
161}
162
163impl From<String> for ThemeNameProvider {
164 fn from(string: String) -> Self {
165 ThemeNameProvider::User(string)
166 }
167}