diff --git a/src/app.rs b/src/app.rs index 2426714..0c84ea6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -6,6 +6,7 @@ use std::cmp::Ordering; use std::convert::TryInto; use std::process::Command; use std::result::Result; +use std::error::Error; use task_hookrs::date::Date; use task_hookrs::import::import; @@ -156,7 +157,7 @@ pub struct TaskReportTable { } impl TaskReportTable { - pub fn new() -> Self { + pub fn new() -> Result> { let virtual_tags = vec![ "PROJECT", "BLOCKED", @@ -196,21 +197,19 @@ impl TaskReportTable { tasks: vec![vec![]], virtual_tags: virtual_tags.iter().map(|s| s.to_string()).collect::>(), }; - task_report_table.export_headers(); - task_report_table + task_report_table.export_headers()?; + Ok(task_report_table) } - pub fn export_headers(&mut self) { + pub fn export_headers(&mut self) -> Result<(), Box> { self.columns = vec![]; self.labels = vec![]; let output = Command::new("task") .arg("show") .arg("report.next.columns") - .output() - .expect("Unable to run `task show report.next.columns`. Check documentation for more information"); - let data = String::from_utf8(output.stdout) - .expect("Unable to decode utf8 from stdout of `task show report.next.columns`"); + .output()?; + let data = String::from_utf8(output.stdout)?; for line in data.split('\n') { if line.starts_with("report.next.columns") { @@ -224,10 +223,8 @@ impl TaskReportTable { let output = Command::new("task") .arg("show") .arg("report.next.labels") - .output() - .expect("Unable to run `task show report.next.labels`. Check documentation for more information"); - let data = String::from_utf8(output.stdout) - .expect("Unable to decode utf8 from stdout of `task show report.next.labels`"); + .output()?; + let data = String::from_utf8(output.stdout)?; for line in data.split('\n') { if line.starts_with("report.next.labels") { @@ -237,6 +234,7 @@ impl TaskReportTable { } } } + Ok(()) } pub fn generate_table(&mut self, tasks: &[Task]) { @@ -372,7 +370,7 @@ pub struct TTApp { } impl TTApp { - pub fn new() -> Self { + pub fn new() -> Result> { let mut app = Self { should_quit: false, state: TableState::default(), @@ -384,45 +382,33 @@ impl TTApp { modify: LineBuffer::with_capacity(MAX_LINE), error: "".to_string(), mode: AppMode::TaskReport, - config: TConfig::default(), - task_report_table: TaskReportTable::new(), + config: TConfig::default()?, + task_report_table: TaskReportTable::new()?, calendar_year: Local::today().year(), }; for c in "status:pending ".chars() { app.filter.insert(c, 1); } - app.get_context(); - app.update(); - app + app.get_context()?; + app.update()?; + Ok(app) } - pub fn get_context(&mut self) { - self.context_name = String::from_utf8( - Command::new("task") - .arg("_get") - .arg("rc.context") - .output() - .expect("Unable to run `task _get rc.context`") - .stdout, - ) - .expect("Unable to decode utf8 from stdout of `task _get rc.context`"); + pub fn get_context(&mut self) -> Result<(), Box> { + let output = Command::new("task") + .arg("_get") + .arg("rc.context") + .output()?; + self.context_name = String::from_utf8(output.stdout)?; self.context_name = self.context_name.strip_suffix('\n').unwrap_or("").to_string(); - self.context_filter = String::from_utf8( - Command::new("task") - .arg("_get") - .arg(format!("rc.context.{}", self.context_name)) - .output() - .expect(format!("Unable to run `task _get rc.context.{}`", self.context_name).as_str()) - .stdout, - ) - .unwrap_or_else(|_| { - panic!( - "Unable to decode utf8 from stdout of `task _get rc.context.{}`", - self.context_name - ) - }); + let output = Command::new("task") + .arg("_get") + .arg(format!("rc.context.{}", self.context_name)) + .output()?; + self.context_filter = String::from_utf8(output.stdout)?; self.context_filter = self.context_filter.strip_suffix('\n').unwrap_or("").to_string(); + Ok(()) } pub fn draw(&mut self, f: &mut Frame) { @@ -933,9 +919,10 @@ impl TTApp { (tasks, headers) } - pub fn update(&mut self) { - self.export_tasks(); + pub fn update(&mut self) -> Result<(), Box> { + self.export_tasks()?; self.update_tags(); + Ok(()) } pub fn next(&mut self) { @@ -971,7 +958,7 @@ impl TTApp { self.state.select(Some(i)); } - pub fn export_tasks(&self) { + pub fn export_tasks(&mut self) -> Result<(), Box> { let mut task = Command::new("task"); task.arg("rc.json.array=on"); @@ -995,17 +982,12 @@ impl TTApp { } } - let output = task - .output() - .expect("Unable to run `task export`. Check documentation for more information."); - let data = String::from_utf8(output.stdout).unwrap_or_default(); - let imported = import(data.as_bytes()); - { - if let Ok(i) = imported { - *(self.tasks.lock().unwrap()) = i; - self.tasks.lock().unwrap().sort_by(cmp); - } - } + let output = task.output()?; + let data = String::from_utf8(output.stdout)?; + let imported = import(data.as_bytes())?; + *(self.tasks.lock().unwrap()) = imported; + self.tasks.lock().unwrap().sort_by(cmp); + Ok(()) } pub fn task_subprocess(&mut self) -> Result<(), String> { @@ -1405,39 +1387,39 @@ impl TTApp { } } - pub fn handle_input(&mut self, input: Key, terminal: &mut Terminal>, events: &Events) { + pub fn handle_input(&mut self, input: Key, terminal: &mut Terminal>, events: &Events) -> Result<(), Box> { match self.mode { AppMode::TaskReport => match input { Key::Ctrl('c') | Key::Char('q') => self.should_quit = true, Key::Char(']') => { self.mode = AppMode::Calendar; } - Key::Char('r') => self.update(), + Key::Char('r') => self.update()?, Key::Down | Key::Char('j') => self.next(), Key::Up | Key::Char('k') => self.previous(), Key::Char('d') => match self.task_done() { - Ok(_) => self.update(), + Ok(_) => self.update()?, Err(e) => { self.mode = AppMode::TaskError; self.error = e; } }, Key::Char('x') => match self.task_delete() { - Ok(_) => self.update(), + Ok(_) => self.update()?, Err(e) => { self.mode = AppMode::TaskError; self.error = e; } }, Key::Char('s') => match self.task_start_or_stop() { - Ok(_) => self.update(), + Ok(_) => self.update()?, Err(e) => { self.mode = AppMode::TaskError; self.error = e; } }, Key::Char('u') => match self.task_undo() { - Ok(_) => self.update(), + Ok(_) => self.update()?, Err(e) => { self.mode = AppMode::TaskError; self.error = e; @@ -1448,7 +1430,7 @@ impl TTApp { let r = self.task_edit(); events.resume_event_loop(terminal); match r { - Ok(_) => self.update(), + Ok(_) => self.update()?, Err(e) => { self.mode = AppMode::TaskError; self.error = e; @@ -1495,7 +1477,7 @@ impl TTApp { Key::Char('\n') => match self.task_modify() { Ok(_) => { self.mode = AppMode::TaskReport; - self.update(); + self.update()?; } Err(e) => { self.mode = AppMode::TaskError; @@ -1512,7 +1494,7 @@ impl TTApp { Key::Char('\n') => match self.task_subprocess() { Ok(_) => { self.mode = AppMode::TaskReport; - self.update(); + self.update()?; } Err(e) => { self.mode = AppMode::TaskError; @@ -1529,7 +1511,7 @@ impl TTApp { Key::Char('\n') => match self.task_log() { Ok(_) => { self.mode = AppMode::TaskReport; - self.update(); + self.update()?; } Err(e) => { self.mode = AppMode::TaskError; @@ -1546,7 +1528,7 @@ impl TTApp { Key::Char('\n') => match self.task_annotate() { Ok(_) => { self.mode = AppMode::TaskReport; - self.update(); + self.update()?; } Err(e) => { self.mode = AppMode::TaskError; @@ -1563,7 +1545,7 @@ impl TTApp { Key::Char('\n') => match self.task_add() { Ok(_) => { self.mode = AppMode::TaskReport; - self.update(); + self.update()?; } Err(e) => { self.mode = AppMode::TaskError; @@ -1579,7 +1561,7 @@ impl TTApp { AppMode::TaskFilter => match input { Key::Char('\n') | Key::Esc => { self.mode = AppMode::TaskReport; - self.update(); + self.update()?; } _ => handle_movement(&mut self.filter, input), }, @@ -1596,6 +1578,7 @@ impl TTApp { _ => {} }, } + Ok(()) } } diff --git a/src/config.rs b/src/config.rs index 35f07b1..ae9a47e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::process::Command; use std::str; use tui::style::{Color, Modifier}; +use std::error::Error; #[derive(Debug, Clone)] pub struct TColor { @@ -70,13 +71,13 @@ impl TaskWarriorBool for str { } impl TConfig { - pub fn default() -> Self { + pub fn default() -> Result> { let bool_collection = Self::get_bool_collection(); - Self { + Ok(Self { enabled: true, obfuscate: bool_collection.get("obfuscate").cloned().unwrap_or(false), print_empty_columns: bool_collection.get("print_empty_columns").cloned().unwrap_or(false), - color: Self::get_color_collection(), + color: Self::get_color_collection()?, rule_precedence_color: Self::get_rule_precedence_color(), uda_selection_indicator: Self::get_uda_selection_indicator(), uda_selection_bold: Self::get_uda_selection_bold(), @@ -84,22 +85,21 @@ impl TConfig { uda_selection_dim: Self::get_uda_selection_dim(), uda_selection_blink: Self::get_uda_selection_blink(), uda_calendar_months_per_row: Self::get_uda_months_per_row(), - } + }) } fn get_bool_collection() -> HashMap { HashMap::new() } - fn get_color_collection() -> HashMap { + fn get_color_collection() -> Result, Box> { + let mut color_collection = HashMap::new(); let output = Command::new("task") .arg("rc.color=off") .arg("show") - .output() - .expect("Unable to run `task show`"); + .output()?; let data = String::from_utf8(output.stdout).expect("Unable to convert stdout to string"); - let mut color_collection = HashMap::new(); for line in data.split('\n') { if line.starts_with("color.") { let mut i = line.split(' '); @@ -113,7 +113,8 @@ impl TConfig { }; } } - color_collection + + Ok(color_collection) } fn get_tcolor(line: &str) -> TColor { diff --git a/src/main.rs b/src/main.rs index fcb23b3..c05140c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,23 +50,43 @@ fn tui_main(_config: &str) -> Result<(), Box> { tick_rate: Duration::from_millis(1000), }); - let mut app = TTApp::new(); - app.next(); + let maybeapp = TTApp::new(); + match maybeapp { + Ok(mut app) => { + app.next(); - loop { - terminal.draw(|mut frame| app.draw(&mut frame)).unwrap(); + loop { + terminal.draw(|mut frame| app.draw(&mut frame)).unwrap(); - // Handle input - match events.next()? { - Event::Input(input) => app.handle_input(input, &mut terminal, &events), - Event::Tick => app.update(), - } + // Handle input + match events.next()? { + Event::Input(input) => { + let r = app.handle_input(input, &mut terminal, &events); + if r.is_err() { + destruct_terminal(terminal); + return r + } + }, + Event::Tick => { + let r = app.update(); + if r.is_err() { + destruct_terminal(terminal); + return r + } + }, + } - if app.should_quit { + if app.should_quit { + destruct_terminal(terminal); + break; + } + } + + Ok(()) + }, + Err(e) => { destruct_terminal(terminal); - break; - } + Err(e) + }, } - - Ok(()) }