1use serde::Deserialize;
2use std::collections::HashMap;
3use std::fs::File;
4use std::io::{self, Read};
5use std::path::Path;
6use thiserror::Error;
7
8type StringMap = HashMap<String, String>;
9type IconMap = HashMap<String, String>;
10type OptionMap = HashMap<String, bool>;
11
12#[derive(Error, Debug)]
13pub enum ConfigError {
14 #[error("Failed to read config file: {0}")]
15 IoError(#[from] io::Error),
16 #[error("Failed to parse TOML: {0}")]
17 TomlError(#[from] toml::de::Error),
18}
19
20#[derive(Deserialize, Debug, Clone)]
22#[serde(default)]
23pub struct Aliases {
24 pub class: StringMap,
25 pub instance: StringMap,
26 pub name: StringMap,
27 pub app_id: StringMap,
28}
29
30impl Aliases {
31 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn get_alias(&self, category: &str, key: &str) -> Option<&String> {
38 match category {
39 "app_id" => self.app_id.get(key),
40 "class" => self.class.get(key),
41 "instance" => self.instance.get(key),
42 "name" => self.name.get(key),
43 _ => None,
44 }
45 }
46}
47
48impl Default for Aliases {
49 fn default() -> Self {
50 Self {
51 class: StringMap::new(),
52 instance: StringMap::new(),
53 name: StringMap::new(),
54 app_id: StringMap::new(),
55 }
56 }
57}
58
59#[derive(Deserialize, Debug, Clone)]
61#[serde(default)]
62pub struct Config {
63 pub icons: IconMap,
64 pub aliases: Aliases,
65 pub general: StringMap,
66 pub options: OptionMap,
67}
68
69impl Config {
70 pub fn new(filename: &Path) -> Result<Self, ConfigError> {
72 let config = Self::from_file(filename)?;
73 Ok(config)
74 }
75
76 pub fn from_file(filename: &Path) -> Result<Self, ConfigError> {
78 let mut file = File::open(filename)?;
79 let mut buffer = String::new();
80 file.read_to_string(&mut buffer)?;
81 let config: Config = toml::from_str(&buffer)?;
82 Ok(config)
83 }
84
85 pub fn get_general(&self, key: &str) -> Option<String> {
87 self.general.get(key).map(|s| s.to_string())
88 }
89
90 pub fn get_option(&self, key: &str) -> Option<bool> {
92 self.options.get(key).copied()
93 }
94
95 pub fn get_icon(&self, key: &str) -> Option<String> {
97 self.icons.get(key).map(|s| s.to_string())
98 }
99
100 pub fn set_general(&mut self, key: String, value: String) {
102 self.general.insert(key, value);
103 }
104
105 pub fn set_option(&mut self, key: String, value: bool) {
107 self.options.insert(key, value);
108 }
109}
110
111impl Default for Config {
112 fn default() -> Self {
113 Self {
114 icons: IconMap::new(),
115 aliases: Aliases::default(),
116 general: StringMap::new(),
117 options: OptionMap::new(),
118 }
119 }
120}