jobber 1.1.5-alpha

Minimalistic console work time tracker
use crate::{
    commands::{Config, not_available},
    entities::{DateTime, Limit, RunningWork, Scheme, Tag, Work, format_tag},
    views::ApplyTableStyle,
};
use color_print::cformat;
use std::collections::HashMap;
use tabled::{
    Table, Tabled,
    settings::{Alignment, Width, object::Columns},
};

#[derive(Tabled, Clone)]
pub(crate) struct WorkView {
    #[tabled(rename = "Pos")]
    pub(crate) no: String,
    #[tabled(rename = "Start")]
    pub(crate) start: String,
    #[tabled(rename = "End")]
    pub(crate) end: String,
    #[tabled(rename = "Duration")]
    pub(crate) duration: String,
    #[tabled(rename = "Subject")]
    pub(crate) subject: String,
    #[tabled(rename = "Tags")]
    pub(crate) tags: String,
    #[tabled(skip)]
    pub(crate) order: DateTime,
}

impl WorkView {
    pub(crate) fn new(
        no: Option<usize>,
        work: &Work,
        tags: &HashMap<String, Tag>,
        scheme: &Scheme,
        ansi: bool,
    ) -> Self {
        Self {
            no: no.map(|no| no.to_string()).unwrap_or_default(),
            start: scheme.format_date_time(work.start()),
            end: scheme.format_time(work.end()),
            duration: scheme.format_duration(work.duration(), crate::entities::Limit::Day, ansi),
            subject: work.subject.clone(),
            tags: if work.tags.is_empty() {
                not_available(ansi)
            } else {
                work.tags
                    .iter()
                    .filter_map(|id| tags.get(id).map(|tag| (id, tag)))
                    .map(|(id, tag)| format_tag(id, tag, ansi))
                    .collect::<Vec<_>>()
                    .join(",")
            },
            order: work.start(),
        }
    }

    pub(crate) fn new_running(running: &RunningWork, scheme: &Scheme, ansi: bool) -> Self {
        Self {
            no: if ansi {
                cformat!("👷")
            } else {
                "(working)".into()
            },
            start: scheme.format_date_time(running.start),
            end: "...".to_string(),
            duration: scheme.format_duration(running.duration(), Limit::Day, ansi),
            subject: running.subject.clone().unwrap_or_default(),
            tags: if running.tags.is_empty() {
                not_available(ansi)
            } else {
                running
                    .tags
                    .iter()
                    .map(|t| t.as_str())
                    .collect::<Vec<_>>()
                    .join(",")
            },
            order: running.start,
        }
    }
}

pub(crate) fn create_work_table(
    rows: impl IntoIterator<Item = WorkView>,
    config: &Config,
) -> Table {
    let mut table = Table::new(rows);
    table.modify(Columns::first(), Alignment::right());
    table.modify(Columns::one(1), Alignment::right());
    table.modify(Columns::one(2), Alignment::right());
    table.modify(Columns::one(3), Alignment::right());
    table.modify(Columns::one(4), Width::wrap(40));
    table.style(&config.table_style);
    table
}