From fbcc966a3da8a75842c6b8843a9fd7f1edb0db15 Mon Sep 17 00:00:00 2001 From: Akshay Date: Wed, 24 Feb 2021 11:57:37 +0530 Subject: add GoalKinds - this allows for changing habit goals easily - easier to add new habits to dijo in newer versions --- src/app/impl_self.rs | 24 +++++++++++++++++------- src/command.rs | 51 +++++++++++++++++++++++++++++++++++++++++++-------- src/habit/bit.rs | 4 ++++ src/habit/count.rs | 4 ++++ src/habit/traits.rs | 20 ++++++++++++++++---- 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; 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 { + if let Some(n) = s.strip_prefix("<") { + return n + .parse::() + .map_err(|_| CommandLineError::InvalidGoal(s.into())) + .map(GoalKind::Addiction); + } else if s.contains(".") { + let value = s + .chars() + .filter(|x| x.is_digit(10)) + .collect::() + .parse::() + .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::() { + 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, bool), + Add(String, Option, 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::() - .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) { 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) { 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); + 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); -- cgit v1.2.3