diff options
Diffstat (limited to 'src/habit')
-rw-r--r-- | src/habit/bit.rs | 117 | ||||
-rw-r--r-- | src/habit/count.rs | 102 | ||||
-rw-r--r-- | src/habit/mod.rs | 20 | ||||
-rw-r--r-- | src/habit/prelude.rs | 20 | ||||
-rw-r--r-- | src/habit/traits.rs | 94 |
5 files changed, 353 insertions, 0 deletions
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 @@ | |||
1 | use std::collections::HashMap; | ||
2 | |||
3 | use chrono::NaiveDate; | ||
4 | use serde::{Deserialize, Serialize}; | ||
5 | |||
6 | use crate::habit::traits::Habit; | ||
7 | use crate::habit::{TrackEvent, ViewMode}; | ||
8 | use crate::CONFIGURATION; | ||
9 | |||
10 | #[derive(Copy, Clone, Debug, Serialize, Deserialize)] | ||
11 | pub struct CustomBool(bool); | ||
12 | |||
13 | use std::fmt; | ||
14 | impl 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 | |||
28 | impl From<bool> for CustomBool { | ||
29 | fn from(b: bool) -> Self { | ||
30 | CustomBool(b) | ||
31 | } | ||
32 | } | ||
33 | |||
34 | #[derive(Debug, Serialize, Deserialize)] | ||
35 | pub 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 | |||
47 | impl 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 | |||
59 | impl 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 @@ | |||
1 | use std::collections::HashMap; | ||
2 | |||
3 | use chrono::NaiveDate; | ||
4 | use serde::{Deserialize, Serialize}; | ||
5 | |||
6 | use crate::habit::traits::Habit; | ||
7 | use crate::habit::{TrackEvent, ViewMode}; | ||
8 | |||
9 | #[derive(Debug, Serialize, Deserialize)] | ||
10 | pub 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 | |||
22 | impl 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 | |||
34 | impl 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 @@ | |||
1 | use std::collections::HashMap; | ||
2 | |||
3 | use chrono::NaiveDate; | ||
4 | use serde::{Deserialize, Serialize}; | ||
5 | |||
6 | use cursive::direction::Direction; | ||
7 | use cursive::event::{Event, EventResult}; | ||
8 | use cursive::{Printer, Vec2}; | ||
9 | |||
10 | mod traits; | ||
11 | pub use traits::{Habit, HabitWrapper}; | ||
12 | |||
13 | mod count; | ||
14 | pub use count::Count; | ||
15 | |||
16 | mod bit; | ||
17 | pub use bit::Bit; | ||
18 | |||
19 | mod prelude; | ||
20 | pub 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 @@ | |||
1 | use serde::{Deserialize, Serialize}; | ||
2 | |||
3 | pub enum TrackEvent { | ||
4 | Increment, | ||
5 | Decrement, | ||
6 | } | ||
7 | |||
8 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] | ||
9 | pub enum ViewMode { | ||
10 | Day, | ||
11 | Week, | ||
12 | Month, | ||
13 | Year, | ||
14 | } | ||
15 | |||
16 | impl 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 @@ | |||
1 | use chrono::NaiveDate; | ||
2 | use cursive::direction::Direction; | ||
3 | use cursive::event::{Event, EventResult}; | ||
4 | use cursive::{Printer, Vec2}; | ||
5 | |||
6 | use typetag; | ||
7 | |||
8 | use crate::habit::{Bit, Count, TrackEvent, ViewMode}; | ||
9 | use crate::views::ShadowView; | ||
10 | |||
11 | pub 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")] | ||
32 | pub 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 | |||
49 | macro_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 | |||
93 | auto_habit_impl!(Count); | ||
94 | auto_habit_impl!(Bit); | ||