aboutsummaryrefslogtreecommitdiff
path: root/src/habit.rs
blob: 7d50adfb98ca937427d2c9f46c32ce929ac953e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use std::collections::HashMap;

use chrono::NaiveDate;
use serde::Serialize;

#[derive(Serialize, Debug, Clone, Copy)]
pub enum HabitType {
    Bit(bool),
    Count(u32),
}

impl HabitType {
    fn inner_bit(&self) -> bool {
        if let HabitType::Bit(v) = self {
            *v
        } else {
            panic!("why");
        }
    }
    fn inner_count(&self) -> u32 {
        if let HabitType::Count(v) = self {
            *v
        } else {
            panic!("why");
        }
    }
}

pub trait HabitTrait {
    fn set_name(&mut self, name: impl AsRef<str>);
    fn set_goal(&mut self, goal: HabitType);
    fn get_name(&self) -> String;
    fn get_by_date(&self, date: NaiveDate) -> Option<&HabitType>;
    fn insert_entry(&mut self, date: NaiveDate, val: HabitType);
    fn reached_goal(&self, date: NaiveDate) -> bool;
    fn remaining(&self, date: NaiveDate) -> u32;
    fn total(&self) -> u32;
}

#[derive(Serialize, Debug)]
pub struct Habit {
    name: String,
    stats: HashMap<NaiveDate, HabitType>,
    goal: HabitType,
}

pub enum TrackEvent {
    Increment,
    Decrement,
}

impl Habit {
    pub fn new(name: impl AsRef<str>, goal: HabitType) -> Self {
        return Habit {
            name: name.as_ref().to_owned(),
            stats: HashMap::new(),
            goal,
        };
    }

    pub fn modify(&mut self, date: NaiveDate, event: TrackEvent) {
        if let Some(val) = self.stats.get_mut(&date) {
            match val {
                HabitType::Bit(b) => *b ^= true,
                HabitType::Count(c) => match event {
                    TrackEvent::Increment => *c += 1,
                    TrackEvent::Decrement => {
                        if *c > 0 {
                            *c -= 1;
                        } else {
                            *c = 0;
                        }
                    }
                },
            }
        } else {
            match self.goal {
                HabitType::Bit(_) => self.insert_entry(date, HabitType::Bit(true)),
                HabitType::Count(_) => self.insert_entry(date, HabitType::Count(1)),
            }
        }
    }
}

impl HabitTrait for Habit {
    fn get_name(&self) -> String {
        return self.name.to_owned();
    }
    fn set_name(&mut self, n: impl AsRef<str>) {
        self.name = n.as_ref().to_owned();
    }
    fn set_goal(&mut self, g: HabitType) {
        self.goal = g;
    }
    fn get_by_date(&self, date: NaiveDate) -> Option<&HabitType> {
        self.stats.get(&date)
    }
    fn insert_entry(&mut self, date: NaiveDate, val: HabitType) {
        *self.stats.entry(date).or_insert(val) = val;
    }
    fn reached_goal(&self, date: NaiveDate) -> bool {
        if let Some(val) = self.stats.get(&date) {
            match val {
                HabitType::Bit(b) => return *b,
                HabitType::Count(c) => {
                    if *c >= self.goal.inner_count() {
                        return true;
                    }
                }
            };
        }
        return false;
    }
    fn remaining(&self, date: NaiveDate) -> u32 {
        if self.reached_goal(date) {
            return 0;
        } else if let Some(val) = self.stats.get(&date) {
            match val {
                HabitType::Bit(_) => return 1,
                HabitType::Count(c) => return self.goal.inner_count() - *c,
            }
        } else {
            return self.total();
        }
    }
    fn total(&self) -> u32 {
        match self.goal {
            HabitType::Bit(_) => return 1,
            HabitType::Count(c) => return c,
        }
    }
}