use crate::errors::FsPathError;
use serde::{de::DeserializeOwned, Serialize};
use std::{
fs::{self, File},
io::{BufWriter, Write},
path::{Component, Path, PathBuf},
};
type Result<T> = std::result::Result<T, FsPathError>;
pub fn create_file(path: impl AsRef<Path>) -> Result<fs::File> {
let path = path.as_ref();
File::create(path).map_err(|err| FsPathError::create_file(err, path))
}
pub fn remove_file(path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
fs::remove_file(path).map_err(|err| FsPathError::remove_file(err, path))
}
pub fn read(path: impl AsRef<Path>) -> Result<Vec<u8>> {
let path = path.as_ref();
fs::read(path).map_err(|err| FsPathError::read(err, path))
}
pub fn read_link(path: impl AsRef<Path>) -> Result<PathBuf> {
let path = path.as_ref();
fs::read_link(path).map_err(|err| FsPathError::read_link(err, path))
}
pub fn read_to_string(path: impl AsRef<Path>) -> Result<String> {
let path = path.as_ref();
fs::read_to_string(path).map_err(|err| FsPathError::read(err, path))
}
pub fn read_json_file<T: DeserializeOwned>(path: &Path) -> Result<T> {
let s = read_to_string(path)?;
serde_json::from_str(&s).map_err(|source| FsPathError::ReadJson { source, path: path.into() })
}
pub fn write_json_file<T: Serialize>(path: &Path, obj: &T) -> Result<()> {
let file = create_file(path)?;
let mut writer = BufWriter::new(file);
serde_json::to_writer(&mut writer, obj)
.map_err(|source| FsPathError::WriteJson { source, path: path.into() })?;
writer.flush().map_err(|e| FsPathError::write(e, path))
}
pub fn write_pretty_json_file<T: Serialize>(path: &Path, obj: &T) -> Result<()> {
let file = create_file(path)?;
let mut writer = BufWriter::new(file);
serde_json::to_writer_pretty(&mut writer, obj)
.map_err(|source| FsPathError::WriteJson { source, path: path.into() })?;
writer.flush().map_err(|e| FsPathError::write(e, path))
}
pub fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Result<()> {
let path = path.as_ref();
fs::write(path, contents).map_err(|err| FsPathError::write(err, path))
}
pub fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<u64> {
let from = from.as_ref();
let to = to.as_ref();
fs::copy(from, to).map_err(|err| FsPathError::copy(err, from, to))
}
pub fn create_dir(path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
fs::create_dir(path).map_err(|err| FsPathError::create_dir(err, path))
}
pub fn create_dir_all(path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
fs::create_dir_all(path).map_err(|err| FsPathError::create_dir(err, path))
}
pub fn remove_dir(path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
fs::remove_dir(path).map_err(|err| FsPathError::remove_dir(err, path))
}
pub fn remove_dir_all(path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
fs::remove_dir_all(path).map_err(|err| FsPathError::remove_dir(err, path))
}
pub fn open(path: impl AsRef<Path>) -> Result<fs::File> {
let path = path.as_ref();
fs::File::open(path).map_err(|err| FsPathError::open(err, path))
}
pub fn normalize_path(path: &Path) -> PathBuf {
let mut components = path.components().peekable();
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
components.next();
PathBuf::from(c.as_os_str())
} else {
PathBuf::new()
};
for component in components {
match component {
Component::Prefix(..) => unreachable!(),
Component::RootDir => {
ret.push(component.as_os_str());
}
Component::CurDir => {}
Component::ParentDir => {
ret.pop();
}
Component::Normal(c) => {
ret.push(c);
}
}
}
ret
}
pub fn files_with_ext<'a>(root: &Path, ext: &'a str) -> impl Iterator<Item = PathBuf> + 'a {
walkdir::WalkDir::new(root)
.sort_by_file_name()
.into_iter()
.filter_map(walkdir::Result::ok)
.filter(|e| e.file_type().is_file() && e.path().extension() == Some(ext.as_ref()))
.map(walkdir::DirEntry::into_path)
}
pub fn json_files(root: &Path) -> impl Iterator<Item = PathBuf> {
files_with_ext(root, "json")
}
pub fn canonicalize_path(path: impl AsRef<Path>) -> std::io::Result<PathBuf> {
dunce::canonicalize(path)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normalize_path() {
let p = Path::new("/a/../file.txt");
let normalized = normalize_path(p);
assert_eq!(normalized, PathBuf::from("/file.txt"));
}
}