diff options
Diffstat (limited to 'src/habit.rs')
-rw-r--r-- | src/habit.rs | 248 |
1 files changed, 174 insertions, 74 deletions
diff --git a/src/habit.rs b/src/habit.rs index 7d50adf..3716183 100644 --- a/src/habit.rs +++ b/src/habit.rs | |||
@@ -3,130 +3,230 @@ use std::collections::HashMap; | |||
3 | use chrono::NaiveDate; | 3 | use chrono::NaiveDate; |
4 | use serde::Serialize; | 4 | use serde::Serialize; |
5 | 5 | ||
6 | #[derive(Serialize, Debug, Clone, Copy)] | 6 | use cursive::direction::Direction; |
7 | pub enum HabitType { | 7 | use cursive::event::{Event, EventResult}; |
8 | Bit(bool), | 8 | use cursive::{Printer, Vec2}; |
9 | Count(u32), | 9 | |
10 | use crate::views::ShadowView; | ||
11 | use crate::CONFIGURATION; | ||
12 | |||
13 | pub enum TrackEvent { | ||
14 | Increment, | ||
15 | Decrement, | ||
10 | } | 16 | } |
11 | 17 | ||
12 | impl HabitType { | 18 | #[derive(Copy, Clone, Debug, Serialize)] |
13 | fn inner_bit(&self) -> bool { | 19 | pub struct CustomBool(bool); |
14 | if let HabitType::Bit(v) = self { | 20 | |
15 | *v | 21 | use std::fmt; |
16 | } else { | 22 | impl fmt::Display for CustomBool { |
17 | panic!("why"); | 23 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
18 | } | 24 | write!( |
25 | f, | ||
26 | "{}", | ||
27 | if self.0 { | ||
28 | CONFIGURATION.true_chr | ||
29 | } else { | ||
30 | CONFIGURATION.false_chr | ||
31 | } | ||
32 | ) | ||
19 | } | 33 | } |
20 | fn inner_count(&self) -> u32 { | 34 | } |
21 | if let HabitType::Count(v) = self { | 35 | |
22 | *v | 36 | impl From<bool> for CustomBool { |
23 | } else { | 37 | fn from(b: bool) -> Self { |
24 | panic!("why"); | 38 | CustomBool(b) |
25 | } | ||
26 | } | 39 | } |
27 | } | 40 | } |
28 | 41 | ||
29 | pub trait HabitTrait { | 42 | pub trait Habit { |
43 | type HabitType; | ||
44 | |||
30 | fn set_name(&mut self, name: impl AsRef<str>); | 45 | fn set_name(&mut self, name: impl AsRef<str>); |
31 | fn set_goal(&mut self, goal: HabitType); | 46 | fn set_goal(&mut self, goal: Self::HabitType); |
32 | fn get_name(&self) -> String; | 47 | fn name(&self) -> String; |
33 | fn get_by_date(&self, date: NaiveDate) -> Option<&HabitType>; | 48 | fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType>; |
34 | fn insert_entry(&mut self, date: NaiveDate, val: HabitType); | 49 | fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType); |
35 | fn reached_goal(&self, date: NaiveDate) -> bool; | 50 | fn reached_goal(&self, date: NaiveDate) -> bool; |
36 | fn remaining(&self, date: NaiveDate) -> u32; | 51 | fn remaining(&self, date: NaiveDate) -> u32; |
37 | fn total(&self) -> u32; | 52 | fn total(&self) -> u32; |
53 | fn modify(&mut self, date: NaiveDate, event: TrackEvent); | ||
38 | } | 54 | } |
39 | 55 | ||
40 | #[derive(Serialize, Debug)] | 56 | pub trait HabitWrapper { |
41 | pub struct Habit { | 57 | fn remaining(&self, date: NaiveDate) -> u32; |
42 | name: String, | 58 | fn total(&self) -> u32; |
43 | stats: HashMap<NaiveDate, HabitType>, | 59 | fn modify(&mut self, date: NaiveDate, event: TrackEvent); |
44 | goal: HabitType, | 60 | fn draw(&self, printer: &Printer); |
61 | fn on_event(&mut self, event: Event) -> EventResult; | ||
62 | fn required_size(&mut self, _: Vec2) -> Vec2; | ||
63 | fn take_focus(&mut self, _: Direction) -> bool; | ||
45 | } | 64 | } |
46 | 65 | ||
47 | pub enum TrackEvent { | 66 | impl<T> HabitWrapper for T |
48 | Increment, | 67 | where |
49 | Decrement, | 68 | T: Habit + ShadowView, |
69 | T::HabitType: std::fmt::Display, | ||
70 | { | ||
71 | fn remaining(&self, date: NaiveDate) -> u32 { | ||
72 | Habit::remaining(self, date) | ||
73 | } | ||
74 | fn total(&self) -> u32 { | ||
75 | Habit::total(self) | ||
76 | } | ||
77 | fn modify(&mut self, date: NaiveDate, event: TrackEvent) { | ||
78 | Habit::modify(self, date, event); | ||
79 | } | ||
80 | fn draw(&self, printer: &Printer) { | ||
81 | ShadowView::draw(self, printer) | ||
82 | } | ||
83 | fn on_event(&mut self, event: Event) -> EventResult { | ||
84 | ShadowView::on_event(self, event) | ||
85 | } | ||
86 | fn required_size(&mut self, x: Vec2) -> Vec2 { | ||
87 | ShadowView::required_size(self, x) | ||
88 | } | ||
89 | fn take_focus(&mut self, d: Direction) -> bool { | ||
90 | ShadowView::take_focus(self, d) | ||
91 | } | ||
50 | } | 92 | } |
51 | 93 | ||
52 | impl Habit { | 94 | #[derive(Debug, Serialize)] |
53 | pub fn new(name: impl AsRef<str>, goal: HabitType) -> Self { | 95 | pub struct Count { |
54 | return Habit { | 96 | name: String, |
97 | stats: HashMap<NaiveDate, u32>, | ||
98 | goal: u32, | ||
99 | } | ||
100 | |||
101 | impl Count { | ||
102 | pub fn new(name: impl AsRef<str>, goal: u32) -> Self { | ||
103 | return Count { | ||
55 | name: name.as_ref().to_owned(), | 104 | name: name.as_ref().to_owned(), |
56 | stats: HashMap::new(), | 105 | stats: HashMap::new(), |
57 | goal, | 106 | goal, |
58 | }; | 107 | }; |
59 | } | 108 | } |
109 | } | ||
60 | 110 | ||
61 | pub fn modify(&mut self, date: NaiveDate, event: TrackEvent) { | 111 | impl Habit for Count { |
62 | if let Some(val) = self.stats.get_mut(&date) { | 112 | type HabitType = u32; |
63 | match val { | 113 | |
64 | HabitType::Bit(b) => *b ^= true, | 114 | fn name(&self) -> String { |
65 | HabitType::Count(c) => match event { | 115 | return self.name.clone(); |
66 | TrackEvent::Increment => *c += 1, | 116 | } |
67 | TrackEvent::Decrement => { | 117 | fn set_name(&mut self, n: impl AsRef<str>) { |
68 | if *c > 0 { | 118 | self.name = n.as_ref().to_owned(); |
69 | *c -= 1; | 119 | } |
70 | } else { | 120 | fn set_goal(&mut self, g: Self::HabitType) { |
71 | *c = 0; | 121 | self.goal = g; |
72 | } | 122 | } |
73 | } | 123 | fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType> { |
74 | }, | 124 | self.stats.get(&date) |
125 | } | ||
126 | fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType) { | ||
127 | *self.stats.entry(date).or_insert(val) = val; | ||
128 | } | ||
129 | fn reached_goal(&self, date: NaiveDate) -> bool { | ||
130 | if let Some(val) = self.stats.get(&date) { | ||
131 | if val >= &self.goal { | ||
132 | return true; | ||
75 | } | 133 | } |
134 | } | ||
135 | return false; | ||
136 | } | ||
137 | fn remaining(&self, date: NaiveDate) -> u32 { | ||
138 | if self.reached_goal(date) { | ||
139 | return 0; | ||
76 | } else { | 140 | } else { |
77 | match self.goal { | 141 | if let Some(val) = self.stats.get(&date) { |
78 | HabitType::Bit(_) => self.insert_entry(date, HabitType::Bit(true)), | 142 | return self.goal - val; |
79 | HabitType::Count(_) => self.insert_entry(date, HabitType::Count(1)), | 143 | } else { |
144 | return self.goal; | ||
80 | } | 145 | } |
81 | } | 146 | } |
82 | } | 147 | } |
148 | fn total(&self) -> u32 { | ||
149 | return self.goal; | ||
150 | } | ||
151 | fn modify(&mut self, date: NaiveDate, event: TrackEvent) { | ||
152 | if let Some(val) = self.stats.get_mut(&date) { | ||
153 | match event { | ||
154 | TrackEvent::Increment => *val += 1, | ||
155 | TrackEvent::Decrement => { | ||
156 | if *val > 0 { | ||
157 | *val -= 1 | ||
158 | } else { | ||
159 | *val = 0 | ||
160 | }; | ||
161 | } | ||
162 | } | ||
163 | } else { | ||
164 | self.insert_entry(date, 1); | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | |||
169 | #[derive(Debug, Serialize)] | ||
170 | pub struct Bit { | ||
171 | name: String, | ||
172 | stats: HashMap<NaiveDate, CustomBool>, | ||
173 | goal: CustomBool, | ||
83 | } | 174 | } |
84 | 175 | ||
85 | impl HabitTrait for Habit { | 176 | impl Bit { |
86 | fn get_name(&self) -> String { | 177 | pub fn new(name: impl AsRef<str>) -> Self { |
87 | return self.name.to_owned(); | 178 | return Bit { |
179 | name: name.as_ref().to_owned(), | ||
180 | stats: HashMap::new(), | ||
181 | goal: CustomBool(true), | ||
182 | }; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | impl Habit for Bit { | ||
187 | type HabitType = CustomBool; | ||
188 | fn name(&self) -> String { | ||
189 | return self.name.clone(); | ||
88 | } | 190 | } |
89 | fn set_name(&mut self, n: impl AsRef<str>) { | 191 | fn set_name(&mut self, n: impl AsRef<str>) { |
90 | self.name = n.as_ref().to_owned(); | 192 | self.name = n.as_ref().to_owned(); |
91 | } | 193 | } |
92 | fn set_goal(&mut self, g: HabitType) { | 194 | fn set_goal(&mut self, g: Self::HabitType) { |
93 | self.goal = g; | 195 | self.goal = g; |
94 | } | 196 | } |
95 | fn get_by_date(&self, date: NaiveDate) -> Option<&HabitType> { | 197 | fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType> { |
96 | self.stats.get(&date) | 198 | self.stats.get(&date) |
97 | } | 199 | } |
98 | fn insert_entry(&mut self, date: NaiveDate, val: HabitType) { | 200 | fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType) { |
99 | *self.stats.entry(date).or_insert(val) = val; | 201 | *self.stats.entry(date).or_insert(val) = val; |
100 | } | 202 | } |
101 | fn reached_goal(&self, date: NaiveDate) -> bool { | 203 | fn reached_goal(&self, date: NaiveDate) -> bool { |
102 | if let Some(val) = self.stats.get(&date) { | 204 | if let Some(val) = self.stats.get(&date) { |
103 | match val { | 205 | if val.0 >= self.goal.0 { |
104 | HabitType::Bit(b) => return *b, | 206 | return true; |
105 | HabitType::Count(c) => { | 207 | } |
106 | if *c >= self.goal.inner_count() { | ||
107 | return true; | ||
108 | } | ||
109 | } | ||
110 | }; | ||
111 | } | 208 | } |
112 | return false; | 209 | return false; |
113 | } | 210 | } |
114 | fn remaining(&self, date: NaiveDate) -> u32 { | 211 | fn remaining(&self, date: NaiveDate) -> u32 { |
115 | if self.reached_goal(date) { | 212 | if let Some(val) = self.stats.get(&date) { |
116 | return 0; | 213 | if val.0 { |
117 | } else if let Some(val) = self.stats.get(&date) { | 214 | return 0; |
118 | match val { | 215 | } else { |
119 | HabitType::Bit(_) => return 1, | 216 | return 1; |
120 | HabitType::Count(c) => return self.goal.inner_count() - *c, | ||
121 | } | 217 | } |
122 | } else { | 218 | } else { |
123 | return self.total(); | 219 | return 1; |
124 | } | 220 | } |
125 | } | 221 | } |
126 | fn total(&self) -> u32 { | 222 | fn total(&self) -> u32 { |
127 | match self.goal { | 223 | return 1; |
128 | HabitType::Bit(_) => return 1, | 224 | } |
129 | HabitType::Count(c) => return c, | 225 | fn modify(&mut self, date: NaiveDate, _: TrackEvent) { |
226 | if let Some(val) = self.stats.get_mut(&date) { | ||
227 | *val = (val.0 ^ true).into(); | ||
228 | } else { | ||
229 | self.insert_entry(date, CustomBool(true)); | ||
130 | } | 230 | } |
131 | } | 231 | } |
132 | } | 232 | } |