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