aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml1
-rw-r--r--src/app.rs10
-rw-r--r--src/command.rs4
-rw-r--r--src/habit/bit.rs10
-rw-r--r--src/habit/count.rs10
-rw-r--r--src/habit/prelude.rs4
-rw-r--r--src/habit/traits.rs28
-rw-r--r--src/views.rs8
8 files changed, 56 insertions, 19 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..ddf2552
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
liberapay: nerdypepper
diff --git a/src/app.rs b/src/app.rs
index 412cfe5..93e5def 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -190,11 +190,15 @@ impl App {
190 pub fn parse_command(&mut self, input: &str) { 190 pub fn parse_command(&mut self, input: &str) {
191 let c = Command::from_string(input); 191 let c = Command::from_string(input);
192 match c { 192 match c {
193 Command::Add(name, kind, goal) => { 193 Command::Add(name, kind, goal, auto) => {
194 if kind == "count" { 194 if kind == "count" {
195 self.add_habit(Box::new(Count::new(name, goal.unwrap_or(0)))); 195 self.add_habit(Box::new(Count::new(
196 name,
197 goal.unwrap_or(0),
198 auto.unwrap_or(false),
199 )));
196 } else if kind == "bit" { 200 } else if kind == "bit" {
197 self.add_habit(Box::new(Bit::new(name))); 201 self.add_habit(Box::new(Bit::new(name, auto.unwrap_or(false))));
198 } 202 }
199 } 203 }
200 Command::Delete(name) => { 204 Command::Delete(name) => {
diff --git a/src/command.rs b/src/command.rs
index afc00ba..c1a855e 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -27,7 +27,7 @@ fn call_on_app(s: &mut Cursive, input: &str) {
27 27
28#[derive(PartialEq)] 28#[derive(PartialEq)]
29pub enum Command { 29pub enum Command {
30 Add(String, String, Option<u32>), // habit name, habit type, optional goal 30 Add(String, String, Option<u32>, Option<bool>), // habit name, habit type, optional goal, auto tracked
31 MonthPrev, 31 MonthPrev,
32 MonthNext, 32 MonthNext,
33 Delete(String), 33 Delete(String),
@@ -50,10 +50,12 @@ impl Command {
50 return Command::Blank; 50 return Command::Blank;
51 } 51 }
52 let goal = args.get(2).map(|g| g.parse::<u32>().ok()).flatten(); 52 let goal = args.get(2).map(|g| g.parse::<u32>().ok()).flatten();
53 let auto = args.get(3).map(|g| g.parse::<bool>().ok()).flatten();
53 return Command::Add( 54 return Command::Add(
54 args.get_mut(0).unwrap().to_string(), 55 args.get_mut(0).unwrap().to_string(),
55 args.get_mut(1).unwrap().to_string(), 56 args.get_mut(1).unwrap().to_string(),
56 goal, 57 goal,
58 auto,
57 ); 59 );
58 } 60 }
59 "delete" | "d" => { 61 "delete" | "d" => {
diff --git a/src/habit/bit.rs b/src/habit/bit.rs
index 292b96a..3386182 100644
--- a/src/habit/bit.rs
+++ b/src/habit/bit.rs
@@ -3,6 +3,7 @@ use std::collections::HashMap;
3use chrono::NaiveDate; 3use chrono::NaiveDate;
4use serde::{Deserialize, Serialize}; 4use serde::{Deserialize, Serialize};
5 5
6use crate::habit::prelude::default_auto;
6use crate::habit::traits::Habit; 7use crate::habit::traits::Habit;
7use crate::habit::{TrackEvent, ViewMode}; 8use crate::habit::{TrackEvent, ViewMode};
8use crate::CONFIGURATION; 9use crate::CONFIGURATION;
@@ -37,6 +38,9 @@ pub struct Bit {
37 stats: HashMap<NaiveDate, CustomBool>, 38 stats: HashMap<NaiveDate, CustomBool>,
38 goal: CustomBool, 39 goal: CustomBool,
39 40
41 #[serde(default = "default_auto")]
42 auto: bool,
43
40 #[serde(skip)] 44 #[serde(skip)]
41 view_month_offset: u32, 45 view_month_offset: u32,
42 46
@@ -45,11 +49,12 @@ pub struct Bit {
45} 49}
46 50
47impl Bit { 51impl Bit {
48 pub fn new(name: impl AsRef<str>) -> Self { 52 pub fn new(name: impl AsRef<str>, auto: bool) -> Self {
49 return Bit { 53 return Bit {
50 name: name.as_ref().to_owned(), 54 name: name.as_ref().to_owned(),
51 stats: HashMap::new(), 55 stats: HashMap::new(),
52 goal: CustomBool(true), 56 goal: CustomBool(true),
57 auto,
53 view_month_offset: 0, 58 view_month_offset: 0,
54 view_mode: ViewMode::Day, 59 view_mode: ViewMode::Day,
55 }; 60 };
@@ -114,4 +119,7 @@ impl Habit for Bit {
114 fn view_mode(&self) -> ViewMode { 119 fn view_mode(&self) -> ViewMode {
115 self.view_mode 120 self.view_mode
116 } 121 }
122 fn is_auto(&self) -> bool {
123 self.auto
124 }
117} 125}
diff --git a/src/habit/count.rs b/src/habit/count.rs
index a0e0aee..1bdf920 100644
--- a/src/habit/count.rs
+++ b/src/habit/count.rs
@@ -3,6 +3,7 @@ use std::collections::HashMap;
3use chrono::NaiveDate; 3use chrono::NaiveDate;
4use serde::{Deserialize, Serialize}; 4use serde::{Deserialize, Serialize};
5 5
6use crate::habit::prelude::default_auto;
6use crate::habit::traits::Habit; 7use crate::habit::traits::Habit;
7use crate::habit::{TrackEvent, ViewMode}; 8use crate::habit::{TrackEvent, ViewMode};
8 9
@@ -12,6 +13,9 @@ pub struct Count {
12 stats: HashMap<NaiveDate, u32>, 13 stats: HashMap<NaiveDate, u32>,
13 goal: u32, 14 goal: u32,
14 15
16 #[serde(default = "default_auto")]
17 auto: bool,
18
15 #[serde(skip)] 19 #[serde(skip)]
16 view_month_offset: u32, 20 view_month_offset: u32,
17 21
@@ -20,11 +24,12 @@ pub struct Count {
20} 24}
21 25
22impl Count { 26impl Count {
23 pub fn new(name: impl AsRef<str>, goal: u32) -> Self { 27 pub fn new(name: impl AsRef<str>, goal: u32, auto: bool) -> Self {
24 return Count { 28 return Count {
25 name: name.as_ref().to_owned(), 29 name: name.as_ref().to_owned(),
26 stats: HashMap::new(), 30 stats: HashMap::new(),
27 goal, 31 goal,
32 auto,
28 view_month_offset: 0, 33 view_month_offset: 0,
29 view_mode: ViewMode::Day, 34 view_mode: ViewMode::Day,
30 }; 35 };
@@ -99,4 +104,7 @@ impl Habit for Count {
99 fn view_mode(&self) -> ViewMode { 104 fn view_mode(&self) -> ViewMode {
100 self.view_mode 105 self.view_mode
101 } 106 }
107 fn is_auto(&self) -> bool {
108 self.auto
109 }
102} 110}
diff --git a/src/habit/prelude.rs b/src/habit/prelude.rs
index 9196f00..b8b2bb2 100644
--- a/src/habit/prelude.rs
+++ b/src/habit/prelude.rs
@@ -18,3 +18,7 @@ impl std::default::Default for ViewMode {
18 ViewMode::Day 18 ViewMode::Day
19 } 19 }
20} 20}
21
22pub fn default_auto() -> bool {
23 false
24}
diff --git a/src/habit/traits.rs b/src/habit/traits.rs
index e28e55d..5092bc5 100644
--- a/src/habit/traits.rs
+++ b/src/habit/traits.rs
@@ -26,6 +26,8 @@ pub trait Habit {
26 26
27 fn set_view_mode(&mut self, mode: ViewMode); 27 fn set_view_mode(&mut self, mode: ViewMode);
28 fn view_mode(&self) -> ViewMode; 28 fn view_mode(&self) -> ViewMode;
29
30 fn is_auto(&self) -> bool;
29} 31}
30 32
31#[typetag::serde(tag = "type")] 33#[typetag::serde(tag = "type")]
@@ -44,21 +46,15 @@ pub trait HabitWrapper: erased_serde::Serialize {
44 46
45 fn set_view_mode(&mut self, mode: ViewMode); 47 fn set_view_mode(&mut self, mode: ViewMode);
46 fn view_mode(&self) -> ViewMode; 48 fn view_mode(&self) -> ViewMode;
49
50 fn is_auto(&self) -> bool;
47} 51}
48 52
49macro_rules! auto_habit_impl { 53macro_rules! auto_habit_impl {
50 ($struct_name:ident) => { 54 ($struct_name:ident) => {
51 #[typetag::serde] 55 #[typetag::serde]
52 impl HabitWrapper for $struct_name { 56 impl HabitWrapper for $struct_name {
53 fn remaining(&self, date: NaiveDate) -> u32 { 57 // ShadowView
54 Habit::remaining(self, date)
55 }
56 fn goal(&self) -> u32 {
57 Habit::goal(self)
58 }
59 fn modify(&mut self, date: NaiveDate, event: TrackEvent) {
60 Habit::modify(self, date, event);
61 }
62 fn draw(&self, printer: &Printer) { 58 fn draw(&self, printer: &Printer) {
63 ShadowView::draw(self, printer) 59 ShadowView::draw(self, printer)
64 } 60 }
@@ -71,6 +67,17 @@ macro_rules! auto_habit_impl {
71 fn take_focus(&mut self, d: Direction) -> bool { 67 fn take_focus(&mut self, d: Direction) -> bool {
72 ShadowView::take_focus(self, d) 68 ShadowView::take_focus(self, d)
73 } 69 }
70
71 // Habit
72 fn remaining(&self, date: NaiveDate) -> u32 {
73 Habit::remaining(self, date)
74 }
75 fn goal(&self) -> u32 {
76 Habit::goal(self)
77 }
78 fn modify(&mut self, date: NaiveDate, event: TrackEvent) {
79 Habit::modify(self, date, event);
80 }
74 fn get_name(&self) -> String { 81 fn get_name(&self) -> String {
75 Habit::name(self) 82 Habit::name(self)
76 } 83 }
@@ -86,6 +93,9 @@ macro_rules! auto_habit_impl {
86 fn view_mode(&self) -> ViewMode { 93 fn view_mode(&self) -> ViewMode {
87 Habit::view_mode(self) 94 Habit::view_mode(self)
88 } 95 }
96 fn is_auto(&self) -> bool {
97 Habit::is_auto(self)
98 }
89 } 99 }
90 }; 100 };
91} 101}
diff --git a/src/views.rs b/src/views.rs
index 9e4a844..54d5431 100644
--- a/src/views.rs
+++ b/src/views.rs
@@ -63,7 +63,7 @@ where
63 }, 63 },
64 ); 64 );
65 65
66 let draw_month = |printer: &Printer| { 66 let draw_week = |printer: &Printer| {
67 let days = (1..31) 67 let days = (1..31)
68 .map(|i| NaiveDate::from_ymd_opt(year, month, i)) 68 .map(|i| NaiveDate::from_ymd_opt(year, month, i))
69 .flatten() // dates 28-31 may not exist, ignore them if they don't 69 .flatten() // dates 28-31 may not exist, ignore them if they don't
@@ -77,10 +77,10 @@ where
77 let bars_to_fill = (completions * full as u32) / weekly_goal; 77 let bars_to_fill = (completions * full as u32) / weekly_goal;
78 let percentage = (completions as f64 * 100.) / weekly_goal as f64; 78 let percentage = (completions as f64 * 100.) / weekly_goal as f64;
79 printer.with_style(future_style, |p| { 79 printer.with_style(future_style, |p| {
80 p.print((4, line_nr), &"�".repeat(full)); 80 p.print((4, line_nr), &"��".repeat(full));
81 }); 81 });
82 printer.with_style(goal_reached_style, |p| { 82 printer.with_style(goal_reached_style, |p| {
83 p.print((4, line_nr), &"�".repeat(bars_to_fill as usize)); 83 p.print((4, line_nr), &"��".repeat(bars_to_fill as usize));
84 }); 84 });
85 printer.with_style( 85 printer.with_style(
86 if is_this_week { 86 if is_this_week {
@@ -120,7 +120,7 @@ where
120 120
121 match self.view_mode() { 121 match self.view_mode() {
122 ViewMode::Day => draw_day(printer), 122 ViewMode::Day => draw_day(printer),
123 ViewMode::Week => draw_month(printer), 123 ViewMode::Week => draw_week(printer),
124 _ => draw_day(printer), 124 _ => draw_day(printer),
125 }; 125 };
126 } 126 }