1use serde::{Deserialize, Deserializer};
2use smart_default::SmartDefault;
3use std::collections::HashMap;
4use std::sync::Arc;
5
6use crate::blocks::BlockConfig;
7use crate::click::ClickHandler;
8use crate::errors::*;
9use crate::formatting::config::Config as FormatConfig;
10use crate::geolocator::Geolocator;
11use crate::icons::{Icon, Icons};
12use crate::themes::{Theme, ThemeOverrides, ThemeUserConfig};
13
14#[derive(Deserialize, Debug)]
15#[serde(deny_unknown_fields)]
16pub struct Config {
17 #[serde(flatten)]
18 pub shared: SharedConfig,
19
20 #[serde(default)]
22 pub invert_scrolling: bool,
23
24 #[serde(default)]
25 pub geolocator: Arc<Geolocator>,
26
27 #[serde(default)]
29 pub double_click_delay: u64,
30
31 #[serde(default = "default_error_format")]
32 pub error_format: FormatConfig,
33 #[serde(default = "default_error_fullscreen")]
34 pub error_fullscreen_format: FormatConfig,
35
36 #[serde(default)]
37 #[serde(rename = "block")]
38 pub blocks: Vec<BlockConfigEntry>,
39}
40
41#[derive(Deserialize, Debug, Clone)]
42pub struct SharedConfig {
43 #[serde(default)]
44 #[serde(deserialize_with = "deserialize_theme_config")]
45 pub theme: Arc<Theme>,
46 #[serde(default)]
47 pub icons: Arc<Icons>,
48 #[serde(default = "default_icons_format")]
49 pub icons_format: Arc<String>,
50}
51
52impl Default for SharedConfig {
53 fn default() -> Self {
54 Self {
55 theme: Default::default(),
56 icons: Default::default(),
57 icons_format: default_icons_format(),
58 }
59 }
60}
61
62fn default_error_format() -> FormatConfig {
63 " {$restart_block_icon |}{$short_error_message|X} "
64 .parse()
65 .unwrap()
66}
67
68fn default_error_fullscreen() -> FormatConfig {
69 " {$restart_block_icon |}$full_error_message "
70 .parse()
71 .unwrap()
72}
73
74fn default_icons_format() -> Arc<String> {
75 Arc::new("{icon}".into())
76}
77
78impl SharedConfig {
79 pub fn get_icon(&self, icon: &str, value: Option<f64>) -> Result<String> {
80 if icon.is_empty() {
81 Ok(String::new())
82 } else {
83 Ok(self.icons_format.replace(
84 "{icon}",
85 self.icons
86 .get(icon, value)
87 .or_error(|| format!("Icon '{icon}' not found"))?,
88 ))
89 }
90 }
91}
92
93#[derive(Deserialize, Debug)]
94pub struct BlockConfigEntry {
95 #[serde(flatten)]
96 pub common: CommonBlockConfig,
97 #[serde(flatten)]
98 pub config: BlockConfig,
99}
100
101#[derive(Deserialize, Debug, SmartDefault)]
102#[serde(default)]
103pub struct CommonBlockConfig {
104 pub click: ClickHandler,
105 pub signal: Option<i32>,
106 pub icons_format: Option<String>,
107 pub theme_overrides: Option<ThemeOverrides>,
108 pub icons_overrides: Option<HashMap<String, Icon>>,
109 pub merge_with_next: bool,
110
111 #[default(5)]
112 pub error_interval: u64,
113 pub error_format: FormatConfig,
114 pub error_fullscreen_format: FormatConfig,
115 pub max_retries: Option<u8>,
116
117 pub if_command: Option<String>,
118}
119
120fn deserialize_theme_config<'de, D>(deserializer: D) -> Result<Arc<Theme>, D::Error>
121where
122 D: Deserializer<'de>,
123{
124 let theme_config = ThemeUserConfig::deserialize(deserializer)?;
125 let theme = Theme::try_from(theme_config).serde_error()?;
126 Ok(Arc::new(theme))
127}