summaryrefslogtreecommitdiffstats
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;
use cursive::Vec2;
use notify::{watcher, RecursiveMode, Watcher};
-use crate::command::{Command, CommandLineError};
-use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode};
+use crate::command::{Command, CommandLineError, GoalKind};
+use crate::habit::{Bit, Count, Float, HabitWrapper, TrackEvent, ViewMode};
use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH};
use crate::app::{App, Cursor, Message, MessageKind, StatusLine};
@@ -227,11 +227,21 @@ impl App {
.set_message(format!("Habit `{}` already exist", &name));
return;
}
- let kind = if goal == Some(1) { "bit" } else { "count" };
- if kind == "count" {
- self.add_habit(Box::new(Count::new(name, goal.unwrap_or(0), auto)));
- } else if kind == "bit" {
- self.add_habit(Box::new(Bit::new(name, auto)));
+ match goal {
+ Some(GoalKind::Bit) => {
+ self.add_habit(Box::new(Bit::new(name, auto)));
+ }
+ Some(GoalKind::Count(v)) => {
+ self.add_habit(Box::new(Count::new(name, v, auto)));
+ }
+ Some(GoalKind::Float(v, p)) => {
+ self.message.set_kind(MessageKind::Error);
+ self.message.set_message(format!("Added floating habit"));
+ self.add_habit(Box::new(Float::new(name, v, p, auto)));
+ }
+ _ => {
+ self.add_habit(Box::new(Count::new(name, 0, auto)));
+ }
}
}
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 @@
use std::fmt;
+use std::str::FromStr;
use cursive::event::{Event, EventResult, Key};
use cursive::theme::{BaseColor, Color, ColorStyle};
@@ -105,9 +106,47 @@ fn call_on_app(s: &mut Cursive, input: &str) {
}
}
+#[derive(Debug, PartialEq)]
+pub enum GoalKind {
+ Count(u32),
+ Bit,
+ Float(u32, u8),
+ Addiction(u32),
+}
+
+impl FromStr for GoalKind {
+ type Err = CommandLineError;
+
+ fn from_str(s: &str) -> Result<Self> {
+ if let Some(n) = s.strip_prefix("<") {
+ return n
+ .parse::<u32>()
+ .map_err(|_| CommandLineError::InvalidGoal(s.into()))
+ .map(GoalKind::Addiction);
+ } else if s.contains(".") {
+ let value = s
+ .chars()
+ .filter(|x| x.is_digit(10))
+ .collect::<String>()
+ .parse::<u32>()
+ .map_err(|_| CommandLineError::InvalidCommand(s.into()))?;
+ let precision = s.chars().skip_while(|&x| x != '.').count() - 1;
+ return Ok(GoalKind::Float(value, precision as u8));
+ }
+ if let Ok(v) = s.parse::<u32>() {
+ if v == 1 {
+ return Ok(GoalKind::Bit);
+ } else {
+ return Ok(GoalKind::Count(v));
+ }
+ }
+ return Err(CommandLineError::InvalidCommand(s.into()));
+ }
+}
+
#[derive(PartialEq)]
pub enum Command {
- Add(String, Option<u32>, bool),
+ Add(String, Option<GoalKind>, bool),
MonthPrev,
MonthNext,
Delete(String),
@@ -125,6 +164,7 @@ pub enum CommandLineError {
InvalidCommand(String), // command name
InvalidArg(u32), // position
NotEnoughArgs(String, u32), // command name, required no. of args
+ InvalidGoal(String), // goal expression
}
impl std::error::Error for CommandLineError {}
@@ -137,6 +177,7 @@ impl fmt::Display for CommandLineError {
CommandLineError::NotEnoughArgs(s, n) => {
write!(f, "Command `{}` requires atleast {} argument(s)!", s, n)
}
+ CommandLineError::InvalidGoal(s) => write!(f, "Invalid goal expression: `{}`", s),
}
}
}
@@ -156,13 +197,7 @@ impl Command {
if args.is_empty() {
return Err(CommandLineError::NotEnoughArgs(first, 1));
}
- let goal = args
- .get(1)
- .map(|x| {
- x.parse::<u32>()
- .map_err(|_| CommandLineError::InvalidArg(2))
- })
- .transpose()?;
+ let goal = args.get(1).map(|x| GoalKind::from_str(x)).transpose()?;
return Ok(Command::Add(
args.get_mut(0).unwrap().to_string(),
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;
use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
+use crate::command::GoalKind;
use crate::habit::prelude::default_auto;
use crate::habit::traits::Habit;
use crate::habit::{InnerData, TrackEvent};
@@ -66,6 +67,9 @@ impl Habit for Bit {
fn set_name(&mut self, n: impl AsRef<str>) {
self.name = n.as_ref().to_owned();
}
+ fn kind(&self) -> GoalKind {
+ GoalKind::Bit
+ }
fn set_goal(&mut self, g: Self::HabitType) {
self.goal = g;
}
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;
use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
+use crate::command::GoalKind;
use crate::habit::prelude::default_auto;
use crate::habit::traits::Habit;
use crate::habit::{InnerData, TrackEvent};
@@ -42,6 +43,9 @@ impl Habit for Count {
fn set_name(&mut self, n: impl AsRef<str>) {
self.name = n.as_ref().to_owned();
}
+ fn kind(&self) -> GoalKind {
+ GoalKind::Count(self.goal)
+ }
fn set_goal(&mut self, g: Self::HabitType) {
self.goal = g;
}
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;
use cursive::direction::Direction;
use cursive::event::{Event, EventResult};
use cursive::{Printer, Vec2};
-
use typetag;
-use crate::habit::{Bit, Count, InnerData, TrackEvent};
+use crate::command::GoalKind;
+use crate::habit::{Bit, Count, Float, InnerData, TrackEvent};
use crate::views::ShadowView;
pub trait Habit {
@@ -20,6 +20,7 @@ pub trait Habit {
fn remaining(&self, date: NaiveDate) -> u32;
fn set_goal(&mut self, goal: Self::HabitType);
fn set_name(&mut self, name: impl AsRef<str>);
+ fn kind(&self) -> GoalKind;
fn inner_data_ref(&self) -> &InnerData;
fn inner_data_mut_ref(&mut self) -> &mut InnerData;
@@ -31,6 +32,7 @@ pub trait Habit {
pub trait HabitWrapper: erased_serde::Serialize {
fn draw(&self, printer: &Printer);
fn goal(&self) -> u32;
+ fn kind(&self) -> GoalKind;
fn modify(&mut self, date: NaiveDate, event: TrackEvent);
fn name(&self) -> String;
fn on_event(&mut self, event: Event) -> EventResult;
@@ -69,6 +71,9 @@ macro_rules! auto_habit_impl {
fn goal(&self) -> u32 {
Habit::goal(self)
}
+ fn kind(&self) -> GoalKind {
+ Habit::kind(self)
+ }
fn modify(&mut self, date: NaiveDate, event: TrackEvent) {
Habit::modify(self, date, event);
}
@@ -88,5 +93,12 @@ macro_rules! auto_habit_impl {
};
}
-auto_habit_impl!(Count);
-auto_habit_impl!(Bit);
+macro_rules! generate_implementations {
+ ($($x:ident),*) => (
+ $(
+ auto_habit_impl!($x);
+ )*
+ );
+}
+
+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};
use chrono::prelude::*;
use chrono::{Local, NaiveDate};
-use crate::habit::{Bit, Count, Habit, TrackEvent, ViewMode};
+use crate::habit::{Bit, Count, Float, Habit, TrackEvent, ViewMode};
use crate::theme::cursor_bg;
use crate::utils::VIEW_WIDTH;
@@ -193,5 +193,12 @@ macro_rules! auto_view_impl {
};
}
-auto_view_impl!(Count);
-auto_view_impl!(Bit);
+macro_rules! generate_view_impls {
+ ($($x:ident),*) => (
+ $(
+ auto_view_impl!($x);
+ )*
+ );
+}
+
+generate_view_impls!(Count, Bit, Float);