From 399020023261ee0b6e6dab9000cc1197b1e865b3 Mon Sep 17 00:00:00 2001 From: Arijit Basu Date: Fri, 2 Apr 2021 17:10:04 +0530 Subject: [PATCH] Anyhow handle errors --- Cargo.lock | 9 ++++- Cargo.toml | 3 +- src/app.rs | 112 +++++++++++++++++++++++++++++++-------------------- src/error.rs | 32 --------------- src/lib.rs | 1 - src/main.rs | 78 +++++++++++------------------------ 6 files changed, 103 insertions(+), 132 deletions(-) delete mode 100644 src/error.rs diff --git a/Cargo.lock b/Cargo.lock index cfbea40..7d20c39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" + [[package]] name = "arrayref" version = "0.3.6" @@ -1117,8 +1123,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "xplr" -version = "0.2.10" +version = "0.2.11" dependencies = [ + "anyhow", "criterion", "crossterm", "dirs", diff --git a/Cargo.toml b/Cargo.toml index 5ec5656..e622128 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xplr" -version = "0.2.10" # Update app.rs +version = "0.2.11" # Update app.rs authors = ["Arijit Basu "] edition = "2018" description = "An experimental, minimal, configurable TUI file explorer, stealing ideas from nnn and fzf." @@ -21,6 +21,7 @@ serde_json = "1.0" serde_yaml = "0.8" handlebars = "3.5" mime_guess = "2.0.3" +anyhow = "1.0" [dev-dependencies] criterion = "0.3" diff --git a/src/app.rs b/src/app.rs index 920c8fc..1b4105b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,17 +1,19 @@ use crate::config::Config; use crate::config::Mode; -use crate::error::Error; use crate::input::Key; +use anyhow::{bail, Result}; use mime_guess; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::collections::BinaryHeap; use std::collections::HashMap; use std::collections::VecDeque; +use std::env; use std::fs; +use std::io; use std::path::PathBuf; -pub const VERSION: &str = "v0.2.10"; // Update Cargo.toml +pub const VERSION: &str = "v0.2.11"; // Update Cargo.toml pub const TEMPLATE_TABLE_ROW: &str = "TEMPLATE_TABLE_ROW"; @@ -263,11 +265,35 @@ pub struct App { } impl App { - pub fn create(config: Config, pwd: String) -> Result { + pub fn create() -> Result { + let mut pwd = PathBuf::from(env::args().skip(1).next().unwrap_or(".".into())) + .canonicalize() + .unwrap_or_default(); + + if pwd.is_file() { + pwd = pwd.parent().map(|p| p.into()).unwrap_or_default(); + } + + let pwd = pwd.to_string_lossy().to_string(); + + let config_dir = dirs::config_dir() + .unwrap_or(PathBuf::from(".")) + .join("xplr"); + + let config_file = config_dir.join("config.yml"); + + let config: Config = if config_file.exists() { + serde_yaml::from_reader(io::BufReader::new(&fs::File::open(&config_file)?))? + } else { + Config::default() + }; + if config.version != VERSION { - Err(Error::IncompatibleVersion( - "incompatible config version".into(), - )) + bail!( + "incompatible config version {} in {}", + config.version, + config_file.to_string_lossy().to_string() + ) } else { let mode = config .modes @@ -309,7 +335,7 @@ impl App { self } - pub fn possibly_mutate(mut self) -> Result { + pub fn possibly_mutate(mut self) -> Result { if let Some(task) = self.tasks.pop() { match task.msg { MsgIn::Internal(msg) => self.handle_internal(msg), @@ -320,14 +346,14 @@ impl App { } } - fn handle_internal(self, msg: InternalMsg) -> Result { + fn handle_internal(self, msg: InternalMsg) -> Result { match msg { InternalMsg::AddDirectory(parent, dir) => self.add_directory(parent, dir), InternalMsg::HandleKey(key) => self.handle_key(key), } } - fn handle_external(self, msg: ExternalMsg, key: Option) -> Result { + fn handle_external(self, msg: ExternalMsg, key: Option) -> Result { match msg { ExternalMsg::Explore => self.explore(), ExternalMsg::Refresh => self.refresh(), @@ -366,11 +392,11 @@ impl App { ExternalMsg::PrintResultAndQuit => self.print_result_and_quit(), ExternalMsg::PrintAppStateAndQuit => self.print_app_state_and_quit(), ExternalMsg::Debug(path) => self.debug(&path), - ExternalMsg::Terminate => Err(Error::Terminated), + ExternalMsg::Terminate => bail!("terminated"), } } - fn handle_key(mut self, key: Key) -> Result { + fn handle_key(mut self, key: Key) -> Result { let kb = self.mode.key_bindings.clone(); let default = kb.default.clone(); let msgs = kb @@ -397,22 +423,22 @@ impl App { Ok(self) } - fn explore(mut self) -> Result { + fn explore(mut self) -> Result { self.msg_out.push_back(MsgOut::Explore); Ok(self) } - fn refresh(mut self) -> Result { + fn refresh(mut self) -> Result { self.msg_out.push_back(MsgOut::Refresh); Ok(self) } - fn clear_screen(mut self) -> Result { + fn clear_screen(mut self) -> Result { self.msg_out.push_back(MsgOut::ClearScreen); Ok(self) } - fn focus_first(mut self) -> Result { + fn focus_first(mut self) -> Result { if let Some(dir) = self.directory_buffer_mut() { dir.focus = 0; self.msg_out.push_back(MsgOut::Refresh); @@ -420,7 +446,7 @@ impl App { Ok(self) } - fn focus_last(mut self) -> Result { + fn focus_last(mut self) -> Result { if let Some(dir) = self.directory_buffer_mut() { dir.focus = dir.total.max(1) - 1; self.msg_out.push_back(MsgOut::Refresh); @@ -428,7 +454,7 @@ impl App { Ok(self) } - fn focus_previous(mut self) -> Result { + fn focus_previous(mut self) -> Result { if let Some(dir) = self.directory_buffer_mut() { dir.focus = dir.focus.max(1) - 1; self.msg_out.push_back(MsgOut::Refresh); @@ -436,7 +462,7 @@ impl App { Ok(self) } - fn focus_previous_by_relative_index(mut self, index: usize) -> Result { + fn focus_previous_by_relative_index(mut self, index: usize) -> Result { if let Some(dir) = self.directory_buffer_mut() { dir.focus = dir.focus.max(index) - index; self.msg_out.push_back(MsgOut::Refresh); @@ -444,7 +470,7 @@ impl App { Ok(self) } - fn focus_previous_by_relative_index_from_input(self) -> Result { + fn focus_previous_by_relative_index_from_input(self) -> Result { if let Some(index) = self.input_buffer().and_then(|i| i.parse::().ok()) { self.focus_previous_by_relative_index(index) } else { @@ -452,7 +478,7 @@ impl App { } } - fn focus_next(mut self) -> Result { + fn focus_next(mut self) -> Result { if let Some(dir) = self.directory_buffer_mut() { dir.focus = (dir.focus + 1).min(dir.total.max(1) - 1); self.msg_out.push_back(MsgOut::Refresh); @@ -460,7 +486,7 @@ impl App { Ok(self) } - fn focus_next_by_relative_index(mut self, index: usize) -> Result { + fn focus_next_by_relative_index(mut self, index: usize) -> Result { if let Some(dir) = self.directory_buffer_mut() { dir.focus = (dir.focus + index).min(dir.total.max(1) - 1); self.msg_out.push_back(MsgOut::Refresh); @@ -468,7 +494,7 @@ impl App { Ok(self) } - fn focus_next_by_relative_index_from_input(self) -> Result { + fn focus_next_by_relative_index_from_input(self) -> Result { if let Some(index) = self.input_buffer().and_then(|i| i.parse::().ok()) { self.focus_next_by_relative_index(index) } else { @@ -476,7 +502,7 @@ impl App { } } - fn change_directory(mut self, dir: &String) -> Result { + fn change_directory(mut self, dir: &String) -> Result { if PathBuf::from(dir).is_dir() { self.pwd = dir.to_owned(); self.msg_out.push_back(MsgOut::Refresh); @@ -484,14 +510,14 @@ impl App { Ok(self) } - fn enter(self) -> Result { + fn enter(self) -> Result { self.focused_node() .map(|n| n.absolute_path.clone()) .map(|p| self.clone().change_directory(&p)) .unwrap_or(Ok(self)) } - fn back(self) -> Result { + fn back(self) -> Result { PathBuf::from(self.pwd()) .parent() .map(|p| { @@ -501,7 +527,7 @@ impl App { .unwrap_or(Ok(self)) } - fn buffer_string(mut self, input: &String) -> Result { + fn buffer_string(mut self, input: &String) -> Result { if let Some(buf) = self.input_buffer.as_mut() { buf.extend(input.chars()); } else { @@ -511,7 +537,7 @@ impl App { Ok(self) } - fn buffer_string_from_key(self, key: Option) -> Result { + fn buffer_string_from_key(self, key: Option) -> Result { if let Some(c) = key.and_then(|k| k.to_char()) { self.buffer_string(&c.to_string()) } else { @@ -519,13 +545,13 @@ impl App { } } - fn reset_input_buffer(mut self) -> Result { + fn reset_input_buffer(mut self) -> Result { self.input_buffer = None; self.msg_out.push_back(MsgOut::Refresh); Ok(self) } - fn focus_by_index(mut self, index: usize) -> Result { + fn focus_by_index(mut self, index: usize) -> Result { if let Some(dir) = self.directory_buffer_mut() { dir.focus = index.min(dir.total.max(1) - 1); self.msg_out.push_back(MsgOut::Refresh); @@ -533,7 +559,7 @@ impl App { Ok(self) } - fn focus_by_index_from_input(self) -> Result { + fn focus_by_index_from_input(self) -> Result { if let Some(index) = self.input_buffer().and_then(|i| i.parse::().ok()) { self.focus_by_index(index) } else { @@ -541,7 +567,7 @@ impl App { } } - fn focus_by_file_name(mut self, name: &String) -> Result { + fn focus_by_file_name(mut self, name: &String) -> Result { if let Some(dir_buf) = self.directory_buffer_mut() { if let Some(focus) = dir_buf .clone() @@ -558,7 +584,7 @@ impl App { Ok(self) } - fn focus_path(self, path: &String) -> Result { + fn focus_path(self, path: &String) -> Result { let pathbuf = PathBuf::from(path); if let Some(parent) = pathbuf.parent() { if let Some(filename) = pathbuf.file_name() { @@ -572,7 +598,7 @@ impl App { } } - fn switch_mode(mut self, mode: &String) -> Result { + fn switch_mode(mut self, mode: &String) -> Result { if let Some(mode) = self.config.modes.get(mode) { self.mode = mode.to_owned(); self.msg_out.push_back(MsgOut::Refresh); @@ -580,18 +606,18 @@ impl App { Ok(self) } - fn call(mut self, command: Command) -> Result { + fn call(mut self, command: Command) -> Result { self.msg_out.push_back(MsgOut::Call(command)); Ok(self) } - fn add_directory(mut self, parent: String, dir: DirectoryBuffer) -> Result { + fn add_directory(mut self, parent: String, dir: DirectoryBuffer) -> Result { self.directory_buffers.insert(parent, dir); self.msg_out.push_back(MsgOut::Refresh); Ok(self) } - fn select(mut self) -> Result { + fn select(mut self) -> Result { if let Some(n) = self.focused_node().map(|n| n.to_owned()) { self.selection.push(n.clone()); self.msg_out.push_back(MsgOut::Refresh); @@ -599,7 +625,7 @@ impl App { Ok(self) } - fn un_select(mut self) -> Result { + fn un_select(mut self) -> Result { if let Some(n) = self.focused_node().map(|n| n.to_owned()) { self.selection = self.selection.into_iter().filter(|s| s != &n).collect(); self.msg_out.push_back(MsgOut::Refresh); @@ -607,7 +633,7 @@ impl App { Ok(self) } - fn toggle_selection(mut self) -> Result { + fn toggle_selection(mut self) -> Result { if let Some(n) = self.focused_node() { if self.selection().contains(n) { self = self.un_select()?; @@ -618,23 +644,23 @@ impl App { Ok(self) } - fn clear_selection(mut self) -> Result { + fn clear_selection(mut self) -> Result { self.selection.clear(); self.msg_out.push_back(MsgOut::Refresh); Ok(self) } - fn print_result_and_quit(mut self) -> Result { + fn print_result_and_quit(mut self) -> Result { self.msg_out.push_back(MsgOut::PrintResultAndQuit); Ok(self) } - fn print_app_state_and_quit(mut self) -> Result { + fn print_app_state_and_quit(mut self) -> Result { self.msg_out.push_back(MsgOut::PrintAppStateAndQuit); Ok(self) } - fn debug(mut self, path: &String) -> Result { + fn debug(mut self, path: &String) -> Result { self.msg_out.push_back(MsgOut::Debug(path.to_owned())); Ok(self) } @@ -697,7 +723,7 @@ impl App { &self.session_path } - pub fn refresh_selection(mut self) -> Result { + pub fn refresh_selection(mut self) -> Result { self.selection = self .selection .into_iter() diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 4f03d99..0000000 --- a/src/error.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::io; -use handlebars; -use serde_yaml; - -#[derive(Debug)] -pub enum Error { - IncompatibleVersion(String), - Terminated, - - // Real errors - TemplateError(handlebars::TemplateError), - YamlError(serde_yaml::Error), - IOError(io::Error), -} - -impl From for Error { - fn from(err: io::Error) -> Self { - Self::IOError(err) - } -} - -impl From for Error { - fn from(err: handlebars::TemplateError) -> Self { - Self::TemplateError(err) - } -} - -impl From for Error { - fn from(err: serde_yaml::Error) -> Self { - Self::YamlError(err) - } -} diff --git a/src/lib.rs b/src/lib.rs index f20d14a..c635daa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ pub mod app; pub mod config; -pub mod error; pub mod explorer; pub mod input; pub mod ui; diff --git a/src/main.rs b/src/main.rs index 50fb023..2e6275f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,10 @@ +use anyhow::Result; use crossterm::event::Event; use crossterm::terminal as term; use crossterm::{event, execute}; use handlebars::Handlebars; -use std::env; use std::fs; -use std::io; use std::io::prelude::*; -use std::path::PathBuf; use std::sync::mpsc; use std::thread; use std::time::Duration; @@ -14,41 +12,12 @@ use termion::get_tty; use tui::backend::CrosstermBackend; use tui::Terminal; use xplr::app; -use xplr::config::Config; -use xplr::error::Error; use xplr::explorer; use xplr::input::Key; use xplr::ui; -fn main() -> Result<(), Error> { - let mut pwd = PathBuf::from(env::args().skip(1).next().unwrap_or(".".into())) - .canonicalize() - .unwrap_or_default(); - - let mut focused_path = None; - - if pwd.is_file() { - focused_path = pwd.file_name().map(|n| n.to_string_lossy().to_string()); - pwd = pwd.parent().map(|p| p.into()).unwrap_or_default(); - } - - let pwd = pwd.to_string_lossy().to_string(); - - let mut last_pwd = pwd.clone(); - - let config_dir = dirs::config_dir() - .unwrap_or(PathBuf::from(".")) - .join("xplr"); - - let config_file = config_dir.join("config.yml"); - - let config: Config = if config_file.exists() { - serde_yaml::from_reader(io::BufReader::new(&fs::File::open(&config_file)?))? - } else { - Config::default() - }; - - let mut app = app::App::create(config, pwd.clone())?; +fn main() -> Result<()> { + let mut app = app::App::create()?; let mut hb = Handlebars::new(); hb.register_template_string( @@ -72,13 +41,13 @@ fn main() -> Result<(), Error> { let tx_pipe = tx_key.clone(); let tx_explorer = tx_key.clone(); - term::enable_raw_mode().unwrap(); - let mut stdout = get_tty().unwrap(); + term::enable_raw_mode()?; + let mut stdout = get_tty()?; // let mut stdout = stdout.lock(); - execute!(stdout, term::EnterAlternateScreen).unwrap(); + execute!(stdout, term::EnterAlternateScreen)?; // let stdout = MouseTerminal::from(stdout); let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend).unwrap(); + let mut terminal = Terminal::new(backend)?; terminal.hide_cursor()?; let (tx, rx_key) = mpsc::channel(); @@ -128,13 +97,14 @@ fn main() -> Result<(), Error> { thread::sleep(Duration::from_millis(10)); }); - explorer::explore(pwd.clone(), focused_path, tx_init); + explorer::explore(app.pwd().clone(), std::env::args().skip(1).next(), tx_init); + let mut last_pwd = app.pwd().clone(); 'outer: while result.is_ok() { while let Some(msg) = app.pop_msg_out() { match msg { app::MsgOut::Debug(path) => { - fs::write(&path, serde_yaml::to_string(&app).unwrap_or_default())?; + fs::write(&path, serde_yaml::to_string(&app)?)?; } app::MsgOut::PrintResultAndQuit => { @@ -171,7 +141,7 @@ fn main() -> Result<(), Error> { }; // UI - terminal.draw(|f| ui::draw(f, &app, &hb)).unwrap(); + terminal.draw(|f| ui::draw(f, &app, &hb))?; // Pipes let focused = app @@ -179,7 +149,7 @@ fn main() -> Result<(), Error> { .map(|n| n.absolute_path.clone()) .unwrap_or_default(); - fs::write(&app.pipes().focus_out, focused).unwrap(); + fs::write(&app.pipes().focus_out, focused)?; app = app.refresh_selection()?; @@ -190,16 +160,16 @@ fn main() -> Result<(), Error> { .collect::>() .join("\n"); - fs::write(&app.pipes().selection_out, selection).unwrap(); + fs::write(&app.pipes().selection_out, selection)?; - fs::write(&app.pipes().mode_out, &app.mode().name).unwrap(); + fs::write(&app.pipes().mode_out, &app.mode().name)?; } app::MsgOut::Call(cmd) => { - tx.send(true).unwrap(); + tx.send(true)?; terminal.clear()?; - term::disable_raw_mode().unwrap(); - execute!(terminal.backend_mut(), term::LeaveAlternateScreen).unwrap(); + term::disable_raw_mode()?; + execute!(terminal.backend_mut(), term::LeaveAlternateScreen)?; terminal.show_cursor()?; let pid = std::process::id().to_string(); @@ -238,7 +208,7 @@ fn main() -> Result<(), Error> { let pipe_focus_out = app.pipes().focus_out.clone(); let pipe_selection_out = app.pipes().selection_out.clone(); - let app_yaml = serde_yaml::to_string(&app).unwrap_or_default(); + let app_yaml = serde_yaml::to_string(&app)?; let session_path = app.session_path(); let result = app.result_str(); @@ -260,10 +230,10 @@ fn main() -> Result<(), Error> { .status(); terminal.hide_cursor()?; - execute!(terminal.backend_mut(), term::EnterAlternateScreen).unwrap(); - term::enable_raw_mode().unwrap(); - tx.send(false).unwrap(); - terminal.draw(|f| ui::draw(f, &app, &hb)).unwrap(); + execute!(terminal.backend_mut(), term::EnterAlternateScreen)?; + term::enable_raw_mode()?; + tx.send(false)?; + terminal.draw(|f| ui::draw(f, &app, &hb))?; } }; } @@ -283,8 +253,8 @@ fn main() -> Result<(), Error> { // thread::sleep(Duration::from_millis(10)); } - term::disable_raw_mode().unwrap(); - execute!(terminal.backend_mut(), term::LeaveAlternateScreen).unwrap(); + term::disable_raw_mode()?; + execute!(terminal.backend_mut(), term::LeaveAlternateScreen)?; terminal.show_cursor()?; if let Some(out) = output {