1use crate::errors::*;
2use crate::util;
3use serde::Deserialize;
4use std::collections::HashMap;
5
6#[derive(Deserialize, Debug, Clone)]
7#[serde(try_from = "IconsConfigRaw")]
8pub struct Icons(pub HashMap<String, Icon>);
9
10#[derive(Deserialize, Debug, Clone)]
11#[serde(untagged)]
12pub enum Icon {
13 Single(String),
14 Progression(Vec<String>),
15}
16
17impl From<&'static str> for Icon {
18 fn from(value: &'static str) -> Self {
19 Self::Single(value.into())
20 }
21}
22
23impl<const N: usize> From<[&str; N]> for Icon {
24 fn from(value: [&str; N]) -> Self {
25 Self::Progression(value.iter().map(|s| s.to_string()).collect())
26 }
27}
28
29impl Default for Icons {
30 fn default() -> Self {
31 Self(map! {
33 "backlight" => "BRIGHT",
34 "bat" => "BAT",
35 "bat_charging" => "CHG",
36 "bat_not_available" => "BAT N/A",
37 "bell" => "ON",
38 "bell-slash" => "OFF",
39 "bluetooth" => "BT",
40 "calendar" => "CAL",
41 "cogs" => "LOAD",
42 "cpu" => "CPU",
43 "cpu_boost_on" => "BOOST ON",
44 "cpu_boost_off" => "BOOST OFF",
45 "disk_drive" => "DISK",
46 "docker" => "DOCKER",
47 "github" => "GITHUB",
48 "gpu" => "GPU",
49 "headphones" => "HEAD",
50 "hueshift" => "HUE",
51 "joystick" => "JOY",
52 "keyboard" => "KBD",
53 "mail" => "MAIL",
54 "memory_mem" => "MEM",
55 "memory_swap" => "SWAP",
56 "mouse" => "MOUSE",
57 "music" => "MUSIC",
58 "music_next" => ">",
59 "music_pause" => "||",
60 "music_play" => ">",
61 "music_prev" => "<",
62 "net_bridge" => "BRIDGE",
63 "net_cellular" => [
64 "NO SIGNAL",
65 "0 BARS",
66 "1 BAR",
67 "2 BARS",
68 "3 BARS",
69 "4 BARS",
70 ],
71 "net_down" => "DOWN",
72 "net_loopback" => "LO",
73 "net_modem" => "MODEM",
74 "net_up" => "UP ",
75 "net_vpn" => "VPN",
76 "net_wired" => "ETH",
77 "net_wireless" => "WLAN",
78 "notification" => "NOTIF",
79 "phone" => "PHONE",
80 "phone_disconnected" => "PHONE",
81 "ping" => "PING",
82 "pomodoro" => "POMODORO",
83 "pomodoro_break" => "BREAK",
84 "pomodoro_paused" => "PAUSED",
85 "pomodoro_started" => "STARTED",
86 "pomodoro_stopped" => "STOPPED",
87 "resolution" => "RES",
88 "scratchpad" => "[]",
89 "tasks" => "TSK",
90 "tea" => "TEA",
91 "thermometer" => "TEMP",
92 "time" => "TIME",
93 "toggle_off" => "OFF",
94 "toggle_on" => "ON",
95 "unknown" => "??",
96 "update" => "UPD",
97 "uptime" => "UP",
98 "volume" => "VOL",
99 "volume_muted" => "VOL MUTED",
100 "microphone" => "MIC",
101 "microphone_muted" => "MIC MUTED",
102 "weather_clouds_night" => "CLOUDY",
103 "weather_clouds" => "CLOUDY",
104 "weather_default" => "WEATHER",
105 "weather_fog_night" => "FOG",
106 "weather_fog" => "FOG",
107 "weather_moon" => "MOONY",
108 "weather_rain_night" => "RAIN",
109 "weather_rain" => "RAIN",
110 "weather_snow" => "SNOW",
111 "weather_sun" => "SUNNY",
112 "weather_thunder_night" => "STORM",
113 "weather_thunder" => "STORM",
114 "webcam" => "CAM",
115 "xrandr" => "SCREEN"
116 })
117 }
118}
119
120impl Icons {
121 pub fn from_file(file: &str) -> Result<Self> {
122 if file == "none" {
123 Ok(Icons::default())
124 } else {
125 let file = util::find_file(file, Some("icons"), Some("toml"))
126 .or_error(|| format!("Icon set '{file}' not found"))?;
127 Ok(Icons(util::deserialize_toml_file(file)?))
128 }
129 }
130
131 pub fn apply_overrides(&mut self, overrides: HashMap<String, Icon>) {
132 self.0.extend(overrides);
133 }
134
135 pub fn get(&self, icon: &'_ str, value: Option<f64>) -> Option<&str> {
136 match (self.0.get(icon)?, value) {
137 (Icon::Single(icon), _) => Some(icon),
138 (Icon::Progression(prog), _) if prog.is_empty() => None,
139 (Icon::Progression(prog), None) => Some(prog.last().unwrap()),
140 (Icon::Progression(prog), Some(value)) => {
141 let index = ((value * prog.len() as f64) as usize).clamp(0, prog.len() - 1);
142 Some(prog[index].as_str())
143 }
144 }
145 }
146}
147
148#[derive(Deserialize, Default)]
149#[serde(deny_unknown_fields, default)]
150struct IconsConfigRaw {
151 icons: Option<String>,
152 overrides: Option<HashMap<String, Icon>>,
153}
154
155impl TryFrom<IconsConfigRaw> for Icons {
156 type Error = Error;
157
158 fn try_from(raw: IconsConfigRaw) -> Result<Self, Self::Error> {
159 let mut icons = Self::from_file(raw.icons.as_deref().unwrap_or("none"))?;
160 if let Some(overrides) = raw.overrides {
161 for icon in overrides {
162 icons.0.insert(icon.0, icon.1);
163 }
164 }
165 Ok(icons)
166 }
167}