i3status_rs/
config.rs

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    /// Set to `true` to invert mouse wheel direction
21    #[serde(default)]
22    pub invert_scrolling: bool,
23
24    #[serde(default)]
25    pub geolocator: Arc<Geolocator>,
26
27    /// The maximum delay (ms) between two clicks that are considered as double click
28    #[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}