aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/habit.rs319
-rw-r--r--src/habit/bit.rs117
-rw-r--r--src/habit/count.rs102
-rw-r--r--src/habit/mod.rs20
-rw-r--r--src/habit/prelude.rs20
-rw-r--r--src/habit/traits.rs94
6 files changed, 353 insertions, 319 deletions
diff --git a/src/habit.rs b/src/habit.rs
deleted file mode 100644
index 725e15f..0000000
--- a/src/habit.rs
+++ /dev/null
@@ -1,319 +0,0 @@
1use std::collections::HashMap;
2
3use chrono::NaiveDate;
4use serde::{Deserialize, Serialize};
5
6use cursive::direction::Direction;
7use cursive::event::{Event, EventResult};
8use cursive::{Printer, Vec2};
9
10use crate::views::ShadowView;
11use crate::CONFIGURATION;
12
13pub enum TrackEvent {
14 Increment,
15 Decrement,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
19pub enum ViewMode {
20 Day,
21 Week,
22 Month,
23 Year,
24}
25
26impl std::default::Default for ViewMode {
27 fn default() -> Self {
28 ViewMode::Day
29 }
30}
31
32#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
33pub struct CustomBool(bool);
34
35use std::fmt;
36impl fmt::Display for CustomBool {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(
39 f,
40 "{:^3}",
41 if self.0 {
42 CONFIGURATION.true_chr
43 } else {
44 CONFIGURATION.false_chr
45 }
46 )
47 }
48}
49
50impl From<bool> for CustomBool {
51 fn from(b: bool) -> Self {
52 CustomBool(b)
53 }
54}
55
56pub trait Habit {
57 type HabitType;
58
59 fn set_name(&mut self, name: impl AsRef<str>);
60 fn set_goal(&mut self, goal: Self::HabitType);
61 fn name(&self) -> String;
62 fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType>;
63 fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType);
64 fn reached_goal(&self, date: NaiveDate) -> bool;
65 fn remaining(&self, date: NaiveDate) -> u32;
66 fn goal(&self) -> u32;
67 fn modify(&mut self, date: NaiveDate, event: TrackEvent);
68
69 fn set_view_month_offset(&mut self, offset: u32);
70 fn view_month_offset(&self) -> u32;
71
72 fn set_view_mode(&mut self, mode: ViewMode);
73 fn view_mode(&self) -> ViewMode;
74}
75
76#[typetag::serde(tag = "type")]
77pub trait HabitWrapper: erased_serde::Serialize {
78 fn remaining(&self, date: NaiveDate) -> u32;
79 fn goal(&self) -> u32;
80 fn modify(&mut self, date: NaiveDate, event: TrackEvent);
81 fn draw(&self, printer: &Printer);
82 fn on_event(&mut self, event: Event) -> EventResult;
83 fn required_size(&mut self, _: Vec2) -> Vec2;
84 fn take_focus(&mut self, _: Direction) -> bool;
85 fn get_name(&self) -> String;
86
87 fn set_view_month_offset(&mut self, offset: u32);
88 fn view_month_offset(&self) -> u32;
89
90 fn set_view_mode(&mut self, mode: ViewMode);
91 fn view_mode(&self) -> ViewMode;
92}
93
94macro_rules! auto_habit_impl {
95 ($struct_name:ident) => {
96 #[typetag::serde]
97 impl HabitWrapper for $struct_name {
98 fn remaining(&self, date: NaiveDate) -> u32 {
99 Habit::remaining(self, date)
100 }
101 fn goal(&self) -> u32 {
102 Habit::goal(self)
103 }
104 fn modify(&mut self, date: NaiveDate, event: TrackEvent) {
105 Habit::modify(self, date, event);
106 }
107 fn draw(&self, printer: &Printer) {
108 ShadowView::draw(self, printer)
109 }
110 fn on_event(&mut self, event: Event) -> EventResult {
111 ShadowView::on_event(self, event)
112 }
113 fn required_size(&mut self, x: Vec2) -> Vec2 {
114 ShadowView::required_size(self, x)
115 }
116 fn take_focus(&mut self, d: Direction) -> bool {
117 ShadowView::take_focus(self, d)
118 }
119 fn get_name(&self) -> String {
120 Habit::name(self)
121 }
122 fn set_view_month_offset(&mut self, offset: u32) {
123 Habit::set_view_month_offset(self, offset)
124 }
125 fn view_month_offset(&self) -> u32 {
126 Habit::view_month_offset(self)
127 }
128 fn set_view_mode(&mut self, mode: ViewMode) {
129 Habit::set_view_mode(self, mode)
130 }
131 fn view_mode(&self) -> ViewMode {
132 Habit::view_mode(self)
133 }
134 }
135 };
136}
137
138auto_habit_impl!(Count);
139auto_habit_impl!(Bit);
140
141#[derive(Debug, Serialize, Deserialize)]
142pub struct Count {
143 name: String,
144 stats: HashMap<NaiveDate, u32>,
145 goal: u32,
146
147 #[serde(skip)]
148 view_month_offset: u32,
149
150 #[serde(skip)]
151 view_mode: ViewMode,
152}
153
154impl Count {
155 pub fn new(name: impl AsRef<str>, goal: u32) -> Self {
156 return Count {
157 name: name.as_ref().to_owned(),
158 stats: HashMap::new(),
159 goal,
160 view_month_offset: 0,
161 view_mode: ViewMode::Day,
162 };
163 }
164}
165
166impl Habit for Count {
167 type HabitType = u32;
168
169 fn name(&self) -> String {
170 return self.name.clone();
171 }
172 fn set_name(&mut self, n: impl AsRef<str>) {
173 self.name = n.as_ref().to_owned();
174 }
175 fn set_goal(&mut self, g: Self::HabitType) {
176 self.goal = g;
177 }
178 fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType> {
179 self.stats.get(&date)
180 }
181 fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType) {
182 *self.stats.entry(date).or_insert(val) = val;
183 }
184 fn reached_goal(&self, date: NaiveDate) -> bool {
185 if let Some(val) = self.stats.get(&date) {
186 if val >= &self.goal {
187 return true;
188 }
189 }
190 return false;
191 }
192 fn remaining(&self, date: NaiveDate) -> u32 {
193 if self.reached_goal(date) {
194 return 0;
195 } else {
196 if let Some(val) = self.stats.get(&date) {
197 return self.goal - val;
198 } else {
199 return self.goal;
200 }
201 }
202 }
203 fn goal(&self) -> u32 {
204 return self.goal;
205 }
206 fn modify(&mut self, date: NaiveDate, event: TrackEvent) {
207 if let Some(val) = self.stats.get_mut(&date) {
208 match event {
209 TrackEvent::Increment => *val += 1,
210 TrackEvent::Decrement => {
211 if *val > 0 {
212 *val -= 1
213 } else {
214 *val = 0
215 };
216 }
217 }
218 } else {
219 self.insert_entry(date, 1);
220 }
221 }
222 fn set_view_month_offset(&mut self, offset: u32) {
223 self.view_month_offset = offset;
224 }
225 fn view_month_offset(&self) -> u32 {
226 self.view_month_offset
227 }
228 fn set_view_mode(&mut self, mode: ViewMode) {
229 self.view_mode = mode;
230 }
231 fn view_mode(&self) -> ViewMode {
232 self.view_mode
233 }
234}
235
236#[derive(Debug, Serialize, Deserialize)]
237pub struct Bit {
238 name: String,
239 stats: HashMap<NaiveDate, CustomBool>,
240 goal: CustomBool,
241
242 #[serde(skip)]
243 view_month_offset: u32,
244
245 #[serde(skip)]
246 view_mode: ViewMode,
247}
248
249impl Bit {
250 pub fn new(name: impl AsRef<str>) -> Self {
251 return Bit {
252 name: name.as_ref().to_owned(),
253 stats: HashMap::new(),
254 goal: CustomBool(true),
255 view_month_offset: 0,
256 view_mode: ViewMode::Day,
257 };
258 }
259}
260
261impl Habit for Bit {
262 type HabitType = CustomBool;
263 fn name(&self) -> String {
264 return self.name.clone();
265 }
266 fn set_name(&mut self, n: impl AsRef<str>) {
267 self.name = n.as_ref().to_owned();
268 }
269 fn set_goal(&mut self, g: Self::HabitType) {
270 self.goal = g;
271 }
272 fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType> {
273 self.stats.get(&date)
274 }
275 fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType) {
276 *self.stats.entry(date).or_insert(val) = val;
277 }
278 fn reached_goal(&self, date: NaiveDate) -> bool {
279 if let Some(val) = self.stats.get(&date) {
280 if val.0 >= self.goal.0 {
281 return true;
282 }
283 }
284 return false;
285 }
286 fn remaining(&self, date: NaiveDate) -> u32 {
287 if let Some(val) = self.stats.get(&date) {
288 if val.0 {
289 return 0;
290 } else {
291 return 1;
292 }
293 } else {
294 return 1;
295 }
296 }
297 fn goal(&self) -> u32 {
298 return 1;
299 }
300 fn modify(&mut self, date: NaiveDate, _: TrackEvent) {
301 if let Some(val) = self.stats.get_mut(&date) {
302 *val = (val.0 ^ true).into();
303 } else {
304 self.insert_entry(date, CustomBool(true));
305 }
306 }
307 fn set_view_month_offset(&mut self, offset: u32) {
308 self.view_month_offset = offset;
309 }
310 fn view_month_offset(&self) -> u32 {
311 self.view_month_offset
312 }
313 fn set_view_mode(&mut self, mode: ViewMode) {
314 self.view_mode = mode;
315 }
316 fn view_mode(&self) -> ViewMode {
317 self.view_mode
318 }
319}
diff --git a/src/habit/bit.rs b/src/habit/bit.rs
new file mode 100644
index 0000000..292b96a
--- /dev/null
+++ b/src/habit/bit.rs
@@ -0,0 +1,117 @@
1use std::collections::HashMap;
2
3use chrono::NaiveDate;
4use serde::{Deserialize, Serialize};
5
6use crate::habit::traits::Habit;
7use crate::habit::{TrackEvent, ViewMode};
8use crate::CONFIGURATION;
9
10#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
11pub struct CustomBool(bool);
12
13use std::fmt;
14impl fmt::Display for CustomBool {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 write!(
17 f,
18 "{:^3}",
19 if self.0 {
20 CONFIGURATION.true_chr
21 } else {
22 CONFIGURATION.false_chr
23 }
24 )
25 }
26}
27
28impl From<bool> for CustomBool {
29 fn from(b: bool) -> Self {
30 CustomBool(b)
31 }
32}
33
34#[derive(Debug, Serialize, Deserialize)]
35pub struct Bit {
36 name: String,
37 stats: HashMap<NaiveDate, CustomBool>,
38 goal: CustomBool,
39
40 #[serde(skip)]
41 view_month_offset: u32,
42
43 #[serde(skip)]
44 view_mode: ViewMode,
45}
46
47impl Bit {
48 pub fn new(name: impl AsRef<str>) -> Self {
49 return Bit {
50 name: name.as_ref().to_owned(),
51 stats: HashMap::new(),
52 goal: CustomBool(true),
53 view_month_offset: 0,
54 view_mode: ViewMode::Day,
55 };
56 }
57}
58
59impl Habit for Bit {
60 type HabitType = CustomBool;
61 fn name(&self) -> String {
62 return self.name.clone();
63 }
64 fn set_name(&mut self, n: impl AsRef<str>) {
65 self.name = n.as_ref().to_owned();
66 }
67 fn set_goal(&mut self, g: Self::HabitType) {
68 self.goal = g;
69 }
70 fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType> {
71 self.stats.get(&date)
72 }
73 fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType) {
74 *self.stats.entry(date).or_insert(val) = val;
75 }
76 fn reached_goal(&self, date: NaiveDate) -> bool {
77 if let Some(val) = self.stats.get(&date) {
78 if val.0 >= self.goal.0 {
79 return true;
80 }
81 }
82 return false;
83 }
84 fn remaining(&self, date: NaiveDate) -> u32 {
85 if let Some(val) = self.stats.get(&date) {
86 if val.0 {
87 return 0;
88 } else {
89 return 1;
90 }
91 } else {
92 return 1;
93 }
94 }
95 fn goal(&self) -> u32 {
96 return 1;
97 }
98 fn modify(&mut self, date: NaiveDate, _: TrackEvent) {
99 if let Some(val) = self.stats.get_mut(&date) {
100 *val = (val.0 ^ true).into();
101 } else {
102 self.insert_entry(date, CustomBool(true));
103 }
104 }
105 fn set_view_month_offset(&mut self, offset: u32) {
106 self.view_month_offset = offset;
107 }
108 fn view_month_offset(&self) -> u32 {
109 self.view_month_offset
110 }
111 fn set_view_mode(&mut self, mode: ViewMode) {
112 self.view_mode = mode;
113 }
114 fn view_mode(&self) -> ViewMode {
115 self.view_mode
116 }
117}
diff --git a/src/habit/count.rs b/src/habit/count.rs
new file mode 100644
index 0000000..a0e0aee
--- /dev/null
+++ b/src/habit/count.rs
@@ -0,0 +1,102 @@
1use std::collections::HashMap;
2
3use chrono::NaiveDate;
4use serde::{Deserialize, Serialize};
5
6use crate::habit::traits::Habit;
7use crate::habit::{TrackEvent, ViewMode};
8
9#[derive(Debug, Serialize, Deserialize)]
10pub struct Count {
11 name: String,
12 stats: HashMap<NaiveDate, u32>,
13 goal: u32,
14
15 #[serde(skip)]
16 view_month_offset: u32,
17
18 #[serde(skip)]
19 view_mode: ViewMode,
20}
21
22impl Count {
23 pub fn new(name: impl AsRef<str>, goal: u32) -> Self {
24 return Count {
25 name: name.as_ref().to_owned(),
26 stats: HashMap::new(),
27 goal,
28 view_month_offset: 0,
29 view_mode: ViewMode::Day,
30 };
31 }
32}
33
34impl Habit for Count {
35 type HabitType = u32;
36
37 fn name(&self) -> String {
38 return self.name.clone();
39 }
40 fn set_name(&mut self, n: impl AsRef<str>) {
41 self.name = n.as_ref().to_owned();
42 }
43 fn set_goal(&mut self, g: Self::HabitType) {
44 self.goal = g;
45 }
46 fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType> {
47 self.stats.get(&date)
48 }
49 fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType) {
50 *self.stats.entry(date).or_insert(val) = val;
51 }
52 fn reached_goal(&self, date: NaiveDate) -> bool {
53 if let Some(val) = self.stats.get(&date) {
54 if val >= &self.goal {
55 return true;
56 }
57 }
58 return false;
59 }
60 fn remaining(&self, date: NaiveDate) -> u32 {
61 if self.reached_goal(date) {
62 return 0;
63 } else {
64 if let Some(val) = self.stats.get(&date) {
65 return self.goal - val;
66 } else {
67 return self.goal;
68 }
69 }
70 }
71 fn goal(&self) -> u32 {
72 return self.goal;
73 }
74 fn modify(&mut self, date: NaiveDate, event: TrackEvent) {
75 if let Some(val) = self.stats.get_mut(&date) {
76 match event {
77 TrackEvent::Increment => *val += 1,
78 TrackEvent::Decrement => {
79 if *val > 0 {
80 *val -= 1
81 } else {
82 *val = 0
83 };
84 }
85 }
86 } else {
87 self.insert_entry(date, 1);
88 }
89 }
90 fn set_view_month_offset(&mut self, offset: u32) {
91 self.view_month_offset = offset;
92 }
93 fn view_month_offset(&self) -> u32 {
94 self.view_month_offset
95 }
96 fn set_view_mode(&mut self, mode: ViewMode) {
97 self.view_mode = mode;
98 }
99 fn view_mode(&self) -> ViewMode {
100 self.view_mode
101 }
102}
diff --git a/src/habit/mod.rs b/src/habit/mod.rs
new file mode 100644
index 0000000..482ca06
--- /dev/null
+++ b/src/habit/mod.rs
@@ -0,0 +1,20 @@
1use std::collections::HashMap;
2
3use chrono::NaiveDate;
4use serde::{Deserialize, Serialize};
5
6use cursive::direction::Direction;
7use cursive::event::{Event, EventResult};
8use cursive::{Printer, Vec2};
9
10mod traits;
11pub use traits::{Habit, HabitWrapper};
12
13mod count;
14pub use count::Count;
15
16mod bit;
17pub use bit::Bit;
18
19mod prelude;
20pub use prelude::{TrackEvent, ViewMode};
diff --git a/src/habit/prelude.rs b/src/habit/prelude.rs
new file mode 100644
index 0000000..9196f00
--- /dev/null
+++ b/src/habit/prelude.rs
@@ -0,0 +1,20 @@
1use serde::{Deserialize, Serialize};
2
3pub enum TrackEvent {
4 Increment,
5 Decrement,
6}
7
8#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
9pub enum ViewMode {
10 Day,
11 Week,
12 Month,
13 Year,
14}
15
16impl std::default::Default for ViewMode {
17 fn default() -> Self {
18 ViewMode::Day
19 }
20}
diff --git a/src/habit/traits.rs b/src/habit/traits.rs
new file mode 100644
index 0000000..e28e55d
--- /dev/null
+++ b/src/habit/traits.rs
@@ -0,0 +1,94 @@
1use chrono::NaiveDate;
2use cursive::direction::Direction;
3use cursive::event::{Event, EventResult};
4use cursive::{Printer, Vec2};
5
6use typetag;
7
8use crate::habit::{Bit, Count, TrackEvent, ViewMode};
9use crate::views::ShadowView;
10
11pub trait Habit {
12 type HabitType;
13
14 fn set_name(&mut self, name: impl AsRef<str>);
15 fn set_goal(&mut self, goal: Self::HabitType);
16 fn name(&self) -> String;
17 fn get_by_date(&self, date: NaiveDate) -> Option<&Self::HabitType>;
18 fn insert_entry(&mut self, date: NaiveDate, val: Self::HabitType);
19 fn reached_goal(&self, date: NaiveDate) -> bool;
20 fn remaining(&self, date: NaiveDate) -> u32;
21 fn goal(&self) -> u32;
22 fn modify(&mut self, date: NaiveDate, event: TrackEvent);
23
24 fn set_view_month_offset(&mut self, offset: u32);
25 fn view_month_offset(&self) -> u32;
26
27 fn set_view_mode(&mut self, mode: ViewMode);
28 fn view_mode(&self) -> ViewMode;
29}
30
31#[typetag::serde(tag = "type")]
32pub trait HabitWrapper: erased_serde::Serialize {
33 fn remaining(&self, date: NaiveDate) -> u32;
34 fn goal(&self) -> u32;
35 fn modify(&mut self, date: NaiveDate, event: TrackEvent);
36 fn draw(&self, printer: &Printer);
37 fn on_event(&mut self, event: Event) -> EventResult;
38 fn required_size(&mut self, _: Vec2) -> Vec2;
39 fn take_focus(&mut self, _: Direction) -> bool;
40 fn get_name(&self) -> String;
41
42 fn set_view_month_offset(&mut self, offset: u32);
43 fn view_month_offset(&self) -> u32;
44
45 fn set_view_mode(&mut self, mode: ViewMode);
46 fn view_mode(&self) -> ViewMode;
47}
48
49macro_rules! auto_habit_impl {
50 ($struct_name:ident) => {
51 #[typetag::serde]
52 impl HabitWrapper for $struct_name {
53 fn remaining(&self, date: NaiveDate) -> u32 {
54 Habit::remaining(self, date)
55 }
56 fn goal(&self) -> u32 {
57 Habit::goal(self)
58 }
59 fn modify(&mut self, date: NaiveDate, event: TrackEvent) {
60 Habit::modify(self, date, event);
61 }
62 fn draw(&self, printer: &Printer) {
63 ShadowView::draw(self, printer)
64 }
65 fn on_event(&mut self, event: Event) -> EventResult {
66 ShadowView::on_event(self, event)
67 }
68 fn required_size(&mut self, x: Vec2) -> Vec2 {
69 ShadowView::required_size(self, x)
70 }
71 fn take_focus(&mut self, d: Direction) -> bool {
72 ShadowView::take_focus(self, d)
73 }
74 fn get_name(&self) -> String {
75 Habit::name(self)
76 }
77 fn set_view_month_offset(&mut self, offset: u32) {
78 Habit::set_view_month_offset(self, offset)
79 }
80 fn view_month_offset(&self) -> u32 {
81 Habit::view_month_offset(self)
82 }
83 fn set_view_mode(&mut self, mode: ViewMode) {
84 Habit::set_view_mode(self, mode)
85 }
86 fn view_mode(&self) -> ViewMode {
87 Habit::view_mode(self)
88 }
89 }
90 };
91}
92
93auto_habit_impl!(Count);
94auto_habit_impl!(Bit);