aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/app/impl_self.rs24
-rw-r--r--src/command.rs51
-rw-r--r--src/habit/bit.rs4
-rw-r--r--src/habit/count.rs4
-rw-r--r--src/habit/traits.rs20
-rw-r--r--src/views.rs13
6 files changed, 94 insertions, 22 deletions
diff --git a/src/app/impl_self.rs b/src/app/impl_self.rs
index d5f93ff..fad965a 100644
--- a/src/app/impl_self.rs
+++ b/src/app/impl_self.rs
@@ -11,8 +11,8 @@ use cursive::direction::Absolute;
11use cursive::Vec2; 11use cursive::Vec2;
12use notify::{watcher, RecursiveMode, Watcher}; 12use notify::{watcher, RecursiveMode, Watcher};
13 13
14use crate::command::{Command, CommandLineError}; 14use crate::command::{Command, CommandLineError, GoalKind};
15use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode}; 15use crate::habit::{Bit, Count, Float, HabitWrapper, TrackEvent, ViewMode};
16use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH}; 16use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH};
17 17
18use crate::app::{App, Cursor, Message, MessageKind, StatusLine}; 18use crate::app::{App, Cursor, Message, MessageKind, StatusLine};
@@ -227,11 +227,21 @@ impl App {
227 .set_message(format!("Habit `{}` already exist", &name)); 227 .set_message(format!("Habit `{}` already exist", &name));
228 return; 228 return;
229 } 229 }
230 let kind = if goal == Some(1) { "bit" } else { "count" }; 230 match goal {
231 if kind == "count" { 231 Some(GoalKind::Bit) => {
232 self.add_habit(Box::new(Count::new(name, goal.unwrap_or(0), auto))); 232 self.add_habit(Box::new(Bit::new(name, auto)));
233 } else if kind == "bit" { 233 }
234 self.add_habit(Box::new(Bit::new(name, auto))); 234 Some(GoalKind::Count(v)) => {
235 self.add_habit(Box::new(Count::new(name, v, auto)));
236 }
237 Some(GoalKind::Float(v, p)) => {
238 self.message.set_kind(MessageKind::Error);
239 self.message.set_message(format!("Added floating habit"));
240 self.add_habit(Box::new(Float::new(name, v, p, auto)));
241 }
242 _ => {
243 self.add_habit(Box::new(Count::new(name, 0, auto)));
244 }
235 } 245 }
236 } 246 }
237 Command::Delete(name) => { 247 Command::Delete(name) => {
diff --git a/src/command.rs b/src/command.rs
index 30aabe2..bfd09d8 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -1,4 +1,5 @@
1use std::fmt; 1use std::fmt;
2use std::str::FromStr;
2 3
3use cursive::event::{Event, EventResult, Key}; 4use cursive::event::{Event, EventResult, Key};
4use cursive::theme::{BaseColor, Color, ColorStyle}; 5use cursive::theme::{BaseColor, Color, ColorStyle};
@@ -105,9 +106,47 @@ fn call_on_app(s: &mut Cursive, input: &str) {
105 } 106 }
106} 107}
107 108
109#[derive(Debug, PartialEq)]
110pub enum GoalKind {
111 Count(u32),
112 Bit,
113 Float(u32, u8),
114 Addiction(u32),
115}
116
117impl FromStr for GoalKind {
118 type Err = CommandLineError;
119
120 fn from_str(s: &str) -> Result<Self> {
121 if let Some(n) = s.strip_prefix("<") {
122 return n
123 .parse::<u32>()
124 .map_err(|_| CommandLineError::InvalidGoal(s.into()))
125 .map(GoalKind::Addiction);
126 } else if s.contains(".") {
127 let value = s
128 .chars()
129 .filter(|x| x.is_digit(10))
130 .collect::<String>()
131 .parse::<u32>()
132 .map_err(|_| CommandLineError::InvalidCommand(s.into()))?;
133 let precision = s.chars().skip_while(|&x| x != '.').count() - 1;
134 return Ok(GoalKind::Float(value, precision as u8));
135 }
136 if let Ok(v) = s.parse::<u32>() {
137 if v == 1 {
138 return Ok(GoalKind::Bit);
139 } else {
140 return Ok(GoalKind::Count(v));
141 }
142 }
143 return Err(CommandLineError::InvalidCommand(s.into()));
144 }
145}
146
108#[derive(PartialEq)] 147#[derive(PartialEq)]
109pub enum Command { 148pub enum Command {
110 Add(String, Option<u32>, bool), 149 Add(String, Option<GoalKind>, bool),
111 MonthPrev, 150 MonthPrev,
112 MonthNext, 151 MonthNext,
113 Delete(String), 152 Delete(String),
@@ -125,6 +164,7 @@ pub enum CommandLineError {
125 InvalidCommand(String), // command name 164 InvalidCommand(String), // command name
126 InvalidArg(u32), // position 165 InvalidArg(u32), // position
127 NotEnoughArgs(String, u32), // command name, required no. of args 166 NotEnoughArgs(String, u32), // command name, required no. of args
167 InvalidGoal(String), // goal expression
128} 168}
129 169
130impl std::error::Error for CommandLineError {} 170impl std::error::Error for CommandLineError {}
@@ -137,6 +177,7 @@ impl fmt::Display for CommandLineError {
137 CommandLineError::NotEnoughArgs(s, n) => { 177 CommandLineError::NotEnoughArgs(s, n) => {
138 write!(f, "Command `{}` requires atleast {} argument(s)!", s, n) 178 write!(f, "Command `{}` requires atleast {} argument(s)!", s, n)
139 } 179 }
180 CommandLineError::InvalidGoal(s) => write!(f, "Invalid goal expression: `{}`", s),
140 } 181 }
141 } 182 }
142} 183}
@@ -156,13 +197,7 @@ impl Command {
156 if args.is_empty() { 197 if args.is_empty() {
157 return Err(CommandLineError::NotEnoughArgs(first, 1)); 198 return Err(CommandLineError::NotEnoughArgs(first, 1));
158 } 199 }
159 let goal = args 200 let goal = args.get(1).map(|x| GoalKind::from_str(x)).transpose()?;
160 .get(1)
161 .map(|x| {
162 x.parse::<u32>()
163 .map_err(|_| CommandLineError::InvalidArg(2))
164 })
165 .transpose()?;
166 return Ok(Command::Add( 201 return Ok(Command::Add(
167 args.get_mut(0).unwrap().to_string(), 202 args.get_mut(0).unwrap().to_string(),
168 goal, 203 goal,
diff --git a/src/habit/bit.rs b/src/habit/bit.rs
index da64ece..c0d5f70 100644
--- a/src/habit/bit.rs
+++ b/src/habit/bit.rs
@@ -4,6 +4,7 @@ use std::default::Default;
4use chrono::NaiveDate; 4use chrono::NaiveDate;
5use serde::{Deserialize, Serialize}; 5use serde::{Deserialize, Serialize};
6 6
7use crate::command::GoalKind;
7use crate::habit::prelude::default_auto; 8use crate::habit::prelude::default_auto;
8use crate::habit::traits::Habit; 9use crate::habit::traits::Habit;
9use crate::habit::{InnerData, TrackEvent}; 10use crate::habit::{InnerData, TrackEvent};
@@ -66,6 +67,9 @@ impl Habit for Bit {
66 fn set_name(&mut self, n: impl AsRef<str>) { 67 fn set_name(&mut self, n: impl AsRef<str>) {
67 self.name = n.as_ref().to_owned(); 68 self.name = n.as_ref().to_owned();
68 } 69 }
70 fn kind(&self) -> GoalKind {
71 GoalKind::Bit
72 }
69 fn set_goal(&mut self, g: Self::HabitType) { 73 fn set_goal(&mut self, g: Self::HabitType) {
70 self.goal = g; 74 self.goal = g;
71 } 75 }
diff --git a/src/habit/count.rs b/src/habit/count.rs
index 09fd399..75b51cc 100644
--- a/src/habit/count.rs
+++ b/src/habit/count.rs
@@ -4,6 +4,7 @@ use std::default::Default;
4use chrono::NaiveDate; 4use chrono::NaiveDate;
5use serde::{Deserialize, Serialize}; 5use serde::{Deserialize, Serialize};
6 6
7use crate::command::GoalKind;
7use crate::habit::prelude::default_auto; 8use crate::habit::prelude::default_auto;
8use crate::habit::traits::Habit; 9use crate::habit::traits::Habit;
9use crate::habit::{InnerData, TrackEvent}; 10use crate::habit::{InnerData, TrackEvent};
@@ -42,6 +43,9 @@ impl Habit for Count {
42 fn set_name(&mut self, n: impl AsRef<str>) { 43 fn set_name(&mut self, n: impl AsRef<str>) {
43 self.name = n.as_ref().to_owned(); 44 self.name = n.as_ref().to_owned();
44 } 45 }
46 fn kind(&self) -> GoalKind {
47 GoalKind::Count(self.goal)
48 }
45 fn set_goal(&mut self, g: Self::HabitType) { 49 fn set_goal(&mut self, g: Self::HabitType) {
46 self.goal = g; 50 self.goal = g;
47 } 51 }
diff --git a/src/habit/traits.rs b/src/habit/traits.rs
index 24d941d..54ec9e1 100644
--- a/src/habit/traits.rs
+++ b/src/habit/traits.rs
@@ -2,10 +2,10 @@ use chrono::NaiveDate;
2use cursive::direction::Direction; 2use cursive::direction::Direction;
3use cursive::event::{Event, EventResult}; 3use cursive::event::{Event, EventResult};
4use cursive::{Printer, Vec2}; 4use cursive::{Printer, Vec2};
5
6use typetag; 5use typetag;
7 6
8use crate::habit::{Bit, Count, InnerData, TrackEvent}; 7use crate::command::GoalKind;
8use crate::habit::{Bit, Count, Float, InnerData, TrackEvent};
9use crate::views::ShadowView; 9use crate::views::ShadowView;
10 10
11pub trait Habit { 11pub trait Habit {
@@ -20,6 +20,7 @@ pub trait Habit {
20 fn remaining(&self, date: NaiveDate) -> u32; 20 fn remaining(&self, date: NaiveDate) -> u32;
21 fn set_goal(&mut self, goal: Self::HabitType); 21 fn set_goal(&mut self, goal: Self::HabitType);
22 fn set_name(&mut self, name: impl AsRef<str>); 22 fn set_name(&mut self, name: impl AsRef<str>);
23 fn kind(&self) -> GoalKind;
23 24
24 fn inner_data_ref(&self) -> &InnerData; 25 fn inner_data_ref(&self) -> &InnerData;
25 fn inner_data_mut_ref(&mut self) -> &mut InnerData; 26 fn inner_data_mut_ref(&mut self) -> &mut InnerData;
@@ -31,6 +32,7 @@ pub trait Habit {
31pub trait HabitWrapper: erased_serde::Serialize { 32pub trait HabitWrapper: erased_serde::Serialize {
32 fn draw(&self, printer: &Printer); 33 fn draw(&self, printer: &Printer);
33 fn goal(&self) -> u32; 34 fn goal(&self) -> u32;
35 fn kind(&self) -> GoalKind;
34 fn modify(&mut self, date: NaiveDate, event: TrackEvent); 36 fn modify(&mut self, date: NaiveDate, event: TrackEvent);
35 fn name(&self) -> String; 37 fn name(&self) -> String;
36 fn on_event(&mut self, event: Event) -> EventResult; 38 fn on_event(&mut self, event: Event) -> EventResult;
@@ -69,6 +71,9 @@ macro_rules! auto_habit_impl {
69 fn goal(&self) -> u32 { 71 fn goal(&self) -> u32 {
70 Habit::goal(self) 72 Habit::goal(self)
71 } 73 }
74 fn kind(&self) -> GoalKind {
75 Habit::kind(self)
76 }
72 fn modify(&mut self, date: NaiveDate, event: TrackEvent) { 77 fn modify(&mut self, date: NaiveDate, event: TrackEvent) {
73 Habit::modify(self, date, event); 78 Habit::modify(self, date, event);
74 } 79 }
@@ -88,5 +93,12 @@ macro_rules! auto_habit_impl {
88 }; 93 };
89} 94}
90 95
91auto_habit_impl!(Count); 96macro_rules! generate_implementations {
92auto_habit_impl!(Bit); 97 ($($x:ident),*) => (
98 $(
99 auto_habit_impl!($x);
100 )*
101 );
102}
103
104generate_implementations!(Count, Bit, Float);
diff --git a/src/views.rs b/src/views.rs
index b90ce2b..ca4f757 100644
--- a/src/views.rs
+++ b/src/views.rs
@@ -7,7 +7,7 @@ use cursive::{Printer, Vec2};
7use chrono::prelude::*; 7use chrono::prelude::*;
8use chrono::{Local, NaiveDate}; 8use chrono::{Local, NaiveDate};
9 9
10use crate::habit::{Bit, Count, Habit, TrackEvent, ViewMode}; 10use crate::habit::{Bit, Count, Float, Habit, TrackEvent, ViewMode};
11use crate::theme::cursor_bg; 11use crate::theme::cursor_bg;
12use crate::utils::VIEW_WIDTH; 12use crate::utils::VIEW_WIDTH;
13 13
@@ -193,5 +193,12 @@ macro_rules! auto_view_impl {
193 }; 193 };
194} 194}
195 195
196auto_view_impl!(Count); 196macro_rules! generate_view_impls {
197auto_view_impl!(Bit); 197 ($($x:ident),*) => (
198 $(
199 auto_view_impl!($x);
200 )*
201 );
202}
203
204generate_view_impls!(Count, Bit, Float);