Add error info to filesystem operations.

This commit is contained in:
Csonka Mihaly 2020-03-21 17:43:13 +01:00
parent ff264735b9
commit a4e2311263
4 changed files with 77 additions and 53 deletions

View file

@ -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<String, Error> {
pathbuf
.as_os_str()
.to_str()
.ok_or_else(|| anyhow!("Invalid path {}", pathbuf.display()))
.map(str::to_string)
}
pub fn cheat_pathbuf() -> Result<PathBuf, Error> {
@ -27,64 +32,75 @@ pub fn cheat_pathbuf() -> Result<PathBuf, Error> {
.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<PathBuf, Error> {
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<PathBuf, Error> {
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<String, Error> {
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<String, Error> {
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<String, Error> {
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<String, Error> {
let cheat_path_str = pathbuf_to_string(cheat_pathbuf()?)?;
Ok(format!("{}/tmp", cheat_path_str))
}

View file

@ -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<FzfOpts, Error> {
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);

View file

@ -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);

View file

@ -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<VariableMap, Error> {
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)]