aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/habit.rs63
-rw-r--r--src/views.rs80
2 files changed, 143 insertions, 0 deletions
diff --git a/src/habit.rs b/src/habit.rs
new file mode 100644
index 0000000..4f05217
--- /dev/null
+++ b/src/habit.rs
@@ -0,0 +1,63 @@
1use std::collections::HashMap;
2
3use chrono::NaiveDate;
4
5#[derive(Debug)]
6pub struct Habit<T> {
7 name: String,
8 stats: HashMap<NaiveDate, T>,
9}
10
11impl<T> Habit<T>
12where
13 T: Copy,
14{
15 pub fn new(name: &str) -> Habit<T> {
16 return Habit {
17 name: name.to_owned(),
18 stats: HashMap::new(),
19 };
20 }
21
22 pub fn get_name(&self) -> String {
23 return self.name.to_owned();
24 }
25
26 pub fn get_by_date(&self, date: NaiveDate) -> Option<&T> {
27 self.stats.get(&date)
28 }
29
30 pub fn insert_entry(&mut self, date: NaiveDate, val: T) {
31 *self.stats.entry(date).or_insert(val) = val;
32 }
33}
34
35impl Habit<bool> {
36 pub fn toggle(&mut self, date: NaiveDate) {
37 if let Some(v) = self.stats.get_mut(&date) {
38 *v ^= true
39 } else {
40 self.insert_entry(date, true);
41 }
42 }
43}
44
45impl Habit<u32> {
46 pub fn increment(&mut self, date: NaiveDate) {
47 if let Some(v) = self.stats.get_mut(&date) {
48 *v += 1
49 }
50 }
51 pub fn decrement(&mut self, date: NaiveDate) {
52 if let Some(v) = self.stats.get_mut(&date) {
53 if *v >= 1 {
54 *v -= 1;
55 } else {
56 *v = 0;
57 };
58 }
59 }
60 pub fn set(&mut self, date: NaiveDate, val: u32) {
61 *self.stats.entry(date).or_insert(val) = val;
62 }
63}
diff --git a/src/views.rs b/src/views.rs
new file mode 100644
index 0000000..5b9ec20
--- /dev/null
+++ b/src/views.rs
@@ -0,0 +1,80 @@
1use cursive::direction::Direction;
2use cursive::event::{Event, EventResult, Key};
3use cursive::view::View;
4use cursive::{Printer, Vec2};
5
6use chrono::prelude::*;
7use chrono::{Local, NaiveDate};
8
9use crate::habit::Habit;
10
11pub struct BitView {
12 habit: Habit<bool>,
13 true_chr: char,
14 false_chr: char,
15 future_chr: char,
16
17 view_width: u32,
18 view_height: u32,
19 // color config
20}
21
22impl BitView {
23 pub fn new(habit: Habit<bool>) -> Self {
24 return BitView {
25 habit,
26 true_chr: 'x',
27 false_chr: 'o',
28 future_chr: '.',
29 view_width: 21,
30 view_height: 9,
31 };
32 }
33 pub fn get_title(&self) -> String {
34 return self.habit.get_name().to_owned();
35 }
36}
37
38impl View for BitView {
39 fn draw(&self, printer: &Printer) {
40 let now = Local::now();
41 let year = now.year();
42 let month = now.month();
43
44 for i in 1..=31 {
45 let day = NaiveDate::from_ymd_opt(year, month, i);
46 if let Some(d) = day {
47 let day_status = self.habit.get_by_date(d).unwrap_or(&false);
48 let coords = ((i % 7) * 3, i / 7 + 2);
49
50 if d <= now.naive_utc().date() {
51 if *day_status {
52 printer.print(coords, &format!("{:^3}", self.true_chr))
53 } else {
54 printer.print(coords, &format!("{:^3}", self.false_chr))
55 }
56 } else {
57 printer.print(coords, &format!("{:^3}", self.future_chr))
58 }
59 }
60 }
61 }
62
63 fn required_size(&mut self, _: Vec2) -> Vec2 {
64 (20, 9).into()
65 }
66
67 fn take_focus(&mut self, _: Direction) -> bool {
68 true
69 }
70
71 fn on_event(&mut self, e: Event) -> EventResult {
72 match e {
73 Event::Key(Key::Enter) => {
74 self.habit.toggle(Local::now().naive_utc().date());
75 return EventResult::Consumed(None);
76 }
77 _ => return EventResult::Ignored,
78 }
79 }
80}