summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAkshay <nerdy@peppe.rs>2020-07-18 21:06:40 +0530
committerAkshay <nerdy@peppe.rs>2020-07-18 21:06:40 +0530
commit0b4af96a515d51c409c6dafef406542dee9da3d4 (patch)
tree21acafc98b48988d43627b8b8374ad02358f25d1
parent29d49dddaae57d59a2c99c376a632a0d9560dcfc (diff)
add smooth error handling
-rw-r--r--src/app/impl_self.rs83
-rw-r--r--src/app/impl_view.rs13
-rw-r--r--src/command.rs95
-rw-r--r--src/main.rs18
4 files changed, 140 insertions, 69 deletions
diff --git a/src/app/impl_self.rs b/src/app/impl_self.rs
index efed4e0..7bf9ab0 100644
--- a/src/app/impl_self.rs
+++ b/src/app/impl_self.rs
@@ -11,12 +11,12 @@ 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::utils;
-use crate::Command;
use crate::CONFIGURATION;
-use crate::app::{App, StatusLine};
+use crate::app::{App, Message, MessageKind, StatusLine};
impl App {
pub fn new() -> Self {
@@ -33,6 +33,7 @@ impl App {
_file_watcher: watcher,
file_event_recv: rx,
view_month_offset: 0,
+ message: Message::default(),
};
}
@@ -200,47 +201,49 @@ impl App {
write_to_file(auto, auto_f);
}
- pub fn parse_command(&mut self, c: Command) {
- match c {
- Command::Add(name, goal, auto) => {
- 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.unwrap_or(false),
- )));
- } else if kind == "bit" {
- self.add_habit(Box::new(Bit::new(name, auto.unwrap_or(false))));
+ pub fn parse_command(&mut self, result: Result<Command, CommandLineError>) {
+ match result {
+ Ok(c) => match c {
+ Command::Add(name, goal, auto) => {
+ 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)));
+ }
}
- }
- Command::Delete(name) => {
- self.delete_by_name(&name);
- self.focus = 0;
- }
- Command::TrackUp(name) => {
- let target_habit = self
- .habits
- .iter_mut()
- .find(|x| x.name() == name && x.is_auto());
- if let Some(h) = target_habit {
- h.modify(Local::now().naive_utc().date(), TrackEvent::Increment);
+ Command::Delete(name) => {
+ self.delete_by_name(&name);
+ self.focus = 0;
}
- }
- Command::TrackDown(name) => {
- let target_habit = self
- .habits
- .iter_mut()
- .find(|x| x.name() == name && x.is_auto());
- if let Some(h) = target_habit {
- h.modify(Local::now().naive_utc().date(), TrackEvent::Decrement);
+ Command::TrackUp(name) => {
+ let target_habit = self
+ .habits
+ .iter_mut()
+ .find(|x| x.name() == name && x.is_auto());
+ if let Some(h) = target_habit {
+ h.modify(Local::now().naive_utc().date(), TrackEvent::Increment);
+ }
}
- }
- Command::Quit => self.save_state(),
- Command::MonthNext => self.sift_forward(),
- Command::MonthPrev => self.sift_backward(),
- _ => {
- eprintln!("UNKNOWN COMMAND!");
+ Command::TrackDown(name) => {
+ let target_habit = self
+ .habits
+ .iter_mut()
+ .find(|x| x.name() == name && x.is_auto());
+ if let Some(h) = target_habit {
+ h.modify(Local::now().naive_utc().date(), TrackEvent::Decrement);
+ }
+ }
+ Command::Quit => self.save_state(),
+ Command::MonthNext => self.sift_forward(),
+ Command::MonthPrev => self.sift_backward(),
+ _ => {
+ eprintln!("UNKNOWN COMMAND!");
+ }
+ },
+ Err(e) => {
+ self.message.set_message(e.to_string());
+ self.message.set_kind(MessageKind::Error);
}
}
}
diff --git a/src/app/impl_view.rs b/src/app/impl_view.rs
index 904403b..0a6bce6 100644
--- a/src/app/impl_view.rs
+++ b/src/app/impl_view.rs
@@ -5,11 +5,12 @@ use std::path::PathBuf;
use cursive::direction::{Absolute, Direction};
use cursive::event::{Event, EventResult, Key};
+use cursive::theme::{Color, Style};
use cursive::view::View;
use cursive::{Printer, Vec2};
use notify::DebouncedEvent;
-use crate::app::App;
+use crate::app::{App, MessageKind};
use crate::habit::{HabitWrapper, ViewMode};
use crate::utils;
use crate::CONFIGURATION;
@@ -36,6 +37,11 @@ impl View for App {
let full = self.max_size().x;
offset = offset.map_x(|_| full - status.1.len());
printer.print(offset, &status.1); // right status
+
+ offset = offset.map_x(|_| 0).map_y(|_| self.max_size().y - 1);
+ printer.with_style(Color::from(self.message.kind()), |p| {
+ p.print(offset, self.message.contents())
+ });
}
fn required_size(&mut self, _: Vec2) -> Vec2 {
@@ -158,6 +164,11 @@ impl View for App {
self.set_view_month_offset(0);
return EventResult::Consumed(None);
}
+ Event::CtrlChar('l') => {
+ self.message.clear();
+ self.message.set_kind(MessageKind::Info);
+ return EventResult::Consumed(None);
+ }
/* Every keybind that is not caught by App trickles
* down to the focused habit. We sift back to today
diff --git a/src/command.rs b/src/command.rs
index 79d0fe5..d285138 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -1,3 +1,5 @@
+use std::fmt;
+
use cursive::view::Resizable;
use cursive::views::{Dialog, EditView};
use cursive::Cursive;
@@ -19,7 +21,7 @@ fn call_on_app(s: &mut Cursive, input: &str) {
// our main cursive object, has to be parsed again
// here
// TODO: fix this somehow
- if Command::from_string(input) == Command::Quit {
+ if let Ok(Command::Quit) = Command::from_string(input) {
s.quit();
}
@@ -28,7 +30,7 @@ fn call_on_app(s: &mut Cursive, input: &str) {
#[derive(PartialEq)]
pub enum Command {
- Add(String, Option<u32>, Option<bool>), // habit name, habit type, optional goal, auto tracked
+ Add(String, Option<u32>, bool),
MonthPrev,
MonthNext,
Delete(String),
@@ -38,46 +40,95 @@ pub enum Command {
Blank,
}
+#[derive(Debug)]
+pub enum CommandLineError {
+ InvalidCommand(String),
+ InvalidArg(u32), // position
+ NotEnoughArgs(String, u32),
+}
+
+impl std::error::Error for CommandLineError {}
+
+impl fmt::Display for CommandLineError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ CommandLineError::InvalidCommand(s) => write!(f, "Invalid command: `{}`", s),
+ CommandLineError::InvalidArg(p) => write!(f, "Invalid argument at position {}", p),
+ CommandLineError::NotEnoughArgs(s, n) => {
+ write!(f, "Command `{}` requires atleast {} argument(s)!", s, n)
+ }
+ }
+ }
+}
+
+type Result<T> = std::result::Result<T, CommandLineError>;
+
impl Command {
- pub fn from_string<P: AsRef<str>>(input: P) -> Self {
+ pub fn from_string<P: AsRef<str>>(input: P) -> Result<Command> {
let mut strings: Vec<&str> = input.as_ref().trim().split(' ').collect();
if strings.is_empty() {
- return Command::Blank;
+ return Ok(Command::Blank);
}
let first = strings.first().unwrap().to_string();
let mut args: Vec<String> = strings.iter_mut().skip(1).map(|s| s.to_string()).collect();
match first.as_ref() {
"add" | "a" => {
- if args.len() < 2 {
- return Command::Blank;
+ if args.is_empty() {
+ return Err(CommandLineError::NotEnoughArgs(first, 1));
+ }
+ let goal = args
+ .get(1)
+ .map(|x| {
+ x.parse::<u32>()
+ .map_err(|_| CommandLineError::InvalidArg(1))
+ })
+ .transpose()?;
+ return Ok(Command::Add(
+ args.get_mut(0).unwrap().to_string(),
+ goal,
+ false,
+ ));
+ }
+ "add-auto" | "aa" => {
+ if args.is_empty() {
+ return Err(CommandLineError::NotEnoughArgs(first, 1));
}
- let goal = args.get(1).map(|g| g.parse::<u32>().ok()).flatten();
- let auto = args.get(2).map(|g| if g == "auto" { true } else { false });
- return Command::Add(args.get_mut(0).unwrap().to_string(), goal, auto);
+ let goal = args
+ .get(1)
+ .map(|x| {
+ x.parse::<u32>()
+ .map_err(|_| CommandLineError::InvalidArg(1))
+ })
+ .transpose()?;
+ return Ok(Command::Add(
+ args.get_mut(0).unwrap().to_string(),
+ goal,
+ true,
+ ));
}
"delete" | "d" => {
- if args.len() < 1 {
- return Command::Blank;
+ if args.is_empty() {
+ return Err(CommandLineError::NotEnoughArgs(first, 1));
}
- return Command::Delete(args[0].to_string());
+ return Ok(Command::Delete(args[0].to_string()));
}
"track-up" | "tup" => {
- if args.len() < 1 {
- return Command::Blank;
+ if args.is_empty() {
+ return Err(CommandLineError::NotEnoughArgs(first, 1));
}
- return Command::TrackUp(args[0].to_string());
+ return Ok(Command::TrackUp(args[0].to_string()));
}
"track-down" | "tdown" => {
- if args.len() < 1 {
- return Command::Blank;
+ if args.is_empty() {
+ return Err(CommandLineError::NotEnoughArgs(first, 1));
}
- return Command::TrackDown(args[0].to_string());
+ return Ok(Command::TrackDown(args[0].to_string()));
}
- "mprev" | "month-prev" => return Command::MonthPrev,
- "mnext" | "month-next" => return Command::MonthNext,
- "q" | "quit" => return Command::Quit,
- _ => return Command::Blank,
+ "mprev" | "month-prev" => return Ok(Command::MonthPrev),
+ "mnext" | "month-next" => return Ok(Command::MonthNext),
+ "q" | "quit" => return Ok(Command::Quit),
+ s => return Err(CommandLineError::InvalidCommand(s.into())),
}
}
}
diff --git a/src/main.rs b/src/main.rs
index dc6081d..f83fc83 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -36,12 +36,18 @@ fn main() {
.get_matches();
if let Some(c) = matches.value_of("command") {
let command = Command::from_string(c);
- if matches!(command, Command::TrackUp(_) | Command::TrackDown(_)) {
- let mut app = App::load_state();
- app.parse_command(command);
- app.save_state();
- } else {
- eprintln!("Invalid or unsupported command!");
+ match command {
+ Ok(Command::TrackUp(_)) | Ok(Command::TrackDown(_)) => {
+ let mut app = App::load_state();
+ app.parse_command(command);
+ app.save_state();
+ }
+ Err(e) => {
+ eprintln!("{}", e);
+ }
+ _ => eprintln!(
+ "Commands other than `track-up` and `track-down` are currently not supported!"
+ ),
}
} else {
let mut s = termion().unwrap();