navi/src/filesystem.rs
2020-04-25 21:42:24 -03:00

119 lines
3.8 KiB
Rust

use crate::structures::config::Config;
use crate::structures::error::filesystem::InvalidPath;
use crate::structures::error::filesystem::UnreadableDir;
use anyhow::Context;
use anyhow::Error;
use core::fmt::Display;
use std::fs;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::{Path, PathBuf};
pub fn read_lines<P>(filename: P) -> Result<impl Iterator<Item = Result<String, Error>>, Error>
where
P: AsRef<Path> + Display + Copy,
{
let file = File::open(filename).with_context(|| format!("Failed to open file {}", filename))?;
Ok(io::BufReader::new(file)
.lines()
.map(|line| line.map_err(Error::from)))
}
pub fn pathbuf_to_string(pathbuf: PathBuf) -> Result<String, Error> {
Ok(pathbuf
.as_os_str()
.to_str()
.ok_or_else(|| InvalidPath(pathbuf.to_path_buf()))
.map(str::to_string)?)
}
pub fn cheat_pathbuf() -> Result<PathBuf, Error> {
dirs::data_dir()
.map(|mut dir| {
dir.push("navi");
dir.push("cheats");
dir
})
.ok_or_else(|| anyhow!("Unable to acquire user data directory for cheatsheets."))
}
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(|| InvalidPath(o.to_path_buf()))?;
if o_str.starts_with('.') {
let parent = pathbuf
.parent()
.ok_or_else(|| anyhow!("`{}` has no parent", pathbuf.display()))?;
let parent_str = parent
.as_os_str()
.to_str()
.ok_or_else(|| InvalidPath(parent.to_path_buf()))?;
let path_str = format!("{}/{}", parent_str, o_str);
let p = PathBuf::from(path_str);
follow_symlink(p)
} else {
follow_symlink(o)
}
})
.unwrap_or(Ok(pathbuf))
}
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() -> Result<String, Error> {
pathbuf_to_string(exe_pathbuf()?)
}
fn cheat_paths_from_config_dir() -> Result<String, Error> {
cheat_pathbuf()
.and_then(pathbuf_to_string)
.and_then(|path| {
fs::read_dir(path.clone())
.map_err(|e| UnreadableDir::new(path.clone(), e).into())
.map(|entries| (path, entries))
})
.and_then(|(path, dir_entries)| {
let mut paths_str = String::from("");
for entry in dir_entries {
let path = entry.map_err(|e| UnreadableDir::new(path.clone(), e))?;
paths_str.push_str(
path.path()
.into_os_string()
.to_str()
.ok_or_else(|| InvalidPath(path.path()))?,
);
paths_str.push_str(":");
}
Ok(paths_str)
})
}
pub fn cheat_paths(config: &Config) -> Result<String, Error> {
config
.path
.clone()
.ok_or_else(|| anyhow!("No cheat paths"))
.or_else(|_| {
cheat_paths_from_config_dir().context("No directory for cheats in user data directory")
})
}
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) -> Result<(), Error> {
fs::remove_dir_all(path).with_context(|| format!("Failed to remove directory `{}`", path))
}
pub fn tmp_path_str() -> Result<String, Error> {
let cheat_path_str = pathbuf_to_string(cheat_pathbuf()?)?;
Ok(format!("{}/tmp", cheat_path_str))
}