From bdf6b158001ce669b86b7d8caea8986bddbebc61 Mon Sep 17 00:00:00 2001 From: qkzk Date: Fri, 10 Mar 2023 22:36:41 +0100 Subject: new version --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0dfff71..536f13b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -817,7 +817,7 @@ dependencies = [ [[package]] name = "fm-tui" -version = "0.1.19" +version = "0.1.20" dependencies = [ "anyhow", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 209a13e..b9d2104 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fm-tui" -version = "0.1.19" +version = "0.1.20" authors = ["Quentin Konieczko "] edition = "2021" license-file = "LICENSE.txt" -- cgit v1.2.3 From 64785f552fa04078483da5a955c8843729a67f9b Mon Sep 17 00:00:00 2001 From: qkzk Date: Fri, 10 Mar 2023 22:41:34 +0100 Subject: display version in help --- development.md | 1 + src/term_manager.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/development.md b/development.md index 911de17..b3085c4 100644 --- a/development.md +++ b/development.md @@ -457,6 +457,7 @@ New view: Tree ! Toggle with 't', fold with 'z'. Navigate normally. - [ ] Version 0.1.20 + - [ ] display version in help - [ ] exec multiple flagged files - [ ] shell menu diff --git a/src/term_manager.rs b/src/term_manager.rs index 89bb937..fceb056 100644 --- a/src/term_manager.rs +++ b/src/term_manager.rs @@ -211,8 +211,10 @@ impl<'a> WinMain<'a> { } fn help_first_row() -> Vec { + let version = std::env!("CARGO_PKG_VERSION"); vec![ HELP_FIRST_SENTENCE.to_owned(), + format!("Version: {version} "), HELP_SECOND_SENTENCE.to_owned(), ] } -- cgit v1.2.3 From 2d0ce2896868f8fc72b560be5ebaa2a4f729b9dd Mon Sep 17 00:00:00 2001 From: qkzk Date: Sat, 11 Mar 2023 16:51:19 +0100 Subject: replace FmError & FmResult by anyhow --- development.md | 4 +- src/action_map.rs | 4 +- src/bulkrename.rs | 46 ++++---- src/completion.rs | 14 +-- src/compress.rs | 17 +-- src/config.rs | 10 +- src/copy_move.rs | 9 +- src/cryptsetup.rs | 57 +++++---- src/decompress.rs | 16 +-- src/event_dispatch.rs | 8 +- src/event_exec.rs | 311 ++++++++++++++++++++++++-------------------------- src/fileinfo.rs | 84 +++++++------- src/fm_error.rs | 190 ------------------------------ src/git.rs | 14 +-- src/help.rs | 4 +- src/keybindings.rs | 4 +- src/lib.rs | 1 - src/log.rs | 6 +- src/main.rs | 5 +- src/marks.rs | 25 ++-- src/mocp.rs | 10 +- src/opener.rs | 41 +++---- src/preview.rs | 67 ++++++----- src/shell_menu.rs | 19 +-- src/status.rs | 54 +++++---- src/tab.rs | 45 ++++---- src/term_manager.rs | 86 ++++++-------- src/trash.rs | 83 +++++--------- src/tree.rs | 22 ++-- src/utils.rs | 18 +-- 30 files changed, 516 insertions(+), 758 deletions(-) delete mode 100644 src/fm_error.rs diff --git a/development.md b/development.md index b3085c4..e378b94 100644 --- a/development.md +++ b/development.md @@ -457,14 +457,14 @@ New view: Tree ! Toggle with 't', fold with 'z'. Navigate normally. - [ ] Version 0.1.20 - - [ ] display version in help + - [x] display version in help + - [x] replace FmResult & FmError by anyhow since I'm already using it... - [ ] exec multiple flagged files - [ ] shell menu - [ ] allow non tui like wttr, diff, bat, tail -n etc. - [ ] more options like "use flagged files" for diff - - [ ] replace FmResult & FmError by anyhow since I'm already using it... - [ ] build option to force reset of config file, warn the user at first start - [ ] update readme & animation - [ ] optionable "plugin" started from config file. Would require every option to be `Option` and may cause problems with the borrow checker. diff --git a/src/action_map.rs b/src/action_map.rs index 2593eaf..4197b34 100644 --- a/src/action_map.rs +++ b/src/action_map.rs @@ -1,8 +1,8 @@ +use anyhow::Result; use strum_macros::{Display, EnumIter, EnumString}; use crate::config::Colors; use crate::event_exec::EventExec; -use crate::fm_error::FmResult; use crate::status::Status; /// Different kind of action which can be mapped to a key. @@ -96,7 +96,7 @@ pub enum ActionMap { impl ActionMap { /// Makes the junction between `Actions` and `Events`. /// Every Action links to a different `EventExec` method. - pub fn matcher(&self, status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn matcher(&self, status: &mut Status, colors: &Colors) -> Result<()> { let current_tab = status.selected(); match *self { ActionMap::Back => EventExec::event_back(status, colors), diff --git a/src/bulkrename.rs b/src/bulkrename.rs index 634a13c..aedaaec 100644 --- a/src/bulkrename.rs +++ b/src/bulkrename.rs @@ -1,3 +1,4 @@ +use anyhow::{anyhow, Result}; use log::info; use rand::Rng; use std::io::{BufRead, Write}; @@ -6,7 +7,6 @@ use std::thread; use std::time::{Duration, SystemTime}; use crate::constant_strings_paths::TMP_FOLDER_PATH; -use crate::fm_error::{FmError, FmResult}; use crate::impl_selectable_content; use crate::opener::Opener; use crate::status::Status; @@ -23,7 +23,7 @@ pub struct Bulkrename<'a> { impl<'a> Bulkrename<'a> { /// Creates a new Bulkrename instance. - pub fn renamer(original_filepath: Vec<&'a Path>) -> FmResult { + pub fn renamer(original_filepath: Vec<&'a Path>) -> Result { let temp_file = Self::generate_random_filepath()?; Ok(Self { original_filepath: Some(original_filepath), @@ -32,7 +32,7 @@ impl<'a> Bulkrename<'a> { }) } - pub fn creator(path_str: &'a str) -> FmResult { + pub fn creator(path_str: &'a str) -> Result { let temp_file = Self::generate_random_filepath()?; info!("created {temp_file:?}"); Ok(Self { @@ -46,7 +46,7 @@ impl<'a> Bulkrename<'a> { /// The tempory file is opened with our `Opener` crate, allowing us /// to use the default text file editor. /// Filenames are sanitized before processing. - fn rename(&mut self, opener: &Opener) -> FmResult<()> { + fn rename(&mut self, opener: &Opener) -> Result<()> { self.write_original_names()?; let original_modification = Self::get_modified_date(&self.temp_file)?; self.open_temp_file_with_editor(opener)?; @@ -57,7 +57,7 @@ impl<'a> Bulkrename<'a> { self.delete_temp_file() } - fn create_files(&mut self, opener: &Opener) -> FmResult<()> { + fn create_files(&mut self, opener: &Opener) -> Result<()> { self.create_random_file()?; let original_modification = Self::get_modified_date(&self.temp_file)?; self.open_temp_file_with_editor(opener)?; @@ -71,7 +71,7 @@ impl<'a> Bulkrename<'a> { fn watch_modification_in_thread( filepath: &Path, original_modification: SystemTime, - ) -> FmResult<()> { + ) -> Result<()> { let filepath = filepath.to_owned(); let handle = thread::spawn(move || loop { if Self::is_file_modified(&filepath, original_modification).unwrap_or(true) { @@ -79,10 +79,13 @@ impl<'a> Bulkrename<'a> { } thread::sleep(Duration::from_millis(100)); }); - Ok(handle.join()?) + match handle.join() { + Ok(handle) => Ok(handle), + Err(e) => Err(anyhow!("watching thread failed {e:?}")), + } } - fn get_modified_date(filepath: &Path) -> FmResult { + fn get_modified_date(filepath: &Path) -> Result { Ok(std::fs::metadata(filepath)?.modified()?) } @@ -97,18 +100,18 @@ impl<'a> Bulkrename<'a> { rand_str } - fn generate_random_filepath() -> FmResult { + fn generate_random_filepath() -> Result { let mut filepath = PathBuf::from(&TMP_FOLDER_PATH); filepath.push(Self::random_name()); Ok(filepath) } - fn create_random_file(&self) -> FmResult<()> { + fn create_random_file(&self) -> Result<()> { std::fs::File::create(&self.temp_file)?; Ok(()) } - fn write_original_names(&self) -> FmResult<()> { + fn write_original_names(&self) -> Result<()> { let mut file = std::fs::File::create(&self.temp_file)?; for path in self.original_filepath.clone().unwrap().iter() { @@ -121,20 +124,17 @@ impl<'a> Bulkrename<'a> { Ok(()) } - fn open_temp_file_with_editor(&self, opener: &Opener) -> FmResult<()> { + fn open_temp_file_with_editor(&self, opener: &Opener) -> Result<()> { info!("opening tempory file {:?}", self.temp_file); opener.open(&self.temp_file) } - fn is_file_modified( - path: &Path, - original_modification: std::time::SystemTime, - ) -> FmResult { + fn is_file_modified(path: &Path, original_modification: std::time::SystemTime) -> Result { let last_modification = Self::get_modified_date(path)?; Ok(last_modification > original_modification) } - fn get_new_filenames(&self) -> FmResult> { + fn get_new_filenames(&self) -> Result> { let file = std::fs::File::open(&self.temp_file)?; let reader = std::io::BufReader::new(file); @@ -146,18 +146,18 @@ impl<'a> Bulkrename<'a> { .collect(); if let Some(original_filepath) = self.original_filepath.clone() { if new_names.len() < original_filepath.len() { - return Err(FmError::custom("new filenames", "not enough filenames")); + return Err(anyhow!("new filenames: not enough filenames")); } } Ok(new_names) } - fn delete_temp_file(&self) -> FmResult<()> { + fn delete_temp_file(&self) -> Result<()> { std::fs::remove_file(&self.temp_file)?; Ok(()) } - fn rename_all(&self, new_filenames: Vec) -> FmResult<()> { + fn rename_all(&self, new_filenames: Vec) -> Result<()> { let mut counter = 0; for (path, filename) in self .original_filepath @@ -175,7 +175,7 @@ impl<'a> Bulkrename<'a> { Ok(()) } - fn create_all_files(&self, new_filenames: &[String]) -> FmResult<()> { + fn create_all_files(&self, new_filenames: &[String]) -> Result<()> { let mut counter = 0; for filename in new_filenames.iter() { let mut new_path = std::path::PathBuf::from(self.parent_dir.unwrap()); @@ -202,7 +202,7 @@ impl<'a> Bulkrename<'a> { Ok(()) } - fn rename_file(&self, path: &Path, filename: &str) -> FmResult<()> { + fn rename_file(&self, path: &Path, filename: &str) -> Result<()> { let mut parent = PathBuf::from(path); parent.pop(); std::fs::rename(path, parent.join(filename))?; @@ -232,7 +232,7 @@ impl Bulk { /// First method is a rename of selected files, /// Second is the creation of files, /// Third is the creation of folders. - pub fn execute_bulk(&self, status: &Status) -> FmResult<()> { + pub fn execute_bulk(&self, status: &Status) -> Result<()> { match self.index { 0 => Bulkrename::renamer(status.filtered_flagged_files())?.rename(&status.opener), 1 => Bulkrename::creator(status.selected_path_str())?.create_files(&status.opener), diff --git a/src/completion.rs b/src/completion.rs index a97775e..1597503 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -1,9 +1,9 @@ use std::fs::{self, ReadDir}; +use anyhow::Result; use strum::IntoEnumIterator; use crate::fileinfo::PathContent; -use crate::fm_error::FmResult; use crate::mode::Mode; use crate::tree::ColoredString; @@ -100,7 +100,7 @@ impl Completion { /// Goto completion. /// Looks for the valid path completing what the user typed. - pub fn goto(&mut self, input_string: &str, current_path: &str) -> FmResult<()> { + pub fn goto(&mut self, input_string: &str, current_path: &str) -> Result<()> { self.update_from_input(input_string, current_path); let (parent, last_name) = split_input_string(input_string); if last_name.is_empty() { @@ -151,7 +151,7 @@ impl Completion { } /// Looks for programs in $PATH completing the one typed by the user. - pub fn exec(&mut self, input_string: &str) -> FmResult<()> { + pub fn exec(&mut self, input_string: &str) -> Result<()> { let mut proposals: Vec = vec![]; if let Some(paths) = std::env::var_os("PATH") { for path in std::env::split_paths(&paths).filter(|path| path.exists()) { @@ -162,7 +162,7 @@ impl Completion { Ok(()) } - pub fn command(&mut self, input_string: &str) -> FmResult<()> { + pub fn command(&mut self, input_string: &str) -> Result<()> { let proposals = crate::action_map::ActionMap::iter() .filter(|command| { command @@ -179,7 +179,7 @@ impl Completion { fn find_completion_in_path( path: std::path::PathBuf, input_string: &str, - ) -> FmResult> { + ) -> Result> { Ok(fs::read_dir(path)? .filter_map(|e| e.ok()) .filter(|e| file_match_input(e, input_string)) @@ -192,7 +192,7 @@ impl Completion { &mut self, input_string: &str, path_content: &PathContent, - ) -> FmResult<()> { + ) -> Result<()> { self.update( path_content .content @@ -209,7 +209,7 @@ impl Completion { &mut self, input_string: &str, content: &[(String, ColoredString)], - ) -> FmResult<()> { + ) -> Result<()> { self.update( content .iter() diff --git a/src/compress.rs b/src/compress.rs index ac3ca50..c41cd51 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -2,7 +2,8 @@ use std::fmt::Display; use std::io::prelude::*; use std::io::Write; -use crate::fm_error::FmResult; +use anyhow::Result; + use crate::impl_selectable_content; use flate2::write::{DeflateEncoder, GzEncoder, ZlibEncoder}; use flate2::Compression; @@ -55,7 +56,7 @@ impl Default for Compresser { impl Compresser { /// Archive the files with tar and compress them with the selected method. /// The compression method is chosen by the user. - pub fn compress(&self, files: Vec) -> FmResult<()> { + pub fn compress(&self, files: Vec) -> Result<()> { let Some(selected) = self.selected() else { return Ok(()) }; match selected { CompressionMethod::DEFLATE => Self::compress_deflate("archive.tar.gz", files), @@ -66,7 +67,7 @@ impl Compresser { } } - fn make_tar(files: Vec, mut archive: tar::Builder) -> FmResult<()> + fn make_tar(files: Vec, mut archive: tar::Builder) -> Result<()> where W: Write, { @@ -80,7 +81,7 @@ impl Compresser { Ok(()) } - fn compress_gzip(archive_name: &str, files: Vec) -> FmResult<()> { + fn compress_gzip(archive_name: &str, files: Vec) -> Result<()> { let compressed_file = std::fs::File::create(archive_name)?; let mut encoder = GzEncoder::new(compressed_file, Compression::default()); @@ -93,7 +94,7 @@ impl Compresser { Ok(()) } - fn compress_deflate(archive_name: &str, files: Vec) -> FmResult<()> { + fn compress_deflate(archive_name: &str, files: Vec) -> Result<()> { let compressed_file = std::fs::File::create(archive_name)?; let mut encoder = DeflateEncoder::new(compressed_file, Compression::default()); @@ -106,7 +107,7 @@ impl Compresser { Ok(()) } - fn compress_zlib(archive_name: &str, files: Vec) -> FmResult<()> { + fn compress_zlib(archive_name: &str, files: Vec) -> Result<()> { let compressed_file = std::fs::File::create(archive_name)?; let mut encoder = ZlibEncoder::new(compressed_file, Compression::default()); @@ -119,7 +120,7 @@ impl Compresser { Ok(()) } - fn compress_lzma(archive_name: &str, files: Vec) -> FmResult<()> { + fn compress_lzma(archive_name: &str, files: Vec) -> Result<()> { let compressed_file = std::fs::File::create(archive_name)?; let mut encoder = LzmaWriter::new_compressor(compressed_file, 6)?; @@ -132,7 +133,7 @@ impl Compresser { Ok(()) } - fn compress_zip(archive_name: &str, files: Vec) -> FmResult<()> { + fn compress_zip(archive_name: &str, files: Vec) -> Result<()> { let archive = std::fs::File::create(archive_name).unwrap(); let mut zip = zip::ZipWriter::new(archive); for file in files.iter() { diff --git a/src/config.rs b/src/config.rs index bf5bb44..6d740b4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,11 +1,11 @@ use std::{fs::File, path}; +use anyhow::Result; use serde_yaml; use tuikit::attr::Color; use crate::color_cache::ColorCache; use crate::constant_strings_paths::DEFAULT_TERMINAL_APPLICATION; -use crate::fm_error::FmResult; use crate::keybindings::Bindings; use crate::utils::is_program_in_path; @@ -25,7 +25,7 @@ pub struct Config { impl Config { /// Returns a default config with hardcoded values. - fn new() -> FmResult { + fn new() -> Result { Ok(Self { colors: Colors::default(), terminal: DEFAULT_TERMINAL_APPLICATION.to_owned(), @@ -33,7 +33,7 @@ impl Config { }) } /// Updates the config from a configuration content. - fn update_from_config(&mut self, yaml: &serde_yaml::value::Value) -> FmResult<()> { + fn update_from_config(&mut self, yaml: &serde_yaml::value::Value) -> Result<()> { self.colors.update_from_config(&yaml["colors"]); self.binds.update_from_config(&yaml["keys"])?; self.terminal = Self::set_terminal(&yaml["terminal"])?; @@ -43,7 +43,7 @@ impl Config { /// First we try to use the current terminal. If it's a fake one (ie. inside neovim float term), /// we look for the configured one, /// else we use the hardcoded one. - fn set_terminal(yaml: &serde_yaml::value::Value) -> FmResult { + fn set_terminal(yaml: &serde_yaml::value::Value) -> Result { let terminal_currently_used = std::env::var("TERM").unwrap_or_default(); if !terminal_currently_used.is_empty() && is_program_in_path(&terminal_currently_used) { Ok(terminal_currently_used) @@ -149,7 +149,7 @@ pub fn str_to_tuikit(color: &str) -> Color { /// 1. hardcoded values /// /// 2. configured values from `~/.config/fm/config_file_name.yaml` if those files exists. -pub fn load_config(path: &str) -> FmResult { +pub fn load_config(path: &str) -> Result { let mut config = Config::new()?; if let Ok(file) = File::open(path::Path::new(&shellexpand::tilde(path).to_string())) { diff --git a/src/copy_move.rs b/src/copy_move.rs index 4244236..2179acb 100644 --- a/src/copy_move.rs +++ b/src/copy_move.rs @@ -3,21 +3,20 @@ use std::path::PathBuf; use std::sync::Arc; use std::thread; +use anyhow::Result; use fs_extra; use indicatif::{InMemoryTerm, ProgressBar, ProgressDrawTarget, ProgressState, ProgressStyle}; use log::info; -// use notify_rust::Notification; use tuikit::prelude::{Attr, Color, Effect, Event, Term}; use crate::fileinfo::human_size; -use crate::fm_error::FmResult; use crate::opener::execute_in_child; fn setup( action: String, height: usize, width: usize, -) -> FmResult<(InMemoryTerm, ProgressBar, fs_extra::dir::CopyOptions)> { +) -> Result<(InMemoryTerm, ProgressBar, fs_extra::dir::CopyOptions)> { let in_mem = InMemoryTerm::new(height as u16, width as u16); let pb = ProgressBar::with_draw_target( Some(100), @@ -93,7 +92,7 @@ pub fn copy_move( sources: Vec, dest: &str, term: Arc, -) -> FmResult<()> { +) -> Result<()> { let c_term = term.clone(); let (height, width) = term.term_size()?; let (in_mem, pb, options) = setup(copy_or_move.verb().to_owned(), height, width)?; @@ -137,7 +136,7 @@ pub fn copy_move( /// Send a notification to the desktop. /// Requires "notify-send" to be installed. -fn notify(text: &str) -> FmResult<()> { +fn notify(text: &str) -> Result<()> { execute_in_child("notify-send", &vec![text])?; Ok(()) } diff --git a/src/cryptsetup.rs b/src/cryptsetup.rs index bfe2fa6..c9dd779 100644 --- a/src/cryptsetup.rs +++ b/src/cryptsetup.rs @@ -1,10 +1,10 @@ use std::io::Write; use std::process::{Command, Stdio}; +use anyhow::{anyhow, Context, Result}; use log::info; use sysinfo::{DiskExt, System, SystemExt}; -use crate::fm_error::{FmError, FmResult}; use crate::impl_selectable_content; use crate::utils::current_username; @@ -49,17 +49,17 @@ impl PasswordHolder { } /// Reads the cryptsetup password - fn cryptsetup(&self) -> FmResult { + fn cryptsetup(&self) -> Result { self.cryptsetup .clone() - .ok_or_else(|| FmError::custom("PasswordHolder", "cryptsetup password isn't set")) + .context("PasswordHolder: cryptsetup password isn't set") } /// Reads the sudo password - fn sudo(&self) -> FmResult { + fn sudo(&self) -> Result { self.sudo .clone() - .ok_or_else(|| FmError::custom("PasswordHolder", "sudo password isn't set")) + .context("PasswordHolder: sudo password isn't set") } fn has_sudo(&self) -> bool { @@ -82,7 +82,7 @@ impl PasswordHolder { /// lsblk -l -o FSTYPE,PATH,UUID,FSVER,MOUNTPOINT,PARTLABEL /// ``` /// as a String. -fn get_devices() -> FmResult { +fn get_devices() -> Result { let output = Command::new("lsblk") .args(&vec!["-l", "-o", "FSTYPE,PATH,UUID,FSVER,MOUNTPOINT"]) .stdin(Stdio::null()) @@ -103,7 +103,7 @@ fn filter_crypto_devices_lines(output: String, key: &str) -> Vec { /// run a sudo command requiring a password (generally to establish the password.) /// Since I can't send 2 passwords at a time, it will only work with the sudo password -fn sudo_password(args: &[String], password: &str) -> FmResult<(bool, String, String)> { +fn sudo_password(args: &[String], password: &str) -> Result<(bool, String, String)> { info!("sudo {:?}", args); let mut child = Command::new("sudo") .args(args) @@ -115,7 +115,7 @@ fn sudo_password(args: &[String], password: &str) -> FmResult<(bool, String, Str let child_stdin = child .stdin .as_mut() - .ok_or_else(|| FmError::custom("run_privileged_command", "couldn't open child stdin"))?; + .context("run_privileged_command: couldn't open child stdin")?; child_stdin.write_all(format!("{password}\n").as_bytes())?; let output = child.wait_with_output()?; @@ -128,7 +128,7 @@ fn sudo_password(args: &[String], password: &str) -> FmResult<(bool, String, Str /// Run a passwordless sudo command. /// Returns stdout & stderr -fn sudo(args: &[String]) -> FmResult<(bool, String, String)> { +fn sudo(args: &[String]) -> Result<(bool, String, String)> { info!("sudo {:?}", args); let child = Command::new("sudo") .args(args) @@ -158,13 +158,13 @@ pub struct CryptoDevice { impl CryptoDevice { /// Parse the output of a lsblk formated line into a struct - fn from_line(line: &str) -> FmResult { + fn from_line(line: &str) -> Result { let mut crypo_device = Self::default(); crypo_device.update_from_line(line)?; Ok(crypo_device) } - fn update_from_line(&mut self, line: &str) -> FmResult<()> { + fn update_from_line(&mut self, line: &str) -> Result<()> { let strings = line.split_whitespace(); let mut params: Vec> = vec![None; 5]; for (count, param) in strings.enumerate() { @@ -172,16 +172,16 @@ impl CryptoDevice { } self.fs_type = params .remove(0) - .ok_or_else(|| FmError::custom("CryptoDevice", "parameter shouldn't be None"))?; + .context("CryptoDevice: parameter shouldn't be None")?; self.path = params .remove(0) - .ok_or_else(|| FmError::custom("CryptoDevice", "parameter shouldn't be None"))?; + .context("CryptoDevice: parameter shouldn't be None")?; self.uuid = params .remove(0) - .ok_or_else(|| FmError::custom("CryptoDevice", "parameter shouldn't be None"))?; + .context("CryptoDevice: parameter shouldn't be None")?; self.fs_ver = params .remove(0) - .ok_or_else(|| FmError::custom("CryptoDevice", "parameter shouldn't be None"))?; + .context("CryptoDevice: parameter shouldn't be None")?; self.mountpoints = params.remove(0); Ok(()) } @@ -265,7 +265,7 @@ impl CryptoDevice { self.mount_point().is_some() } - fn set_device_name(&mut self) -> FmResult<()> { + fn set_device_name(&mut self) -> Result<()> { let child = Command::new("lsblk") .arg("-l") .arg("-n") @@ -285,7 +285,7 @@ impl CryptoDevice { self.device_name = Some( s.split_whitespace() .next() - .ok_or_else(|| FmError::custom("mapped point", "shouldn't be empty"))? + .context("mapped point: shouldn't be empty")? .to_owned(), ); } else { @@ -295,7 +295,7 @@ impl CryptoDevice { } /// String representation of the device. - pub fn as_string(&self) -> FmResult { + pub fn as_string(&self) -> Result { Ok(if let Some(mount_point) = self.mount_point() { format!("{} -> {}", self.path, mount_point) } else { @@ -303,13 +303,10 @@ impl CryptoDevice { }) } - fn open_mount(&mut self, username: &str, passwords: &mut PasswordHolder) -> FmResult { + fn open_mount(&mut self, username: &str, passwords: &mut PasswordHolder) -> Result { self.set_device_name()?; if self.is_mounted() { - Err(FmError::custom( - "luks open mount", - "device is already mounted", - )) + Err(anyhow!("luks open mount: device is already mounted")) } else { // sudo let (success, _, _) = sudo_password( @@ -345,7 +342,7 @@ impl CryptoDevice { } } - fn umount_close(&mut self, username: &str, passwords: &mut PasswordHolder) -> FmResult { + fn umount_close(&mut self, username: &str, passwords: &mut PasswordHolder) -> Result { self.set_device_name()?; // sudo let (success, _, _) = sudo_password( @@ -380,7 +377,7 @@ pub struct Device { impl Device { /// Reads a device from a line of text from cryptsetup. - pub fn from_line(line: &str) -> FmResult { + pub fn from_line(line: &str) -> Result { Ok(Self { cryptdevice: CryptoDevice::from_line(line)?, password_holder: PasswordHolder::default(), @@ -399,7 +396,7 @@ pub struct DeviceOpener { impl DeviceOpener { /// Updates itself from the output of cryptsetup. - pub fn update(&mut self) -> FmResult<()> { + pub fn update(&mut self) -> Result<()> { self.content = filter_crypto_devices_lines(get_devices()?, "crypto") .iter() .map(|line| Device::from_line(line)) @@ -420,7 +417,7 @@ impl DeviceOpener { } /// Open and mount the selected device. - pub fn mount_selected(&mut self) -> FmResult<()> { + pub fn mount_selected(&mut self) -> Result<()> { let username = current_username()?; let mut passwords = self.content[self.index].password_holder.clone(); let success = self.content[self.index] @@ -435,7 +432,7 @@ impl DeviceOpener { } /// Unmount and close the selected device. - pub fn umount_selected(&mut self) -> FmResult<()> { + pub fn umount_selected(&mut self) -> Result<()> { let username = current_username()?; let mut passwords = self.content[self.index].password_holder.clone(); let success = self.content[self.index] @@ -449,7 +446,7 @@ impl DeviceOpener { Ok(()) } - fn reset_faillock() -> FmResult<()> { + fn reset_faillock() -> Result<()> { Command::new("faillock") .arg("--user") .arg(current_username()?) @@ -461,7 +458,7 @@ impl DeviceOpener { Ok(()) } - fn drop_sudo() -> FmResult<()> { + fn drop_sudo() -> Result<()> { Command::new("sudo") .arg("-k") .stdin(Stdio::null()) diff --git a/src/decompress.rs b/src/decompress.rs index d4bc001..8894abe 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -1,4 +1,4 @@ -use crate::fm_error::{FmError, FmResult}; +use anyhow::{Context, Result}; use flate2::read::{GzDecoder, ZlibDecoder}; use std::fs::File; use std::path::Path; @@ -7,13 +7,13 @@ use tar::Archive; /// Decompress a zipped compressed file into its parent directory. /// It may fail an return a `FmError` if the file has no parent, /// which should be impossible. -pub fn decompress_zip(source: &Path) -> FmResult<()> { +pub fn decompress_zip(source: &Path) -> Result<()> { let file = File::open(source)?; let mut zip = zip::ZipArchive::new(file)?; let parent = source .parent() - .ok_or_else(|| FmError::custom("decompress", "source should have a parent"))?; + .context("decompress: source should have a parent")?; zip.extract(parent)?; Ok(()) @@ -22,13 +22,13 @@ pub fn decompress_zip(source: &Path) -> FmResult<()> { /// Decompress a gz compressed file into its parent directory. /// It may fail an return a `FmError` if the file has no parent, /// which should be impossible. -pub fn decompress_gz(source: &Path) -> FmResult<()> { +pub fn decompress_gz(source: &Path) -> Result<()> { let tar_gz = File::open(source)?; let tar = GzDecoder::new(tar_gz); let mut archive = Archive::new(tar); let parent = source .parent() - .ok_or_else(|| FmError::custom("decompress", "source should have a parent"))?; + .context("decompress: source should have a parent")?; archive.unpack(parent)?; Ok(()) @@ -37,13 +37,13 @@ pub fn decompress_gz(source: &Path) -> FmResult<()> { /// Decompress a zlib compressed file into its parent directory. /// It may fail an return a `FmError` if the file has no parent, /// which should be impossible. -pub fn decompress_xz(source: &Path) -> FmResult<()> { +pub fn decompress_xz(source: &Path) -> Result<()> { let tar_xz = File::open(source)?; let tar = ZlibDecoder::new(tar_xz); let mut archive = Archive::new(tar); let parent = source .parent() - .ok_or_else(|| FmError::custom("decompress", "source should have a parent"))?; + .context("decompress: source should have a parent")?; archive.unpack(parent)?; Ok(()) @@ -51,7 +51,7 @@ pub fn decompress_xz(source: &Path) -> FmResult<()> { /// List files contained in a ZIP file. /// Will return an error if the ZIP file is corrupted. -pub fn list_files_zip

(source: P) -> FmResult> +pub fn list_files_zip

(source: P) -> Result> where P: AsRef, { diff --git a/src/event_dispatch.rs b/src/event_dispatch.rs index 164782b..d2ba90b 100644 --- a/src/event_dispatch.rs +++ b/src/event_dispatch.rs @@ -1,8 +1,8 @@ +use anyhow::Result; use tuikit::prelude::{Event, Key, MouseButton}; use crate::config::Colors; use crate::event_exec::EventExec; -use crate::fm_error::FmResult; use crate::keybindings::Bindings; use crate::mode::{InputSimple, MarkAction, Mode, Navigate}; use crate::status::Status; @@ -26,7 +26,7 @@ impl EventDispatcher { /// Only non keyboard events are dealt here directly. /// Keyboard events are configurable and are sent to specific functions /// which needs to know those keybindings. - pub fn dispatch(&self, status: &mut Status, ev: Event, colors: &Colors) -> FmResult<()> { + pub fn dispatch(&self, status: &mut Status, ev: Event, colors: &Colors) -> Result<()> { match ev { Event::Key(Key::WheelUp(_, col, _)) => { EventExec::event_select_pane(status, col)?; @@ -59,14 +59,14 @@ impl EventDispatcher { } } - fn key_matcher(&self, status: &mut Status, key: Key, colors: &Colors) -> FmResult<()> { + fn key_matcher(&self, status: &mut Status, key: Key, colors: &Colors) -> Result<()> { match self.binds.get(&key) { Some(action) => action.matcher(status, colors), None => Ok(()), } } - fn char(&self, status: &mut Status, key_char: Key, colors: &Colors) -> FmResult<()> { + fn char(&self, status: &mut Status, key_char: Key, colors: &Colors) -> Result<()> { match key_char { Key::Char(c) => match status.selected_non_mut().mode { Mode::InputSimple(InputSimple::Sort) => { diff --git a/src/event_exec.rs b/src/event_exec.rs index 34763d8..612c3ab 100644 --- a/src/event_exec.rs +++ b/src/event_exec.rs @@ -4,6 +4,7 @@ use std::fs; use std::path; use std::str::FromStr; +use anyhow::{anyhow, Context, Result}; use copypasta::{ClipboardContext, ClipboardProvider}; use log::info; use sysinfo::SystemExt; @@ -20,7 +21,6 @@ use crate::cryptsetup::EncryptedAction; use crate::cryptsetup::PasswordKind; use crate::fileinfo::FileKind; use crate::filter::FilterKind; -use crate::fm_error::{FmError, FmResult}; use crate::log::read_log; use crate::mocp::Mocp; use crate::mode::Navigate; @@ -40,7 +40,7 @@ pub struct EventExec {} impl EventExec { /// Reset the selected tab view to the default. - pub fn refresh_status(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn refresh_status(status: &mut Status, colors: &Colors) -> Result<()> { status.force_clear(); status.refresh_users()?; status.selected().refresh_view()?; @@ -54,12 +54,7 @@ impl EventExec { /// isn't sufficiant to display enough information. /// We also need to know the new height of the terminal to start scrolling /// up or down. - pub fn resize( - status: &mut Status, - width: usize, - height: usize, - colors: &Colors, - ) -> FmResult<()> { + pub fn resize(status: &mut Status, width: usize, height: usize, colors: &Colors) -> Result<()> { status.set_dual_pane_if_wide_enough(width)?; status.selected().set_height(height); Self::refresh_status(status, colors)?; @@ -67,13 +62,13 @@ impl EventExec { } /// Remove every flag on files in this directory and others. - pub fn event_clear_flags(status: &mut Status) -> FmResult<()> { + pub fn event_clear_flags(status: &mut Status) -> Result<()> { status.flagged.clear(); Ok(()) } /// Flag all files in the current directory. - pub fn event_flag_all(status: &mut Status) -> FmResult<()> { + pub fn event_flag_all(status: &mut Status) -> Result<()> { status.tabs[status.index] .path_content .content @@ -86,7 +81,7 @@ impl EventExec { /// Reverse every flag in _current_ directory. Flagged files in other /// directory aren't affected. - pub fn event_reverse_flags(status: &mut Status) -> FmResult<()> { + pub fn event_reverse_flags(status: &mut Status) -> Result<()> { status.tabs[status.index] .path_content .content @@ -96,7 +91,7 @@ impl EventExec { } /// Toggle a single flag and move down one row. - pub fn event_toggle_flag(status: &mut Status) -> FmResult<()> { + pub fn event_toggle_flag(status: &mut Status) -> Result<()> { let tab = status.selected_non_mut(); match tab.mode { @@ -126,7 +121,7 @@ impl EventExec { } /// Change to CHMOD mode allowing to edit permissions of a file. - pub fn event_chmod(status: &mut Status) -> FmResult<()> { + pub fn event_chmod(status: &mut Status) -> Result<()> { if status.selected().path_content.is_empty() { return Ok(()); } @@ -143,7 +138,7 @@ impl EventExec { /// Enter JUMP mode, allowing to jump to any flagged file. /// Does nothing if no file is flagged. - pub fn event_jump(status: &mut Status) -> FmResult<()> { + pub fn event_jump(status: &mut Status) -> Result<()> { if !status.flagged.is_empty() { status.flagged.index = 0; status.selected().set_mode(Mode::Navigate(Navigate::Jump)) @@ -152,13 +147,13 @@ impl EventExec { } /// Enter Marks new mode, allowing to bind a char to a path. - pub fn event_marks_new(tab: &mut Tab) -> FmResult<()> { + pub fn event_marks_new(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::Navigate(Navigate::Marks(MarkAction::New))); Ok(()) } /// Enter Marks jump mode, allowing to jump to a marked file. - pub fn event_marks_jump(status: &mut Status) -> FmResult<()> { + pub fn event_marks_jump(status: &mut Status) -> Result<()> { if status.marks.is_empty() { return Ok(()); } @@ -169,7 +164,7 @@ impl EventExec { } /// Jump to the current mark. - pub fn exec_marks_jump_selected(status: &mut Status) -> FmResult<()> { + pub fn exec_marks_jump_selected(status: &mut Status) -> Result<()> { let marks = status.marks.clone(); let tab = status.selected(); if let Some((_, path)) = marks.selected() { @@ -184,7 +179,7 @@ impl EventExec { /// Update the selected mark with the current path. /// Doesn't change its char. /// If it doesn't fail, a new pair will be set with (oldchar, new path). - pub fn exec_marks_update_selected(status: &mut Status) -> FmResult<()> { + pub fn exec_marks_update_selected(status: &mut Status) -> Result<()> { let marks = status.marks.clone(); let len = status.selected_non_mut().path_content.content.len(); if let Some((ch, _)) = marks.selected() { @@ -198,7 +193,7 @@ impl EventExec { } /// Execute a new mark, saving it to a config file for futher use. - pub fn exec_marks_new(status: &mut Status, c: char, colors: &Colors) -> FmResult<()> { + pub fn exec_marks_new(status: &mut Status, c: char, colors: &Colors) -> Result<()> { let path = status.selected().path_content.path.clone(); status.marks.new_mark(c, path)?; Self::event_normal(status.selected())?; @@ -208,7 +203,7 @@ impl EventExec { /// Execute a jump to a mark, moving to a valid path. /// If the saved path is invalid, it does nothing but reset the view. - pub fn exec_marks_jump(status: &mut Status, c: char, colors: &Colors) -> FmResult<()> { + pub fn exec_marks_jump(status: &mut Status, c: char, colors: &Colors) -> Result<()> { if let Some(path) = status.marks.get(c) { status.selected().set_pathcontent(&path)? } @@ -218,12 +213,12 @@ impl EventExec { } /// Creates a symlink of every flagged file to the current directory. - pub fn event_symlink(status: &mut Status) -> FmResult<()> { + pub fn event_symlink(status: &mut Status) -> Result<()> { for original_file in status.flagged.content.iter() { let filename = original_file .as_path() .file_name() - .ok_or_else(|| FmError::custom("event symlink", "File not found"))?; + .context("event symlink: File not found")?; let link = status .selected_non_mut() .directory_of_selected()? @@ -237,7 +232,7 @@ impl EventExec { /// Enter bulkrename mode, opening a random temp file where the user /// can edit the selected filenames. /// Once the temp file is saved, those file names are changed. - pub fn event_bulk(status: &mut Status) -> FmResult<()> { + pub fn event_bulk(status: &mut Status) -> Result<()> { status.selected().set_mode(Mode::Navigate(Navigate::Bulk)); Ok(()) } @@ -260,28 +255,28 @@ impl EventExec { status.shell_menu.next() } - pub fn exec_bulk(status: &mut Status) -> FmResult<()> { + pub fn exec_bulk(status: &mut Status) -> Result<()> { status.bulk.execute_bulk(status) } - pub fn exec_shellmenu(status: &mut Status) -> FmResult<()> { + pub fn exec_shellmenu(status: &mut Status) -> Result<()> { status.shell_menu.execute(status) } /// Copy the flagged file to current directory. /// A progress bar is displayed and a notification is sent once it's done. - pub fn exec_copy_paste(status: &mut Status) -> FmResult<()> { + pub fn exec_copy_paste(status: &mut Status) -> Result<()> { status.cut_or_copy_flagged_files(CopyMove::Copy) } /// Move the flagged file to current directory. /// A progress bar is displayed and a notification is sent once it's done. - pub fn exec_cut_paste(status: &mut Status) -> FmResult<()> { + pub fn exec_cut_paste(status: &mut Status) -> Result<()> { status.cut_or_copy_flagged_files(CopyMove::Move) } /// Recursively delete all flagged files. - pub fn exec_delete_files(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn exec_delete_files(status: &mut Status, colors: &Colors) -> Result<()> { for pathbuf in status.flagged.content.iter() { if pathbuf.is_dir() { std::fs::remove_dir_all(pathbuf)?; @@ -299,7 +294,7 @@ impl EventExec { /// the file. /// Nothing is done if the user typed nothing or an invalid permission like /// 955. - pub fn exec_chmod(status: &mut Status) -> FmResult<()> { + pub fn exec_chmod(status: &mut Status) -> Result<()> { if status.selected().input.is_empty() { return Ok(()); } @@ -315,7 +310,7 @@ impl EventExec { status.reset_tabs_view() } - pub fn exec_set_neovim_address(status: &mut Status) -> FmResult<()> { + pub fn exec_set_neovim_address(status: &mut Status) -> Result<()> { status.nvim_server = status.selected_non_mut().input.string(); status.selected().reset_mode(); Ok(()) @@ -324,7 +319,7 @@ impl EventExec { /// Execute a jump to the selected flagged file. /// If the user selected a directory, we jump inside it. /// Otherwise, we jump to the parent and select the file. - pub fn exec_jump(status: &mut Status) -> FmResult<()> { + pub fn exec_jump(status: &mut Status) -> Result<()> { let Some(jump_target) = status.flagged.selected() else { return Ok(()) }; let jump_target = jump_target.to_owned(); let target_dir = match jump_target.parent() { @@ -349,7 +344,7 @@ impl EventExec { status: &mut Status, confirmed_action: NeedConfirmation, colors: &Colors, - ) -> FmResult<()> { + ) -> Result<()> { Self::_exec_confirmed_action(status, confirmed_action, colors)?; status.selected().set_mode(Mode::Normal); Ok(()) @@ -359,7 +354,7 @@ impl EventExec { status: &mut Status, confirmed_action: NeedConfirmation, colors: &Colors, - ) -> FmResult<()> { + ) -> Result<()> { match confirmed_action { NeedConfirmation::Delete => Self::exec_delete_files(status, colors), NeedConfirmation::Move => Self::exec_cut_paste(status), @@ -377,13 +372,13 @@ impl EventExec { /// Leave current mode to normal mode. /// Reset the inputs and completion, reset the window, exit the preview. - pub fn event_reset_mode(tab: &mut Tab) -> FmResult<()> { + pub fn event_reset_mode(tab: &mut Tab) -> Result<()> { tab.reset_mode(); tab.refresh_view() } /// Reset the inputs and completion, reset the window, exit the preview. - pub fn event_normal(tab: &mut Tab) -> FmResult<()> { + pub fn event_normal(tab: &mut Tab) -> Result<()> { tab.refresh_view() } @@ -499,7 +494,7 @@ impl EventExec { } /// Select the left or right tab depending on where the user clicked. - pub fn event_select_pane(status: &mut Status, col: u16) -> FmResult<()> { + pub fn event_select_pane(status: &mut Status, col: u16) -> Result<()> { let (width, _) = status.term_size()?; if (col as usize) < width / 2 { status.select_tab(0)?; @@ -510,7 +505,7 @@ impl EventExec { } /// Select a given row, if there's something in it. - pub fn event_select_row(status: &mut Status, row: u16, colors: &Colors) -> FmResult<()> { + pub fn event_select_row(status: &mut Status, row: u16, colors: &Colors) -> Result<()> { let tab = status.selected(); match tab.mode { Mode::Normal => { @@ -568,7 +563,7 @@ impl EventExec { /// Move to parent directory if there's one. /// Does /// Add the starting directory to history. - pub fn event_move_to_parent(tab: &mut Tab) -> FmResult<()> { + pub fn event_move_to_parent(tab: &mut Tab) -> Result<()> { tab.move_to_parent() } @@ -578,7 +573,7 @@ impl EventExec { } /// Open the file with configured opener or enter the directory. - pub fn exec_file(status: &mut Status) -> FmResult<()> { + pub fn exec_file(status: &mut Status) -> Result<()> { let tab = status.selected(); if tab.path_content.is_empty() { return Ok(()); @@ -606,7 +601,7 @@ impl EventExec { } /// Add a char to input string, look for a possible completion. - pub fn event_text_insert_and_complete(tab: &mut Tab, c: char) -> FmResult<()> { + pub fn event_text_insert_and_complete(tab: &mut Tab, c: char) -> Result<()> { Self::event_text_insertion(tab, c); tab.fill_completion() } @@ -615,7 +610,7 @@ impl EventExec { /// A confirmation is asked before copying all flagged files to /// the current directory. /// Does nothing if no file is flagged. - pub fn event_copy_paste(status: &mut Status) -> FmResult<()> { + pub fn event_copy_paste(status: &mut Status) -> Result<()> { if status.flagged.is_empty() { return Ok(()); } @@ -629,7 +624,7 @@ impl EventExec { /// A confirmation is asked before moving all flagged files to /// the current directory. /// Does nothing if no file is flagged. - pub fn event_cut_paste(status: &mut Status) -> FmResult<()> { + pub fn event_cut_paste(status: &mut Status) -> Result<()> { if status.flagged.is_empty() { return Ok(()); } @@ -640,20 +635,20 @@ impl EventExec { } /// Enter the new dir mode. - pub fn event_new_dir(tab: &mut Tab) -> FmResult<()> { + pub fn event_new_dir(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputSimple(InputSimple::Newdir)); Ok(()) } /// Enter the new file mode. - pub fn event_new_file(tab: &mut Tab) -> FmResult<()> { + pub fn event_new_file(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputSimple(InputSimple::Newfile)); Ok(()) } /// Enter the execute mode. Most commands must be executed to allow for /// a confirmation. - pub fn event_exec(tab: &mut Tab) -> FmResult<()> { + pub fn event_exec(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputCompleted(InputCompleted::Exec)); Ok(()) } @@ -662,7 +657,7 @@ impl EventExec { /// Every file can be previewed. See the `crate::enum::Preview` for /// more details on previewinga file. /// Does nothing if the directory is empty. - pub fn event_preview(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn event_preview(status: &mut Status, colors: &Colors) -> Result<()> { if status.selected_non_mut().path_content.is_empty() { return Ok(()); } @@ -691,7 +686,7 @@ impl EventExec { /// Enter the delete mode. /// A confirmation is then asked before deleting all the flagged files. /// Does nothing is no file is flagged. - pub fn event_delete_file(status: &mut Status) -> FmResult<()> { + pub fn event_delete_file(status: &mut Status) -> Result<()> { if status.flagged.is_empty() { return Ok(()); } @@ -703,7 +698,7 @@ impl EventExec { /// Display the help which can be navigated and displays the configrable /// binds. - pub fn event_help(status: &mut Status) -> FmResult<()> { + pub fn event_help(status: &mut Status) -> Result<()> { let help = status.help.clone(); let tab = status.selected(); tab.set_mode(Mode::Preview); @@ -713,7 +708,7 @@ impl EventExec { } /// Display the last actions impacting the file tree - pub fn event_log(tab: &mut Tab) -> FmResult<()> { + pub fn event_log(tab: &mut Tab) -> Result<()> { let log = read_log()?; tab.set_mode(Mode::Preview); tab.preview = Preview::log(log); @@ -724,7 +719,7 @@ impl EventExec { /// Enter the search mode. /// Matching items are displayed as you type them. - pub fn event_search(tab: &mut Tab) -> FmResult<()> { + pub fn event_search(tab: &mut Tab) -> Result<()> { tab.searched = None; tab.set_mode(Mode::InputCompleted(InputCompleted::Search)); Ok(()) @@ -732,7 +727,7 @@ impl EventExec { /// Enter the regex mode. /// Every file matching the typed regex will be flagged. - pub fn event_regex_match(tab: &mut Tab) -> FmResult<()> { + pub fn event_regex_match(tab: &mut Tab) -> Result<()> { match tab.mode { Mode::Tree => (), _ => tab.set_mode(Mode::InputSimple(InputSimple::RegexMatch)), @@ -741,14 +736,14 @@ impl EventExec { } /// Enter the sort mode, allowing the user to select a sort method. - pub fn event_sort(tab: &mut Tab) -> FmResult<()> { + pub fn event_sort(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputSimple(InputSimple::Sort)); Ok(()) } /// Once a quit event is received, we change a flag and break the main loop. /// It's usefull to reset the cursor before leaving the application. - pub fn event_quit(tab: &mut Tab) -> FmResult<()> { + pub fn event_quit(tab: &mut Tab) -> Result<()> { if let Mode::Tree = tab.mode { Self::event_normal(tab)?; tab.set_mode(Mode::Normal) @@ -773,7 +768,7 @@ impl EventExec { /// by extension. /// The first letter is used to identify the method. /// If the user types an uppercase char, the sort is reverse. - pub fn event_leave_sort(status: &mut Status, c: char, colors: &Colors) -> FmResult<()> { + pub fn event_leave_sort(status: &mut Status, c: char, colors: &Colors) -> Result<()> { if status.selected_non_mut().path_content.content.is_empty() { return Ok(()); } @@ -804,7 +799,7 @@ impl EventExec { } /// Toggle the display of hidden files. - pub fn event_toggle_hidden(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn event_toggle_hidden(status: &mut Status, colors: &Colors) -> Result<()> { let tab = status.selected(); tab.show_hidden = !tab.show_hidden; tab.path_content.reset_files(&tab.filter, tab.show_hidden)?; @@ -816,12 +811,12 @@ impl EventExec { } /// Open a file with custom opener. - pub fn event_open_file(status: &mut Status) -> FmResult<()> { + pub fn event_open_file(status: &mut Status) -> Result<()> { match status.opener.open( &status .selected_non_mut() .selected() - .ok_or_else(|| FmError::custom("event open file", "Empty directory"))? + .context("event open file, Empty directory")? .path, ) { Ok(_) => (), @@ -838,7 +833,7 @@ impl EventExec { /// Keep a track of the current mode to ensure we rename the correct file. /// When we enter rename from a "tree" mode, we'll need to rename the selected file in the tree, /// not the selected file in the pathcontent. - pub fn event_rename(tab: &mut Tab) -> FmResult<()> { + pub fn event_rename(tab: &mut Tab) -> Result<()> { if tab.selected().is_some() { tab.set_mode(Mode::InputSimple(InputSimple::Rename)); } @@ -846,7 +841,7 @@ impl EventExec { } /// Enter the goto mode where an user can type a path to jump to. - pub fn event_goto(tab: &mut Tab) -> FmResult<()> { + pub fn event_goto(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputCompleted(InputCompleted::Goto)); tab.completion.reset(); Ok(()) @@ -855,7 +850,7 @@ impl EventExec { /// Open a new terminal in current directory. /// The shell is a fork of current process and will exit if the application /// is terminated first. - pub fn event_shell(status: &mut Status) -> FmResult<()> { + pub fn event_shell(status: &mut Status) -> Result<()> { let tab = status.selected_non_mut(); let path = tab.directory_of_selected()?; execute_in_child_without_output_with_path(&status.opener.terminal, path, None)?; @@ -864,13 +859,13 @@ impl EventExec { /// Enter the history mode, allowing to navigate to previously visited /// directory. - pub fn event_shell_menu(tab: &mut Tab) -> FmResult<()> { + pub fn event_shell_menu(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::Navigate(Navigate::ShellMenu)); Ok(()) } /// Enter the history mode, allowing to navigate to previously visited /// directory. - pub fn event_history(tab: &mut Tab) -> FmResult<()> { + pub fn event_history(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::Navigate(Navigate::History)); Ok(()) } @@ -878,7 +873,7 @@ impl EventExec { /// Enter the shortcut mode, allowing to visit predefined shortcuts. /// Basic folders (/, /dev... $HOME) and mount points (even impossible to /// visit ones) are proposed. - pub fn event_shortcut(tab: &mut Tab) -> FmResult<()> { + pub fn event_shortcut(tab: &mut Tab) -> Result<()> { std::env::set_current_dir(tab.current_path())?; tab.shortcut.update_git_root(); tab.set_mode(Mode::Navigate(Navigate::Shortcut)); @@ -886,7 +881,7 @@ impl EventExec { } /// A right click opens a file or a directory. - pub fn event_right_click(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn event_right_click(status: &mut Status, colors: &Colors) -> Result<()> { match status.selected().mode { Mode::Normal => Self::exec_file(status), Mode::Tree => Self::exec_tree(status, colors), @@ -903,7 +898,7 @@ impl EventExec { /// If no RPC server were provided at launch time - which may happen for /// reasons unknow to me - it does nothing. /// It requires the "nvim-send" application to be in $PATH. - pub fn event_nvim_filepicker(status: &mut Status) -> FmResult<()> { + pub fn event_nvim_filepicker(status: &mut Status) -> Result<()> { if !is_program_in_path(NVIM_RPC_SENDER) { return Ok(()); }; @@ -920,7 +915,7 @@ impl EventExec { Ok(()) } - pub fn event_set_nvim_server(status: &mut Status) -> FmResult<()> { + pub fn event_set_nvim_server(status: &mut Status) -> Result<()> { status .selected() .set_mode(Mode::InputSimple(InputSimple::SetNvimAddress)); @@ -940,31 +935,31 @@ impl EventExec { } /// Copy the selected filename to the clipboard. Only the filename. - pub fn event_filename_to_clipboard(tab: &Tab) -> FmResult<()> { + pub fn event_filename_to_clipboard(tab: &Tab) -> Result<()> { Self::set_clipboard( tab.selected() - .ok_or_else(|| FmError::custom("event_filename_to_clipboard", "no selected file"))? + .context("event_filename_to_clipboard: no selected file")? .filename .clone(), ) } /// Copy the selected filepath to the clipboard. The absolute path. - pub fn event_filepath_to_clipboard(tab: &Tab) -> FmResult<()> { + pub fn event_filepath_to_clipboard(tab: &Tab) -> Result<()> { Self::set_clipboard( tab.selected() - .ok_or_else(|| FmError::custom("event_filepath_to_clipboard", "no selected file"))? + .context("event_filepath_to_clipboard: no selected file")? .path .to_str() - .ok_or_else(|| FmError::custom("event_filepath_to_clipboard", "no selected file"))? + .context("event_filepath_to_clipboard: no selected file")? .to_owned(), ) } - fn set_clipboard(content: String) -> FmResult<()> { + fn set_clipboard(content: String) -> Result<()> { info!("copied to clipboard: {}", content); - let mut ctx = ClipboardContext::new()?; - ctx.set_contents(content)?; + let Ok(mut ctx) = ClipboardContext::new() else { return Ok(()); }; + let Ok(_) = ctx.set_contents(content) else { return Ok(()); }; // For some reason, it's not writen if you don't read it back... let _ = ctx.get_contents(); Ok(()) @@ -972,13 +967,13 @@ impl EventExec { /// Enter the filter mode, where you can filter. /// See `crate::filter::Filter` for more details. - pub fn event_filter(tab: &mut Tab) -> FmResult<()> { + pub fn event_filter(tab: &mut Tab) -> Result<()> { tab.set_mode(Mode::InputSimple(InputSimple::Filter)); Ok(()) } /// Move back in history to the last visited directory. - pub fn event_back(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn event_back(status: &mut Status, colors: &Colors) -> Result<()> { if status.selected_non_mut().history.content.len() <= 1 { return Ok(()); } @@ -995,7 +990,7 @@ impl EventExec { } /// Move to $HOME aka ~. - pub fn event_home(tab: &mut Tab) -> FmResult<()> { + pub fn event_home(tab: &mut Tab) -> Result<()> { let home_cow = shellexpand::tilde("~"); let home: &str = home_cow.borrow(); let path = std::fs::canonicalize(home)?; @@ -1016,13 +1011,13 @@ impl EventExec { /// It uses the `fs::rename` function and has the same limitations. /// We only try to rename in the same directory, so it shouldn't be a problem. /// Filename is sanitized before processing. - pub fn exec_rename(tab: &mut Tab) -> FmResult<()> { + pub fn exec_rename(tab: &mut Tab) -> Result<()> { let fileinfo = match tab.previous_mode { Mode::Tree => &tab.directory.tree.current_node.fileinfo, _ => tab .path_content .selected() - .ok_or_else(|| FmError::custom("rename", "couldnt parse selected"))?, + .context("rename: couldnt parse selected")?, }; let original_path = &fileinfo.path; @@ -1047,7 +1042,7 @@ impl EventExec { /// Creates a new file with input string as name. /// Nothing is done if the file already exists. /// Filename is sanitized before processing. - pub fn exec_newfile(tab: &mut Tab) -> FmResult<()> { + pub fn exec_newfile(tab: &mut Tab) -> Result<()> { let path = tab .path_content .path @@ -1064,7 +1059,7 @@ impl EventExec { /// We use `fs::create_dir` internally so it will fail if the input string /// ie. the user can create `newdir` or `newdir/newfolder`. /// Directory name is sanitized before processing. - pub fn exec_newdir(tab: &mut Tab) -> FmResult<()> { + pub fn exec_newdir(tab: &mut Tab) -> Result<()> { let path = tab .path_content .path @@ -1080,9 +1075,9 @@ impl EventExec { /// from the input string. It will fail silently if the executable can't /// be found. /// Optional parameters can be passed normally. ie. `"ls -lah"` - pub fn exec_exec(tab: &mut Tab) -> FmResult<()> { + pub fn exec_exec(tab: &mut Tab) -> Result<()> { if tab.path_content.content.is_empty() { - return Err(FmError::custom("exec exec", "empty directory")); + return Err(anyhow!("exec exec: empty directory")); } let exec_command = tab.input.string(); let mut args: Vec<&str> = exec_command.split(' ').collect(); @@ -1090,16 +1085,12 @@ impl EventExec { if std::path::Path::new(command).exists() { let path = &tab .selected() - .ok_or_else(|| { - FmError::custom("exec exec", &format!("can't find command {command}")) - })? + .ok_or_else(|| anyhow!("exec exec: can't find command {command}"))? .path .to_str() - .ok_or_else(|| { - FmError::custom("exec exec", &format!("can't find command {command}")) - })?; + .ok_or_else(|| anyhow!("exec exec: can't find command {command}"))?; // let path = &tab.path_content.selected_path_string().ok_or_else(|| { - // FmError::custom("exec exec", &format!("can't find command {}", command)) + // anyhow!("exec exec", &format!("can't find command {}", command)) // })?; args.push(path); execute_in_child(command, &args)?; @@ -1111,13 +1102,13 @@ impl EventExec { /// Executes a `dragon-drop` command on the selected file. /// It obviously requires the `dragon-drop` command to be installed. - pub fn event_drag_n_drop(status: &mut Status) -> FmResult<()> { + pub fn event_drag_n_drop(status: &mut Status) -> Result<()> { let tab = status.selected_non_mut(); let Some(file) = tab.selected() else { return Ok(()) }; let path_str = file .path .to_str() - .ok_or_else(|| FmError::custom("event drag n drop", "Couldn't read path"))?; + .context("event drag n drop: couldn't read path")?; execute_in_child(DEFAULT_DRAGNDROP, &vec![path_str])?; Ok(()) @@ -1128,7 +1119,7 @@ impl EventExec { /// ie. If you typed `"jpg"` before, it will move to the first file /// whose filename contains `"jpg"`. /// The current order of files is used. - pub fn exec_search(status: &mut Status, colors: &Colors) -> FmResult<()> { + pub fn exec_search(status: &mut Status, colors: &Colors) -> Result<()> { let tab = status.selected(); let searched = tab.input.string(); tab.input.reset(); @@ -1158,7 +1149,7 @@ impl EventExec { } } - pub fn event_search_next(tab: &mut Tab) -> FmResult<()> { + pub fn event_search_next(tab: &mut Tab) -> Result<()> { match tab.mode { Mode::Tree => (), _ => { @@ -1174,7 +1165,7 @@ impl EventExec { /// The first completion proposition is used, `~` expansion is done. /// If no result were found, no cd is done and we go back to normal mode /// silently. - pub fn exec_goto(tab: &mut Tab) -> FmResult<()> { + pub fn exec_goto(tab: &mut Tab) -> Result<()> { if tab.completion.is_empty() { return Ok(()); } @@ -1189,12 +1180,12 @@ impl EventExec { /// Move to the selected shortcut. /// It may fail if the user has no permission to visit the path. - pub fn exec_shortcut(tab: &mut Tab) -> FmResult<()> { + pub fn exec_shortcut(tab: &mut Tab) -> Result<()> { tab.input.reset(); let path = tab .shortcut .selected() - .ok_or_else(|| FmError::custom("exec shortcut", "empty shortcuts"))? + .context("exec shortcut: empty shortcuts")? .clone(); tab.history.pus