diff options
-rw-r--r-- | src/main.rs | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0afd0b1 --- /dev/null +++ b/src/main.rs | |||
@@ -0,0 +1,226 @@ | |||
1 | #![allow(dead_code)] | ||
2 | #![allow(unused_imports)] | ||
3 | #![allow(unused_must_use)] | ||
4 | |||
5 | use chrono::prelude::*; | ||
6 | use chrono::{Local, NaiveDate}; | ||
7 | use serde::Serialize; | ||
8 | |||
9 | use cursive::direction::{Direction, Orientation}; | ||
10 | use cursive::event::{Event, EventResult, Key}; | ||
11 | use cursive::theme::Color::*; | ||
12 | use cursive::theme::PaletteColor::*; | ||
13 | use cursive::theme::{BaseColor, BorderStyle, Palette, Theme}; | ||
14 | use cursive::view::View; | ||
15 | use cursive::views::{Dialog, DummyView, LinearLayout, TextView}; | ||
16 | use cursive::{Cursive, Printer, Vec2}; | ||
17 | |||
18 | use std::fs::File; | ||
19 | use std::io::prelude::*; | ||
20 | |||
21 | use std::collections::HashMap; | ||
22 | |||
23 | #[derive(Debug)] | ||
24 | struct Habit<T> { | ||
25 | name: String, | ||
26 | stats: HashMap<NaiveDate, T>, | ||
27 | } | ||
28 | |||
29 | impl<T> Habit<T> | ||
30 | where | ||
31 | T: Copy, | ||
32 | { | ||
33 | fn new(name: &str) -> Habit<T> { | ||
34 | return Habit { | ||
35 | name: name.to_owned(), | ||
36 | stats: HashMap::new(), | ||
37 | }; | ||
38 | } | ||
39 | |||
40 | fn get_by_date(&self, date: NaiveDate) -> Option<&T> { | ||
41 | self.stats.get(&date) | ||
42 | } | ||
43 | |||
44 | fn insert_entry(&mut self, date: NaiveDate, val: T) { | ||
45 | *self.stats.entry(date).or_insert(val) = val; | ||
46 | } | ||
47 | } | ||
48 | |||
49 | impl Habit<bool> { | ||
50 | fn toggle(&mut self, date: NaiveDate) { | ||
51 | if let Some(v) = self.stats.get_mut(&date) { | ||
52 | *v ^= true | ||
53 | } else { | ||
54 | self.insert_entry(date, true); | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | impl Habit<u32> { | ||
60 | fn increment(&mut self, date: NaiveDate) { | ||
61 | if let Some(v) = self.stats.get_mut(&date) { | ||
62 | *v += 1 | ||
63 | } | ||
64 | } | ||
65 | fn decrement(&mut self, date: NaiveDate) { | ||
66 | if let Some(v) = self.stats.get_mut(&date) { | ||
67 | if *v >= 1 { | ||
68 | *v -= 1; | ||
69 | } else { | ||
70 | *v = 0; | ||
71 | }; | ||
72 | } | ||
73 | } | ||
74 | fn set(&mut self, date: NaiveDate, val: u32) { | ||
75 | *self.stats.entry(date).or_insert(val) = val; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | enum ViewMode { | ||
80 | Daily, | ||
81 | Monthly, | ||
82 | } | ||
83 | |||
84 | struct BitView { | ||
85 | habit: Habit<bool>, | ||
86 | true_chr: char, | ||
87 | false_chr: char, | ||
88 | future_chr: char, | ||
89 | |||
90 | view_width: u32, | ||
91 | view_height: u32, | ||
92 | // color config | ||
93 | } | ||
94 | |||
95 | impl BitView { | ||
96 | fn new(habit: Habit<bool>) -> Self { | ||
97 | return BitView { | ||
98 | habit, | ||
99 | true_chr: 'x', | ||
100 | false_chr: 'o', | ||
101 | future_chr: '.', | ||
102 | view_width: 21, | ||
103 | view_height: 9, | ||
104 | }; | ||
105 | } | ||
106 | fn get_title(&self) -> String { | ||
107 | return self.habit.name.to_owned(); | ||
108 | // return format!( | ||
109 | // "{:^width$.max$}", | ||
110 | // self.habit.name, | ||
111 | // width = self.view_width as usize, | ||
112 | // max = self.view_width as usize - 3 | ||
113 | // ); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | impl View for BitView { | ||
118 | fn draw(&self, printer: &Printer) { | ||
119 | let now = Local::now(); | ||
120 | let year = now.year(); | ||
121 | let month = now.month(); | ||
122 | |||
123 | for i in 1..=31 { | ||
124 | let day = NaiveDate::from_ymd_opt(year, month, i); | ||
125 | let d = day.unwrap(); | ||
126 | let day_stat = self.habit.get_by_date(d).unwrap_or(&false); | ||
127 | let coords = ((i % 7) * 3, i / 7 + 2); | ||
128 | |||
129 | if d < now.naive_utc().date() { | ||
130 | printer.print(coords, &format!("{:^3}", self.false_chr)) | ||
131 | } else if d > now.naive_utc().date() { | ||
132 | printer.print(coords, &format!("{:^3}", self.future_chr)) | ||
133 | } else { | ||
134 | printer.print(coords, &format!("{:^3}", self.future_chr)) | ||
135 | } | ||
136 | // if let Some(d) = day { | ||
137 | // let day_status = self.habit.get_by_date(d).unwrap_or(&false); | ||
138 | // let coords = ((i % 7) * 3, i / 7 + 2); | ||
139 | |||
140 | // if d <= now.naive_utc().date() { | ||
141 | // if *day_status { | ||
142 | // printer.print(coords, &format!("{:^3}", self.true_chr)) | ||
143 | // } else { | ||
144 | // printer.print(coords, &format!("{:^3}", self.false_chr)) | ||
145 | // } | ||
146 | // } else { | ||
147 | // printer.print(coords, &format!("{:^3}", self.future_chr)) | ||
148 | // } | ||
149 | // } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | fn required_size(&mut self, _: Vec2) -> Vec2 { | ||
154 | (20, 9).into() | ||
155 | } | ||
156 | |||
157 | fn take_focus(&mut self, _: Direction) -> bool { | ||
158 | true | ||
159 | } | ||
160 | |||
161 | fn on_event(&mut self, e: Event) -> EventResult { | ||
162 | match e { | ||
163 | Event::Key(Key::Enter) => { | ||
164 | self.habit.toggle(Local::now().naive_utc().date()); | ||
165 | return EventResult::Consumed(None); | ||
166 | } | ||
167 | _ => return EventResult::Ignored, | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | |||
172 | fn pallete_gen() -> Palette { | ||
173 | let mut p = Palette::default(); | ||
174 | p[Background] = Dark(BaseColor::Black); | ||
175 | p[Shadow] = Light(BaseColor::Black); | ||
176 | p[View] = Dark(BaseColor::Black); | ||
177 | p[Primary] = Dark(BaseColor::White); | ||
178 | p[Secondary] = Light(BaseColor::Black); | ||
179 | p[Tertiary] = Dark(BaseColor::Green); | ||
180 | p[TitlePrimary] = Light(BaseColor::White); | ||
181 | p[Highlight] = Dark(BaseColor::Red); | ||
182 | p[HighlightInactive] = Dark(BaseColor::Black); | ||
183 | |||
184 | return p; | ||
185 | } | ||
186 | |||
187 | fn theme_gen() -> Theme { | ||
188 | let mut t = Theme::default(); | ||
189 | t.shadow = false; | ||
190 | t.borders = BorderStyle::Simple; | ||
191 | t.palette = pallete_gen(); | ||
192 | return t; | ||
193 | } | ||
194 | |||
195 | fn main() { | ||
196 | let mut work_out: Habit<bool> = Habit::new("gymming"); | ||
197 | work_out.insert_entry(NaiveDate::from_ymd(2020, 2, 4), true); | ||
198 | work_out.insert_entry(NaiveDate::from_ymd(2020, 2, 2), true); | ||
199 | work_out.insert_entry(NaiveDate::from_ymd(2020, 2, 3), true); | ||
200 | work_out.insert_entry(NaiveDate::from_ymd(2020, 2, 1), true); | ||
201 | work_out.insert_entry(NaiveDate::from_ymd(2020, 2, 5), false); | ||
202 | work_out.insert_entry(NaiveDate::from_ymd(2020, 2, 8), false); | ||
203 | work_out.insert_entry(NaiveDate::from_ymd(2020, 2, 11), false); | ||
204 | |||
205 | let mut again: Habit<bool> = Habit::new("reading"); | ||
206 | again.insert_entry(NaiveDate::from_ymd(2020, 2, 4), true); | ||
207 | again.insert_entry(NaiveDate::from_ymd(2020, 2, 2), true); | ||
208 | |||
209 | let mut s = Cursive::default(); | ||
210 | |||
211 | let gym_view = BitView::new(work_out); | ||
212 | let gym_title = gym_view.get_title(); | ||
213 | |||
214 | let reading_view = BitView::new(again); | ||
215 | let reading_title = reading_view.get_title(); | ||
216 | |||
217 | s.add_global_callback('q', |a| a.quit()); | ||
218 | s.add_layer( | ||
219 | LinearLayout::horizontal() | ||
220 | .child(Dialog::around(gym_view).title(gym_title)) | ||
221 | .child(Dialog::around(reading_view).title(reading_title)), | ||
222 | ); | ||
223 | |||
224 | s.set_theme(theme_gen()); | ||
225 | s.run(); | ||
226 | } | ||