jobber 1.1.5-alpha

Minimalistic console work time tracker
use crate::{commands::Result, views::TableStyle};
use derive_more::{Deref, DerefMut};
use serde::{Deserialize, Serialize};
use tabled::{
    grid::config::Borders,
    settings::{Style, Theme},
};

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct ConfigInner {
    pub(crate) current_job: Option<usize>,
    pub(crate) table_style: TableStyle,
}

#[derive(Deref, DerefMut)]
pub(crate) struct Config {
    #[deref]
    #[deref_mut]
    config: ConfigInner,
    original_config: ConfigInner,
}

impl Config {
    fn app_name<'a>() -> &'a str {
        env!("CARGO_PKG_NAME")
    }

    pub(crate) fn load() -> Result<Config> {
        let config: ConfigInner = confy::load(Self::app_name(), None)?;
        Ok(Self {
            original_config: config.clone(),
            config: config.clone(),
        })
    }

    pub(crate) fn set_current_job(&mut self, job_id: usize) {
        self.config.current_job = Some(job_id);
    }

    pub(crate) fn calendar_theme(&self) -> Theme {
        let mut theme = Theme::from_style(Style::empty());
        match self.table_style {
            TableStyle::Empty => (),
            TableStyle::Markdown => theme.set_borders(MARKDOWN),
            TableStyle::Rounded => theme.set_borders(ROUNDED),
        }
        theme
    }

    pub(crate) fn app_path(&self) -> Result<std::path::PathBuf> {
        Ok(confy::get_configuration_file_path(Self::app_name(), None)?
            .parent()
            .expect("invalid configuration path")
            .to_path_buf())
    }

    pub(crate) fn database_path(&self) -> Result<std::path::PathBuf> {
        Ok(self.app_path()?.join("jobber.json"))
    }
}

impl Drop for Config {
    fn drop(&mut self) {
        if self.config != self.original_config {
            confy::store(Self::app_name(), None, &self.config)
                .expect("failed to store configuration")
        }
    }
}

const ROUNDED: Borders<char> = Borders {
    top: Some(''),
    bottom: Some(''),
    left: Some(''),
    right: Some(''),
    top_left: Some(''),
    top_right: Some(''),
    bottom_left: Some(''),
    bottom_right: Some(''),
    top_intersection: None,
    bottom_intersection: None,
    horizontal: None,
    vertical: None,
    intersection: None,
    left_intersection: None,
    right_intersection: None,
};

const MARKDOWN: Borders<char> = Borders {
    top: None,
    bottom: None,
    left: Some('|'),
    right: Some('|'),
    top_left: None,
    top_right: None,
    bottom_left: None,
    bottom_right: None,
    top_intersection: None,
    bottom_intersection: None,
    horizontal: None,
    vertical: None,
    intersection: None,
    left_intersection: None,
    right_intersection: None,
};