toddi/
config.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::path::PathBuf;

use anyhow::{anyhow, Context, Result};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize)]
pub struct Config {
    pub todo_file: PathBuf,
    pub done_file: PathBuf,
}

static TODO_FILE: &str = "todo.txt";
static DONE_FILE: &str = "done.txt";

impl Config {
    fn new() -> Result<Self> {
        let mut todo_txt_path = std::path::PathBuf::new();
        let mut done_txt_path = std::path::PathBuf::new();
        set_default_path(&mut todo_txt_path, TODO_FILE)?;
        set_default_path(&mut done_txt_path, DONE_FILE)?;
        Ok(Config {
            todo_file: todo_txt_path,
            done_file: done_txt_path,
        })
    }

    pub fn load(config_file_path: Option<PathBuf>) -> Result<Config> {
        let mut file_path: PathBuf;
        if let Some(file) = config_file_path.as_deref() {
            file_path = file.to_path_buf();
        } else {
            file_path = get_configdir()?;
            file_path.push("toddi");
            file_path.push("config.toml");

            match std::fs::exists(&file_path) {
                Ok(value) => {
                    if !value {
                        println!("Default configuration file does not exist.");
                        match Config::new() {
                            Ok(config) => {
                                match toml::to_string(&config) {
                                    Ok(conf_str) => {
                                        if let Some(parent_dir) = file_path.parent() {
                                            if !parent_dir.exists() {
                                                std::fs::create_dir_all(parent_dir)?;
                                            }
                                        }
                                        println!(
                                            "Writing default configuration file: {}",
                                            file_path.display()
                                        );
                                        std::fs::write(&file_path, &conf_str).with_context(
                                            || {
                                                format!(
                                                    "Could not write file `{}`\nwith config: {}",
                                                    file_path.display(),
                                                    &conf_str
                                                )
                                            },
                                        )?;
                                    }
                                    Err(err) => {
                                        return Err(anyhow!(
                                            "Could not serialize default configuration!\nError: {:?}",
                                            err
                                        ));
                                    }
                                };
                            }
                            Err(err) => {
                                return Err(anyhow!(
                                    "Could not load configuration!\nError: {:?}",
                                    err
                                ))
                            }
                        };
                    }
                }
                Err(err) => {
                    return Err(anyhow!(
                        "Could not assess whether configuration file exists or not!\nError: {:?}",
                        err
                    ))
                }
            }
        }

        let config_file = std::fs::read_to_string(&file_path)
            .with_context(|| format!("could not read file `{}`", file_path.display()))?;
        match toml::from_str(&config_file) {
            Ok(parsed_config) => {
                let parsed_config: Config = parsed_config;
                Ok(parsed_config)
            }
            Err(err) => Err(anyhow!("Could not parse configuration!\nError: {:?}", err)),
        }
    }
}

fn set_default_path(path_buffer: &mut PathBuf, filename: &str) -> Result<()> {
    let home_folder = get_homedir()?;
    let default_folder = ".todo-txt";
    path_buffer.push(&home_folder);
    path_buffer.push(default_folder);
    path_buffer.push(filename);
    Ok(())
}

fn get_homedir() -> Result<PathBuf> {
    if let Some(dir) = dirs::home_dir() {
        Ok(dir)
    } else {
        Err(anyhow!(
            "Issue with `dirs` crate: could not fetch home directory."
        ))
    }
}

fn get_configdir() -> Result<PathBuf> {
    if let Some(dir) = dirs::config_dir() {
        Ok(dir)
    } else {
        Err(anyhow!(
            "Issue with `dirs` crate: could not fetch configuration directory."
        ))
    }
}