aboutsummaryrefslogtreecommitdiff
path: root/src/app/impl_self.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/impl_self.rs')
-rw-r--r--src/app/impl_self.rs247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/app/impl_self.rs b/src/app/impl_self.rs
new file mode 100644
index 0000000..efed4e0
--- /dev/null
+++ b/src/app/impl_self.rs
@@ -0,0 +1,247 @@
1use std::default::Default;
2use std::f64;
3use std::fs::{File, OpenOptions};
4use std::io::prelude::*;
5use std::path::PathBuf;
6use std::sync::mpsc::channel;
7use std::time::Duration;
8
9use chrono::Local;
10use cursive::direction::Absolute;
11use cursive::Vec2;
12use notify::{watcher, RecursiveMode, Watcher};
13
14use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode};
15use crate::utils;
16use crate::Command;
17use crate::CONFIGURATION;
18
19use crate::app::{App, StatusLine};
20
21impl App {
22 pub fn new() -> Self {
23 let (tx, rx) = channel();
24 let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
25 watcher
26 .watch(utils::auto_habit_file(), RecursiveMode::Recursive)
27 .unwrap_or_else(|e| {
28 panic!("Unable to start file watcher: {}", e);
29 });
30 return App {
31 habits: vec![],
32 focus: 0,
33 _file_watcher: watcher,
34 file_event_recv: rx,
35 view_month_offset: 0,
36 };
37 }
38
39 pub fn add_habit(&mut self, h: Box<dyn HabitWrapper>) {
40 self.habits.push(h);
41 }
42
43 pub fn delete_by_name(&mut self, name: &str) {
44 self.habits.retain(|h| h.name() != name);
45 }
46
47 pub fn get_mode(&self) -> ViewMode {
48 if self.habits.is_empty() {
49 return ViewMode::Day;
50 }
51 return self.habits[self.focus].view_mode();
52 }
53
54 pub fn set_mode(&mut self, mode: ViewMode) {
55 if !self.habits.is_empty() {
56 self.habits[self.focus].set_view_mode(mode);
57 }
58 }
59
60 pub fn set_view_month_offset(&mut self, offset: u32) {
61 self.view_month_offset = offset;
62 for v in self.habits.iter_mut() {
63 v.set_view_month_offset(offset);
64 }
65 }
66
67 pub fn sift_backward(&mut self) {
68 self.view_month_offset += 1;
69 for v in self.habits.iter_mut() {
70 v.set_view_month_offset(self.view_month_offset);
71 }
72 }
73
74 pub fn sift_forward(&mut self) {
75 if self.view_month_offset > 0 {
76 self.view_month_offset -= 1;
77 for v in self.habits.iter_mut() {
78 v.set_view_month_offset(self.view_month_offset);
79 }
80 }
81 }
82
83 pub fn set_focus(&mut self, d: Absolute) {
84 let grid_width = CONFIGURATION.grid_width;
85 match d {
86 Absolute::Right => {
87 if self.focus != self.habits.len() - 1 {
88 self.focus += 1;
89 }
90 }
91 Absolute::Left => {
92 if self.focus != 0 {
93 self.focus -= 1;
94 }
95 }
96 Absolute::Down => {
97 if self.focus + grid_width < self.habits.len() - 1 {
98 self.focus += grid_width;
99 } else {
100 self.focus = self.habits.len() - 1;
101 }
102 }
103 Absolute::Up => {
104 if self.focus as isize - grid_width as isize >= 0 {
105 self.focus -= grid_width;
106 } else {
107 self.focus = 0;
108 }
109 }
110 Absolute::None => {}
111 }
112 }
113
114 pub fn status(&self) -> StatusLine {
115 let today = chrono::Local::now().naive_utc().date();
116 let remaining = self.habits.iter().map(|h| h.remaining(today)).sum::<u32>();
117 let total = self.habits.iter().map(|h| h.goal()).sum::<u32>();
118 let completed = total - remaining;
119
120 let timestamp = if self.view_month_offset == 0 {
121 format!("{}", Local::now().date().format("%d/%b/%y"),)
122 } else {
123 let months = self.view_month_offset;
124 format!("{}", format!("{} months ago", months),)
125 };
126
127 StatusLine {
128 0: format!(
129 "Today: {} completed, {} remaining --{}--",
130 completed,
131 remaining,
132 self.get_mode()
133 ),
134 1: timestamp,
135 }
136 }
137
138 pub fn max_size(&self) -> Vec2 {
139 let grid_width = CONFIGURATION.grid_width;
140 let width = {
141 if self.habits.len() > 0 {
142 grid_width * CONFIGURATION.view_width
143 } else {
144 0
145 }
146 };
147 let height = {
148 if self.habits.len() > 0 {
149 (CONFIGURATION.view_height as f64
150 * (self.habits.len() as f64 / grid_width as f64).ceil())
151 as usize
152 } else {
153 0
154 }
155 };
156 Vec2::new(width, height + 2)
157 }
158
159 pub fn load_state() -> Self {
160 let (regular_f, auto_f) = (utils::habit_file(), utils::auto_habit_file());
161 let read_from_file = |file: PathBuf| -> Vec<Box<dyn HabitWrapper>> {
162 if let Ok(ref mut f) = File::open(file) {
163 let mut j = String::new();
164 f.read_to_string(&mut j);
165 return serde_json::from_str(&j).unwrap();
166 } else {
167 return Vec::new();
168 }
169 };
170
171 let mut regular = read_from_file(regular_f);
172 let auto = read_from_file(auto_f);
173 regular.extend(auto);
174 return App {
175 habits: regular,
176 ..Default::default()
177 };
178 }
179
180 // this function does IO
181 // TODO: convert this into non-blocking async function
182 pub fn save_state(&self) {
183 let (regular, auto): (Vec<_>, Vec<_>) = self.habits.iter().partition(|&x| !x.is_auto());
184 let (regular_f, auto_f) = (utils::habit_file(), utils::auto_habit_file());
185
186 let write_to_file = |data: Vec<&Box<dyn HabitWrapper>>, file: PathBuf| {
187 let j = serde_json::to_string_pretty(&data).unwrap();
188 match OpenOptions::new()
189 .write(true)
190 .create(true)
191 .truncate(true)
192 .open(file)
193 {
194 Ok(ref mut f) => f.write_all(j.as_bytes()).unwrap(),
195 Err(_) => panic!("Unable to write!"),
196 };
197 };
198
199 write_to_file(regular, regular_f);
200 write_to_file(auto, auto_f);
201 }
202
203 pub fn parse_command(&mut self, c: Command) {
204 match c {
205 Command::Add(name, goal, auto) => {
206 let kind = if goal == Some(1) { "bit" } else { "count" };
207 if kind == "count" {
208 self.add_habit(Box::new(Count::new(
209 name,
210 goal.unwrap_or(0),
211 auto.unwrap_or(false),
212 )));
213 } else if kind == "bit" {
214 self.add_habit(Box::new(Bit::new(name, auto.unwrap_or(false))));
215 }
216 }
217 Command::Delete(name) => {
218 self.delete_by_name(&name);
219 self.focus = 0;
220 }
221 Command::TrackUp(name) => {
222 let target_habit = self
223 .habits
224 .iter_mut()
225 .find(|x| x.name() == name && x.is_auto());
226 if let Some(h) = target_habit {
227 h.modify(Local::now().naive_utc().date(), TrackEvent::Increment);
228 }
229 }
230 Command::TrackDown(name) => {
231 let target_habit = self
232 .habits
233 .iter_mut()
234 .find(|x| x.name() == name && x.is_auto());
235 if let Some(h) = target_habit {
236 h.modify(Local::now().naive_utc().date(), TrackEvent::Decrement);
237 }
238 }
239 Command::Quit => self.save_state(),
240 Command::MonthNext => self.sift_forward(),
241 Command::MonthPrev => self.sift_backward(),
242 _ => {
243 eprintln!("UNKNOWN COMMAND!");
244 }
245 }
246 }
247}