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 " {$short_error_message|X} ".parse().unwrap()
64}
65
66fn default_error_fullscreen() -> FormatConfig {
67 " $full_error_message ".parse().unwrap()
68}
69
70fn default_icons_format() -> Arc<String> {
71 Arc::new("{icon}".into())
72}
73
74impl SharedConfig {
75 pub fn get_icon(&self, icon: &str, value: Option<f64>) -> Result<String> {
76 if icon.is_empty() {
77 Ok(String::new())
78 } else {
79 Ok(self.icons_format.replace(
80 "{icon}",
81 self.icons
82 .get(icon, value)
83 .or_error(|| format!("Icon '{icon}' not found"))?,
84 ))
85 }
86 }
87}
88
89#[derive(Deserialize, Debug)]
90pub struct BlockConfigEntry {
91 #[serde(flatten)]
92 pub common: CommonBlockConfig,
93 #[serde(flatten)]
94 pub config: BlockConfig,
95}
96
97#[derive(Deserialize, Debug, SmartDefault)]
98#[serde(default)]
99pub struct CommonBlockConfig {
100 pub click: ClickHandler,
101 pub signal: Option<i32>,
102 pub icons_format: Option<String>,
103 pub theme_overrides: Option<ThemeOverrides>,
104 pub icons_overrides: Option<HashMap<String, Icon>>,
105 pub merge_with_next: bool,
106
107 #[default(5)]
108 pub error_interval: u64,
109 pub error_format: FormatConfig,
110 pub error_fullscreen_format: FormatConfig,
111
112 pub if_command: Option<String>,
113}
114
115fn deserialize_theme_config<'de, D>(deserializer: D) -> Result<Arc<Theme>, D::Error>
116where
117 D: Deserializer<'de>,
118{
119 let theme_config = ThemeUserConfig::deserialize(deserializer)?;
120 let theme = Theme::try_from(theme_config).serde_error()?;
121 Ok(Arc::new(theme))
122}