toddi 0.3.2

A TODO focuser built on top of todo.txt
Documentation
use std::path::PathBuf;

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

#[derive(Deserialize, Serialize)]
pub struct Config {
    pub todo_files: Vec<PathBuf>,
}

impl Config {
    fn new() -> Result<Self> {
        let mut todo_txt_paths: Vec<PathBuf> = Vec::new();
        set_default_path(&mut todo_txt_paths, toddi::TODO_FILE)?;
        Ok(Config {
            todo_files: todo_txt_paths,
        })
    }

    pub fn load(config_file_path: PathBuf) -> Result<Config> {
        match std::fs::exists(&config_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) = config_file_path.parent() {
                                        if !parent_dir.exists() {
                                            std::fs::create_dir_all(parent_dir)?;
                                        }
                                    }
                                    println!(
                                        "Writing default configuration file: {}",
                                        config_file_path.display()
                                    );
                                    std::fs::write(&config_file_path, &conf_str).with_context(
                                        || {
                                            format!(
                                                "Could not write file `{}`\nwith config: {}",
                                                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(&config_file_path)
            .with_context(|| format!("could not read file `{}`", config_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(paths: &mut Vec<PathBuf>, filename: &str) -> Result<()> {
    let home_folder = get_homedir()?;
    let default_folder = ".todo-txt";
    let mut path_buffer = std::path::PathBuf::new();
    path_buffer.push(&home_folder);
    path_buffer.push(default_folder);
    path_buffer.push(filename);
    paths.push(path_buffer);
    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."
        ))
    }
}