use std::path::{Path, PathBuf};
use anyhow::Context;
use crate::CIResult;
pub trait PathExt {
fn file_name(&self) -> CIResult<String>;
fn file_stem(&self) -> CIResult<String>;
fn extension(&self) -> CIResult<String>;
fn parent(&self) -> CIResult<PathBuf>;
fn to_string(&self) -> CIResult<String>;
fn executable(&self) -> bool;
fn append_suffix(&self, suffix: &str) -> CIResult<PathBuf>;
fn read_dir<P>(&self, predicate: P) -> CIResult<Vec<PathBuf>>
where
P: FnMut(&PathBuf) -> bool;
}
impl<T> PathExt for T
where
T: AsRef<Path>,
{
fn file_name(&self) -> CIResult<String> {
let path = self.as_ref();
path.file_name()
.and_then(|s| s.to_str())
.map(|s| s.to_string())
.with_context(|| format!("failed to get file name `{}`", path.display()))
}
fn file_stem(&self) -> CIResult<String> {
let path = self.as_ref();
path.file_stem()
.and_then(|s| s.to_str())
.map(|s| s.to_string())
.with_context(|| format!("failed to get file stem `{}`", path.display()))
}
fn extension(&self) -> CIResult<String> {
let path = self.as_ref();
path.extension()
.and_then(|s| s.to_str())
.map(|s| s.to_string())
.with_context(|| format!("failed to get extension `{}`", path.display()))
}
fn parent(&self) -> CIResult<PathBuf> {
let path = self.as_ref();
Ok(path
.parent()
.with_context(|| format!("failed to get parent dir `{}`", path.display()))?
.to_path_buf())
}
fn to_string(&self) -> CIResult<String> {
let path = self.as_ref();
path.to_str()
.map(|s| s.to_string())
.with_context(|| format!("failed to convert to string `{}`", path.display()))
}
fn executable(&self) -> bool {
use std::os::unix::prelude::*;
std::fs::metadata(self.as_ref())
.map(|metadata| metadata.is_file() && metadata.permissions().mode() & 0o111 != 0)
.unwrap_or(false)
}
fn append_suffix(&self, suffix: &str) -> CIResult<PathBuf> {
let file_stem = self.file_stem()?;
let extension = self.extension();
let file_name = if let Ok(extension) = extension {
format!("{}-{}.{}", file_stem, suffix, extension)
} else {
format!("{}-{}", file_stem, suffix)
};
Ok(self.as_ref().with_file_name(file_name))
}
fn read_dir<P>(&self, predicate: P) -> CIResult<Vec<PathBuf>>
where
P: FnMut(&PathBuf) -> bool,
{
let path = self.as_ref();
Ok(path
.read_dir()
.with_context(|| format!("failed to read directory `{}`", path.display()))?
.filter_map(|e| e.ok())
.map(|e| e.path())
.filter(predicate)
.collect::<Vec<_>>())
}
}