diff options
-rw-r--r-- | src/app/impl_self.rs | 24 | ||||
-rw-r--r-- | src/command.rs | 51 | ||||
-rw-r--r-- | src/habit/bit.rs | 4 | ||||
-rw-r--r-- | src/habit/count.rs | 4 | ||||
-rw-r--r-- | src/habit/traits.rs | 20 | ||||
-rw-r--r-- | src/views.rs | 13 |
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; | |||
11 | use cursive::Vec2; | 11 | use cursive::Vec2; |
12 | use notify::{watcher, RecursiveMode, Watcher}; | 12 | use notify::{watcher, RecursiveMode, Watcher}; |
13 | 13 | ||
14 | use crate::command::{Command, CommandLineError}; | 14 | use crate::command::{Command, CommandLineError, GoalKind}; |
15 | use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode}; | 15 | use crate::habit::{Bit, Count, Float, HabitWrapper, TrackEvent, ViewMode}; |
16 | use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH}; | 16 | use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH}; |
17 | 17 | ||
18 | use crate::app::{App, Cursor, Message, MessageKind, StatusLine}; | 18 | use 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 @@ | |||
1 | use std::fmt; | 1 | use std::fmt; |
2 | use std::str::FromStr; | ||
2 | 3 | ||
3 | use cursive::event::{Event, EventResult, Key}; | 4 | use cursive::event::{Event, EventResult, Key}; |
4 | use cursive::theme::{BaseColor, Color, ColorStyle}; | 5 | use 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)] | ||
110 | pub enum GoalKind { | ||
111 | Count(u32), | ||
112 | Bit, | ||
113 | Float(u32, u8), | ||
114 | Addiction(u32), | ||
115 | } | ||
116 | |||
117 | impl 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)] |
109 | pub enum Command { | 148 | pub 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 | ||
130 | impl std::error::Error for CommandLineError {} | 170 | impl 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; | |||
4 | use chrono::NaiveDate; | 4 | use chrono::NaiveDate; |
5 | use serde::{Deserialize, Serialize}; | 5 | use serde::{Deserialize, Serialize}; |
6 | 6 | ||
7 | use crate::command::GoalKind; | ||
7 | use crate::habit::prelude::default_auto; | 8 | use crate::habit::prelude::default_auto; |
8 | use crate::habit::traits::Habit; | 9 | use crate::habit::traits::Habit; |
9 | use crate::habit::{InnerData, TrackEvent}; | 10 | use 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; | |||
4 | use chrono::NaiveDate; | 4 | use chrono::NaiveDate; |
5 | use serde::{Deserialize, Serialize}; | 5 | use serde::{Deserialize, Serialize}; |
6 | 6 | ||
7 | use crate::command::GoalKind; | ||
7 | use crate::habit::prelude::default_auto; | 8 | use crate::habit::prelude::default_auto; |
8 | use crate::habit::traits::Habit; | 9 | use crate::habit::traits::Habit; |
9 | use crate::habit::{InnerData, TrackEvent}; | 10 | use 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; | |||
2 | use cursive::direction::Direction; | 2 | use cursive::direction::Direction; |
3 | use cursive::event::{Event, EventResult}; | 3 | use cursive::event::{Event, EventResult}; |
4 | use cursive::{Printer, Vec2}; | 4 | use cursive::{Printer, Vec2}; |
5 | |||
6 | use typetag; | 5 | use typetag; |
7 | 6 | ||
8 | use crate::habit::{Bit, Count, InnerData, TrackEvent}; | 7 | use crate::command::GoalKind; |
8 | use crate::habit::{Bit, Count, Float, InnerData, TrackEvent}; | ||
9 | use crate::views::ShadowView; | 9 | use crate::views::ShadowView; |
10 | 10 | ||
11 | pub trait Habit { | 11 | pub 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 { | |||
31 | pub trait HabitWrapper: erased_serde::Serialize { | 32 | pub 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 | ||
91 | auto_habit_impl!(Count); | 96 | macro_rules! generate_implementations { |
92 | auto_habit_impl!(Bit); | 97 | ($($x:ident),*) => ( |
98 | $( | ||
99 | auto_habit_impl!($x); | ||
100 | )* | ||
101 | ); | ||
102 | } | ||
103 | |||
104 | generate_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}; | |||
7 | use chrono::prelude::*; | 7 | use chrono::prelude::*; |
8 | use chrono::{Local, NaiveDate}; | 8 | use chrono::{Local, NaiveDate}; |
9 | 9 | ||
10 | use crate::habit::{Bit, Count, Habit, TrackEvent, ViewMode}; | 10 | use crate::habit::{Bit, Count, Float, Habit, TrackEvent, ViewMode}; |
11 | use crate::theme::cursor_bg; | 11 | use crate::theme::cursor_bg; |
12 | use crate::utils::VIEW_WIDTH; | 12 | use crate::utils::VIEW_WIDTH; |
13 | 13 | ||
@@ -193,5 +193,12 @@ macro_rules! auto_view_impl { | |||
193 | }; | 193 | }; |
194 | } | 194 | } |
195 | 195 | ||
196 | auto_view_impl!(Count); | 196 | macro_rules! generate_view_impls { |
197 | auto_view_impl!(Bit); | 197 | ($($x:ident),*) => ( |
198 | $( | ||
199 | auto_view_impl!($x); | ||
200 | )* | ||
201 | ); | ||
202 | } | ||
203 | |||
204 | generate_view_impls!(Count, Bit, Float); | ||