use std::env; use std::path::{Path, PathBuf}; use std::fs::File; use std::io::Read; use dirs::home_dir; use serde::{Serialize, Deserialize}; use crate::{error::{self, Error::*}, formatters::Formatter}; #[derive(Debug, Serialize, Deserialize)] pub enum WeekDay { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday, } #[derive(Debug, Serialize, Deserialize)] pub struct Config { /// Absolute path to the sqlite database pub database_file: PathBuf, // "/home/abraham/.timetrap.db" /// The duration of time to use for rounding with the -r flag pub round_in_seconds: u32, // 900 /// delimiter used when appending notes via t edit --append pub append_notes_delimiter: String, //" " /// an array of directories to search for user defined fomatter classes pub formatter_search_paths: Vec, //- "/home/abraham/.timetrap/formatters" /// The format to use when display is invoked without a --format option pub default_formatter: Formatter, //text /// Which auto sheet module to use. pub auto_sheet: String, //dotfiles /// an array of directories to search for user defined auto_sheet classes pub auto_sheet_search_paths: Vec, // - "/home/abraham/.timetrap/auto_sheets" /// The default command to invoke when you call t pub default_command: Option, /// Automatically check out of running entries when you check in or out pub auto_checkout: bool, // false /// Prompt for a note if one isn't provided when checking in pub require_note: bool, // true /// The command to start editing notes. Defaults to false which means no /// external editor is used. Please see the section below on Notes Editing /// for tips on using non-terminal based editors. Example: note_editor: /// "vim" pub note_editor: String, // nvim /// The day of the week to use as the start of the week for t week. pub week_start: WeekDay, // Monday } impl Config { /// Tries as hard as possible to read the current configuration. Retrieving /// the path to it from the environment or common locations. pub fn read() -> error::Result { // first try from env variable TIMETRAP_CONFIG_FILE if let Ok(value) = env::var("TIMETRAP_CONFIG_FILE") { if value.ends_with(".toml") { return Ok(Self::read_from_toml(value)?); } else { return Ok(Self::read_from_yaml(value)?); } } // Next try from some known directories if let Some(home) = home_dir() { let mut old_location = home.clone(); old_location.push(".timetrap.yml"); if old_location.is_file() { return Self::read_from_yaml(old_location); } let mut new_location = home.clone(); new_location.push(".config"); new_location.push("timetrap"); new_location.push("config.toml"); if new_location.is_file() { Self::read_from_toml(new_location) } else { Self::create_and_return_config() } } else { Err(error::Error::NoHomeDir) } } fn read_from_yaml>(path: P) -> error::Result { let path: PathBuf = path.as_ref().into(); let mut contents = String::new(); let mut file = File::open(&path).map_err(|_| FileNotFound(path.clone()))?; file.read_to_string(&mut contents).map_err(|_| CouldntRead(path.clone()))?; serde_yaml::from_str(&contents).map_err(|error| YamlError { path, error }) } fn read_from_toml>(path: P) -> error::Result { let path: PathBuf = path.as_ref().into(); let mut contents = String::new(); let mut file = File::open(&path).map_err(|_| FileNotFound(path.clone()))?; file.read_to_string(&mut contents).map_err(|_| CouldntRead(path.clone()))?; toml::from_str(&contents).map_err(|error| TomlError { path, error }) } fn create_and_return_config() -> error::Result { unimplemented!() } } impl Default for Config { fn default() -> Config { Config { database_file: PathBuf::new(), // "/home/abraham/.timetrap.db" round_in_seconds: 900, // 900 append_notes_delimiter: " ".into(), //" " formatter_search_paths: Vec::new(), //- "/home/abraham/.timetrap/formatters" default_formatter: Formatter::Text, //text auto_sheet: "dotfiles".into(), //dotfiles auto_sheet_search_paths: Vec::new(), // - "/home/abraham/.timetrap/auto_sheets" default_command: None, auto_checkout: false, // false require_note: true, // true note_editor: "vim".into(), // nvim week_start: WeekDay::Monday, // Monday } } }