todor/
conf.rs

1use std::path::PathBuf;
2use std::sync::RwLock;
3use dirs;
4use toml;
5use serde::Deserialize;
6use lazy_static::lazy_static;
7
8use crate::util::*;
9
10const DEF_CONFIG_PATH : &str = ".config/todor/todor.toml";
11const DATA_BASE : &str = ".local/share/todor";
12const DEF_CONFIG_CONTENT: &str = r#"# config for todor in toml
13
14## base directory for todor data
15basedir = "~/.local/share/todor"
16
17## blink the icons of items or not
18blink = true
19"#;
20
21lazy_static! {
22    pub static ref CONFIG: RwLock<Config> = RwLock::new(Config::load(None));
23}
24
25pub fn get_default_basedir() -> String {
26    // for windows compatibility
27    let rel_base :PathBuf = DATA_BASE.split("/").collect();
28    dirs::home_dir()
29        .expect("cannot get home dir")
30        .join(rel_base)
31        .to_str()
32        .expect("cannot convert path to string")
33        .to_string()
34}
35
36#[derive(Deserialize, Debug)]
37pub struct Config {
38    /// base directory for todor data
39    pub basedir: Option<String>,
40
41    /// blink the icons of items or not
42    pub blink: Option<bool>,
43}
44
45impl Default for Config {
46    fn default() -> Self {
47        Config {
48            basedir: Some(get_default_basedir()),
49            blink: Some(true),
50        }
51    }
52}
53
54impl Config {
55    pub fn update_with(&mut self, aconf: &Config) {
56        if let Some(basedir) = &aconf.basedir {
57            self.basedir = Some(basedir.clone());
58        }
59
60        if let Some(blink) = aconf.blink {
61            self.blink = Some(blink);
62        }
63    }
64
65    pub fn load(path_str: Option<String>) -> Self {
66        let mut work_conf = Config::default();
67
68        let confp;
69        if let Some(path_str) = path_str {
70            confp = PathBuf::from(util::path_normalize(&path_str));
71            if !confp.exists() {
72                eprintln!("config file not found, ignore and use defaults");
73                return work_conf;
74            }
75        } else {
76            let rel_base :PathBuf = DEF_CONFIG_PATH.split("/").collect();
77            confp = dirs::home_dir()
78                .expect("cannot get home dir")
79                .join(rel_base);
80
81            if !confp.exists() {
82                std::fs::create_dir_all(confp.parent().unwrap())
83                    .expect("Failed to create base directory");
84
85                std::fs::write(confp.clone(), DEF_CONFIG_CONTENT)
86                    .expect("cannot create config file");
87            }
88        }
89
90        let mut conf :Config = toml::from_str(
91                 &std::fs::read_to_string(&confp)
92                .expect("cannot read config file"))
93            .expect("cannot parse config file");
94        if let Some(basedir) = conf.basedir {
95            conf.basedir = Some(util::path_normalize(&basedir))
96        }
97
98        work_conf.update_with(&conf);
99        work_conf
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_get_default_basedir() {
109        assert!(get_default_basedir().contains(".local/share/todor"));
110    }
111
112    #[test]
113    fn test_config_load() {
114        let temp_dir = tempfile::tempdir().unwrap();
115        let testtoml = temp_dir.path().join("config.toml");
116        let testcontent = r#"basedir = "/tmp/.todor-test/"
117        blink = false
118        "#;
119        std::fs::write(&testtoml, testcontent).expect("write err");
120        let conf = Config::load(Some(testtoml.to_str().unwrap().into()));
121        assert_eq!(conf.basedir, Some("/tmp/.todor-test/".into()));
122        assert_eq!(conf.blink, Some(false));
123    }
124
125    #[test]
126    fn test_config_default() {
127        let conf = Config::default();
128        assert_eq!(conf.basedir, Some(get_default_basedir()));
129        assert_eq!(conf.blink, Some(true));
130    }
131
132    #[test]
133    fn test_config_update() {
134        let mut conf = Config::default();
135        let aconf = Config {
136            basedir: Some("/nowhere".into()),
137            blink: Some(false),
138        };
139        conf.update_with(&aconf);
140        assert_eq!(conf.basedir, Some("/nowhere".into()));
141        assert_eq!(conf.blink, Some(false));
142    }
143}