genee 0.9.3

A habit tracker app with command-line interface
Documentation
use super::table_utils;
use crate::{CliOptions, datafile::DiaryDataSqlite};
use anyhow::{Ok, Result};
use chrono::NaiveDate;
use ratatui::{prelude::*, widgets::*};

pub enum TopOccurrenceListWidgetInput {
    UpdateRange((NaiveDate, NaiveDate)),
}

pub struct TopOccurrenceListWidget {
    range_from: NaiveDate,
    range_until: NaiveDate,
    count: usize,
    header: Vec<(String, usize)>,
    data: Vec<(Vec<usize>, usize)>,
}

impl TopOccurrenceListWidget {
    pub fn new(
        datafile: &DiaryDataSqlite,
        range_from: NaiveDate,
        range_until: NaiveDate,
        opts: &CliOptions,
    ) -> Result<Self> {
        let header = datafile.get_header()?;
        let mut widget = TopOccurrenceListWidget {
            range_from,
            range_until,
            count: opts.list_most_frequent_days.unwrap(),
            header,
            data: vec![],
        };
        widget.update_data(datafile)?;
        Ok(widget)
    }

    pub fn render(&mut self, frame: &mut Frame, area: Rect) {
        let widths: Vec<Constraint> = (0..self.header.len() + 1)
            .map(|i| {
                if i == 0 {
                    Constraint::Max(5)
                } else {
                    Constraint::Max(3)
                }
            })
            .collect();
        let mut rows = vec![table_utils::get_table_header(&self.header, "Count")];
        for (ids, count) in &self.data {
            let mut cells = vec![Cell::new(format!("{:5}", count))];
            for habit_val in table_utils::decode_habit_vector(&self.header, ids) {
                cells.push(if habit_val {
                    Cell::from("")
                } else {
                    Cell::from(" ")
                });
            }
            rows.push(Row::new(cells));
        }
        let table = Table::new(rows, widths).block(Block::bordered().title(self.title()));
        frame.render_widget(table, area);
    }

    pub fn expected_height(&self) -> usize {
        self.count + 3
    }

    pub fn update(
        &mut self,
        datafile: &DiaryDataSqlite,
        input: TopOccurrenceListWidgetInput,
    ) -> Result<()> {
        match input {
            TopOccurrenceListWidgetInput::UpdateRange((from, until)) => {
                self.range_from = from;
                self.range_until = until;
                self.update_data(datafile)?;
            }
        }
        Ok(())
    }

    pub fn update_opts(&self, opts: &mut CliOptions) {
        opts.list_most_frequent_days = Some(self.count);
    }

    fn update_data(&mut self, datafile: &DiaryDataSqlite) -> Result<()> {
        self.data = datafile.get_most_frequent_daily_data(
            &Some(self.range_from),
            &self.range_until,
            Some(self.count),
        )?;
        Ok(())
    }

    fn title(&self) -> String {
        format!(
            "Most occurring daily habits from {} until {}",
            self.range_from, self.range_until
        )
    }
}