Skip to main content

todo_txt/
config.rs

1use std::collections::HashMap;
2
3#[derive(Clone, Debug, envir::Deserialize, envir::Serialize)]
4#[envir(prefix = "COLOR_")]
5pub struct Colors {
6    #[envir(default)]
7    pub context: crate::Color,
8    #[envir(default)]
9    pub done: crate::Color,
10    #[envir(default)]
11    pub date: crate::Color,
12    #[envir(skip)]
13    pub none: crate::Color,
14    #[envir(default)]
15    pub meta: crate::Color,
16    #[envir(default)]
17    pub number: crate::Color,
18    #[envir(default)]
19    pub project: crate::Color,
20    #[envir(load_with = "priorities_load", export_with = "priorities_export")]
21    priorities: HashMap<String, crate::Color>,
22}
23
24fn priorities_load(env: &HashMap<String, String>) -> envir::Result<HashMap<String, crate::Color>> {
25    let mut priorities = HashMap::new();
26    priorities.insert("PRI_A".to_string(), "yellow".into());
27    priorities.insert("PRI_B".to_string(), "green".into());
28    priorities.insert("PRI_C".to_string(), "bright blue".into());
29    priorities.insert("PRI_X".to_string(), "white".into());
30
31    for (k, v) in env {
32        if k.starts_with("PRI_") {
33            priorities.insert(k.clone(), v.parse().unwrap_or_default());
34        }
35    }
36
37    Ok(priorities)
38}
39
40fn priorities_export(priorities: &HashMap<String, crate::Color>) -> HashMap<String, String> {
41    priorities
42        .iter()
43        .map(|(k, v)| (k.clone(), v.to_string()))
44        .collect()
45}
46
47impl Colors {
48    pub fn for_pri(&self, pri: &crate::Priority) -> crate::Color {
49        self.priorities
50            .get(&format!("PRI_{pri}"))
51            .unwrap_or_else(|| self.priorities.get("PRI_X").unwrap())
52            .clone()
53    }
54}
55
56#[derive(envir::Deserialize, envir::Serialize)]
57#[envir(prefix = "TODOTXT_")]
58pub struct Config {
59    #[envir(noprefix, name = "TODO_ACTIONS_DIR", default = "${HOME}/.todo/actions")]
60    pub action_dir: String,
61    #[envir(default = "true")]
62    pub auto_archive: bool,
63    #[envir(nested)]
64    pub colors: Colors,
65    #[envir(default)]
66    pub date_on_add: bool,
67    #[envir(default = "help")]
68    pub default_action: String,
69    #[envir(default)]
70    pub disable_filter: bool,
71    #[envir(default = "cat")]
72    pub final_filter: String,
73    #[envir(default)]
74    pub force: bool,
75    #[envir(
76        noprefix,
77        name = "TODO_NOTE_ARCHIVE",
78        default = "${TODO_DIR}/notes/archive.txt"
79    )]
80    pub note_archive: String,
81    #[envir(noprefix, name = "TODO_NOTES_DIR", default = "${TODO_DIR}/notes")]
82    pub notes_dir: String,
83    #[envir(noprefix, name = "TODO_NOTE_FILTER", default = "cat")]
84    pub note_filter: String,
85    #[envir(default)]
86    pub plain: bool,
87    #[envir(default = "true")]
88    pub preserve_line_numbers: bool,
89    pub priority_on_add: Option<char>,
90    #[envir(default = "env LC_COLLATE=C sort -f -k2")]
91    pub sort_command: String,
92    #[envir(noprefix)]
93    pub todo_dir: String,
94    #[envir(noprefix, default = "${TODO_DIR}/todo.txt")]
95    pub todo_file: String,
96    #[envir(noprefix, default = "${TODO_DIR}/done.txt")]
97    pub done_file: String,
98    #[envir(noprefix, default = "${TODO_DIR}/report.txt")]
99    pub report_file: String,
100    #[envir(default = "1")]
101    pub verbose: u8,
102}
103
104impl Config {
105    #[must_use]
106    pub fn from_env() -> Self {
107        Self::load_env();
108
109        envir::from_env().unwrap()
110    }
111
112    pub fn load_env() {
113        envir::set("NONE", "");
114        envir::set("BLACK", "\x1B[0;30m");
115        envir::set("RED", "\x1B[0;31m");
116        envir::set("GREEN", "\x1B[0;32m");
117        envir::set("BROWN", "\x1B[0;33m");
118        envir::set("BLUE", "\x1B[0;34m");
119        envir::set("PURPLE", "\x1B[0;35m");
120        envir::set("CYAN", "\x1B[0;36m");
121        envir::set("LIGHT_GREY", "\x1B[0;37m");
122        envir::set("DARK_GREY", "\x1B[1;30m");
123        envir::set("LIGHT_RED", "\x1B[1;31m");
124        envir::set("LIGHT_GREEN", "\x1B[1;32m");
125        envir::set("YELLOW", "\x1B[1;33m");
126        envir::set("LIGHT_BLUE", "\x1B[1;34m");
127        envir::set("LIGHT_PURPLE", "\x1B[1;35m");
128        envir::set("LIGHT_CYAN", "\x1B[1;36m");
129        envir::set("WHITE", "\x1B[1;37m");
130        envir::set("DEFAULT", "\x1B[0m");
131
132        Self::load_config_file();
133    }
134
135    #[cfg(not(debug_assertions))]
136    fn load_config_file() {
137        let home = envir::get("HOME").unwrap();
138
139        let configs: Vec<Box<dyn Fn() -> String>> = vec![
140            Box::new(|| format!("{home}/.todo/config")),
141            Box::new(|| format!("{home}/todo.cfg")),
142            Box::new(|| format!("{home}/.todo.cfg")),
143            Box::new(|| format!("{home}/.config/todo/config")),
144            Box::new(|| {
145                let filename = std::env::args().next().unwrap();
146                let mut path = std::path::PathBuf::from(filename);
147                path.pop();
148
149                format!("{}/todo.cfg", path.display())
150            }),
151            Box::new(|| {
152                envir::get("TODOTXT_GLOBAL_CFG_FILE")
153                    .unwrap_or_else(|_| "/etc/todo/config".to_string())
154            }),
155        ];
156
157        for config in configs {
158            if envir::from_path(config()).is_ok() {
159                break;
160            }
161        }
162    }
163
164    #[cfg(debug_assertions)]
165    fn load_config_file() {
166        envir::dotenv();
167    }
168}