diff --git a/src/filesystem.rs b/src/filesystem.rs index 592e86b..c8fd41b 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -1,4 +1,5 @@ use crate::structures::option::Config; +use anyhow::Context; use anyhow::Error; use std::fs; use std::fs::File; @@ -13,8 +14,12 @@ where Ok(io::BufReader::new(file).lines()) } -pub fn pathbuf_to_string(pathbuf: PathBuf) -> String { - pathbuf.as_os_str().to_str().unwrap().to_string() +pub fn pathbuf_to_string(pathbuf: PathBuf) -> Result { + pathbuf + .as_os_str() + .to_str() + .ok_or_else(|| anyhow!("Invalid path {}", pathbuf.display())) + .map(str::to_string) } pub fn cheat_pathbuf() -> Result { @@ -27,64 +32,75 @@ pub fn cheat_pathbuf() -> Result { .ok_or_else(|| anyhow!("Unable to acquire user data directory for cheatsheets.")) } -fn follow_symlink(pathbuf: PathBuf) -> PathBuf { - let other = fs::read_link(pathbuf.clone()); - match other { - Ok(o) => { - let o_str = o.as_os_str().to_str().unwrap(); +fn follow_symlink(pathbuf: PathBuf) -> Result { + fs::read_link(pathbuf.clone()) + .map(|o| { + let o_str = o + .as_os_str() + .to_str() + .ok_or_else(|| anyhow!("Invalid path {}", o.display()))?; if o_str.starts_with('.') { - let parent_str = pathbuf.parent().unwrap().as_os_str().to_str().unwrap(); + let parent_str = pathbuf + .parent() + .ok_or_else(|| anyhow!("{} has no parent", pathbuf.display()))? + .as_os_str() + .to_str() + .ok_or_else(|| anyhow!("Parent of {} is an invalid path", pathbuf.display()))?; let path_str = format!("{}/{}", parent_str, o_str); let p = PathBuf::from(path_str); follow_symlink(p) } else { follow_symlink(o) } - } - Err(_) => pathbuf, - } + }) + .unwrap_or(Ok(pathbuf)) } -fn exe_pathbuf() -> PathBuf { - let pathbuf = std::env::current_exe().unwrap(); +fn exe_pathbuf() -> Result { + let pathbuf = std::env::current_exe().context("Unable to acquire executable's path")?; follow_symlink(pathbuf) } -pub fn exe_string() -> String { - pathbuf_to_string(exe_pathbuf()) +pub fn exe_string() -> Result { + pathbuf_to_string(exe_pathbuf()?) } -fn cheat_paths_from_config_dir() -> String { - let mut paths_str = String::from(""); - - if let Ok(f) = cheat_pathbuf() { - if let Ok(paths) = fs::read_dir(pathbuf_to_string(f)) { - for path in paths { - paths_str.push_str(path.unwrap().path().into_os_string().to_str().unwrap()); +fn cheat_paths_from_config_dir() -> Result { + cheat_pathbuf() + .and_then(|f| fs::read_dir(pathbuf_to_string(f)?).context("Unable to read directory")) + .and_then(|dir_entries| { + let mut paths_str = String::from(""); + for entry in dir_entries { + let path = entry.context("Unable to read directory")?; + paths_str.push_str( + path.path() + .into_os_string() + .to_str() + .ok_or_else(|| anyhow!("Invalid path {}", path.path().display()))?, + ); paths_str.push_str(":"); } - } - } - - paths_str + Ok(paths_str) + }) } -pub fn cheat_paths(config: &Config) -> String { +pub fn cheat_paths(config: &Config) -> Result { config .path .clone() - .unwrap_or_else(cheat_paths_from_config_dir) + .ok_or_else(|| anyhow!("No cheat paths")) + .or_else(|_| cheat_paths_from_config_dir().context("No cheat paths from config directory")) } -pub fn create_dir(path: &str) { - fs::create_dir_all(path).unwrap_or(()); +pub fn create_dir(path: &str) -> Result<(), Error> { + fs::create_dir_all(path).with_context(|| format!("Failed to create directory {}", path)) } -pub fn remove_dir(path: &str) { - fs::remove_dir_all(path).unwrap_or(()); +pub fn remove_dir(path: &str) -> Result<(), Error> { + fs::remove_dir_all(path).with_context(|| format!("Failed to remove directory {}", path)) } -pub fn tmp_path_str() -> String { - let cheat_path_str = pathbuf_to_string(cheat_pathbuf().unwrap()); - format!("{}/tmp", cheat_path_str) +pub fn tmp_path_str() -> Result { + let cheat_path_str = pathbuf_to_string(cheat_pathbuf()?)?; + Ok(format!("{}/tmp", cheat_path_str)) } diff --git a/src/flows/core.rs b/src/flows/core.rs index f959a2c..d4f8143 100644 --- a/src/flows/core.rs +++ b/src/flows/core.rs @@ -26,12 +26,12 @@ pub enum Variant { Query(String), } -fn gen_core_fzf_opts(variant: Variant, config: &Config) -> FzfOpts { +fn gen_core_fzf_opts(variant: Variant, config: &Config) -> Result { let mut opts = FzfOpts { preview: if config.no_preview { None } else { - Some(format!("{} preview {{}}", filesystem::exe_string())) + Some(format!("{} preview {{}}", filesystem::exe_string()?)) }, autoselect: !config.no_autoselect, overrides: config.fzf_overrides.clone(), @@ -45,7 +45,7 @@ fn gen_core_fzf_opts(variant: Variant, config: &Config) -> FzfOpts { Variant::Query(q) => opts.query = Some(q), } - opts + Ok(opts) } fn extract_from_selections(raw_snippet: &str, contains_key: bool) -> (&str, &str, &str) { @@ -172,9 +172,13 @@ fn with_new_lines(txt: String) -> String { pub fn main(variant: Variant, config: Config, contains_key: bool) -> Result<(), Error> { let _ = display::WIDTHS; - let opts = gen_core_fzf_opts(variant, &config); - let (raw_selection, variables) = - fzf::call(opts, |stdin| Some(parser::read_all(&config, stdin))); + let opts = gen_core_fzf_opts(variant, &config).context("Failed to generate fzf options")?; + let (raw_selection, variables) = fzf::call(opts, |stdin| { + Some( + parser::read_all(&config, stdin) + .expect("Failed to read all variables intended for fzf"), + ) + }); let (key, tags, snippet) = extract_from_selections(&raw_selection[..], contains_key); diff --git a/src/flows/repo.rs b/src/flows/repo.rs index bd840ad..2d7f676 100644 --- a/src/flows/repo.rs +++ b/src/flows/repo.rs @@ -10,10 +10,10 @@ use std::io::Write; use walkdir::WalkDir; pub fn browse() -> Result<(), Error> { - let repo_path_str = format!("{}/featured", filesystem::tmp_path_str()); + let repo_path_str = format!("{}/featured", filesystem::tmp_path_str()?); - filesystem::remove_dir(&repo_path_str); - filesystem::create_dir(&repo_path_str); + filesystem::remove_dir(&repo_path_str)?; + filesystem::create_dir(&repo_path_str)?; let repo_url = "https://github.com/denisidoro/cheats"; Repository::clone(repo_url, &repo_path_str) @@ -34,7 +34,7 @@ pub fn browse() -> Result<(), Error> { None }); - filesystem::remove_dir(&repo_path_str); + filesystem::remove_dir(&repo_path_str)?; add(repo) } @@ -42,12 +42,12 @@ pub fn browse() -> Result<(), Error> { pub fn add(uri: String) -> Result<(), Error> { let (actual_uri, user, repo) = git::meta(uri.as_str()); - let cheat_path_str = filesystem::pathbuf_to_string(filesystem::cheat_pathbuf()?); - let tmp_path_str = filesystem::tmp_path_str(); + let cheat_path_str = filesystem::pathbuf_to_string(filesystem::cheat_pathbuf()?)?; + let tmp_path_str = filesystem::tmp_path_str()?; let tmp_path_str_with_trailing_slash = format!("{}/", &tmp_path_str); - filesystem::remove_dir(&tmp_path_str); - filesystem::create_dir(&tmp_path_str); + filesystem::remove_dir(&tmp_path_str)?; + filesystem::create_dir(&tmp_path_str)?; eprintln!("Cloning {} into {}...\n", &actual_uri, &tmp_path_str); @@ -89,7 +89,7 @@ pub fn add(uri: String) -> Result<(), Error> { fs::copy(from, to)?; } - filesystem::remove_dir(&tmp_path_str); + filesystem::remove_dir(&tmp_path_str)?; eprintln!("The following .cheat files were imported successfully:\n{}\n\nThey are now located at {}\n\nPlease run navi again to check the results.", files, cheat_path_str); diff --git a/src/parser.rs b/src/parser.rs index 2f63821..3d90995 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -5,6 +5,7 @@ use crate::structures::fnv::HashLine; use crate::structures::fzf::{Opts as FzfOpts, SuggestionType}; use crate::structures::option::Config; use crate::welcome; +use anyhow::Error; use regex::Regex; use std::collections::HashSet; use std::fs; @@ -160,11 +161,14 @@ fn read_file( false } -pub fn read_all(config: &Config, stdin: &mut std::process::ChildStdin) -> VariableMap { +pub fn read_all( + config: &Config, + stdin: &mut std::process::ChildStdin, +) -> Result { let mut variables = VariableMap::new(); let mut found_something = false; let mut visited_lines = HashSet::new(); - let paths = filesystem::cheat_paths(config); + let paths = filesystem::cheat_paths(config)?; let folders = paths.split(':'); for folder in folders { @@ -186,7 +190,7 @@ pub fn read_all(config: &Config, stdin: &mut std::process::ChildStdin) -> Variab welcome::cheatsheet(stdin); } - variables + Ok(variables) } #[cfg(test)]