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    " {$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}