use std::collections::HashMap; use chrono::NaiveDate; use cursive::direction::Absolute; use serde::{Deserialize, Serialize}; use crate::app::Cursor; use crate::habit::prelude::default_auto; use crate::habit::traits::Habit; use crate::habit::{TrackEvent, ViewMode}; use crate::CONFIGURATION; #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct CustomBool(bool); use std::fmt; impl fmt::Display for CustomBool { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{:^3}", if self.0 { CONFIGURATION.look.true_chr } else { CONFIGURATION.look.false_chr } ) } } impl From for CustomBool { fn from(b: bool) -> Self { CustomBool(b) } } #[derive(Debug, Serialize, Deserialize)] pub struct Bit { name: String, stats: HashMap, goal: CustomBool, #[serde(default = "default_auto")] auto: bool, #[serde(skip)] view_month_offset: u32, #[serde(skip)] cursor: Cursor, #[serde(skip)] view_mode: ViewMode, } impl Bit { pub fn new(name: impl AsRef, auto: bool) -> Self { return Bit { name: name.as_ref().to_owned(), stats: HashMap::new(), goal: CustomBool(true), auto, view_month_offset: 0, cursor: Cursor::new(), view_mode: ViewMode::Day, }; } } impl Habit for Bit { type HabitType = CustomBool; fn name(&self) -> String { return self.name.clone(); } fn set_name(&mut self, n: impl AsRef) { self.name = n.as_ref().to_owned(); } fn set_goal(&mut self, g: Self::HabitType) { self.goal = g; } fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType> { self.stats.get(&date) } fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType) { *self.stats.entry(date).or_insert(val) = val; } fn reached_goal(&self, date: NaiveDate) -> bool { if let Some(val) = self.stats.get(&date) { if val.0 >= self.goal.0 { return true; } } return false; } fn remaining(&self, date: NaiveDate) -> u32 { if let Some(val) = self.stats.get(&date) { if val.0 { return 0; } else { return 1; } } else { return 1; } } fn goal(&self) -> u32 { return 1; } fn modify(&mut self, date: NaiveDate, event: TrackEvent) { if let Some(val) = self.stats.get_mut(&date) { match event { TrackEvent::Increment => *val = (val.0 ^ true).into(), TrackEvent::Decrement => { if val.0 { *val = false.into(); } else { self.stats.remove(&date); } } } } else { if event == TrackEvent::Increment { self.insert_entry(date, CustomBool(true)); } } } fn set_view_month_offset(&mut self, offset: u32) { self.view_month_offset = offset; } fn view_month_offset(&self) -> u32 { self.view_month_offset } fn move_cursor(&mut self, d: Absolute) { self.cursor.do_move(d); } fn cursor(&self) -> Cursor { self.cursor } fn set_view_mode(&mut self, mode: ViewMode) { self.view_mode = mode; } fn view_mode(&self) -> ViewMode { self.view_mode } fn is_auto(&self) -> bool { self.auto } }