use serde;
use serde_json;
use std::error::Error;
use std::io::{Read, Write};
use std::path::Path;
use std::{fmt, fs, io};
use toml;
#[derive(Debug)]
pub enum FileError<E> {
Io(io::Error),
Format(E),
}
pub type JsonFileError = FileError<serde_json::Error>;
pub type TomlFileSaveError = FileError<toml::ser::Error>;
pub type TomlFileLoadError = FileError<toml::de::Error>;
impl<E> From<io::Error> for FileError<E> {
fn from(err: io::Error) -> Self {
FileError::Io(err)
}
}
impl From<serde_json::Error> for JsonFileError {
fn from(err: serde_json::Error) -> Self {
FileError::Format(err)
}
}
impl From<toml::ser::Error> for TomlFileSaveError {
fn from(err: toml::ser::Error) -> Self {
FileError::Format(err)
}
}
impl From<toml::de::Error> for TomlFileLoadError {
fn from(err: toml::de::Error) -> Self {
FileError::Format(err)
}
}
impl<E> Error for FileError<E>
where
E: Error,
{
fn cause(&self) -> Option<&dyn Error> {
match *self {
FileError::Io(ref err) => Some(err),
FileError::Format(ref err) => Some(err),
}
}
}
impl<E> fmt::Display for FileError<E>
where
E: Error,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FileError::Io(ref err) => fmt::Display::fmt(err, f),
FileError::Format(ref err) => fmt::Display::fmt(err, f),
}
}
}
pub fn safe_file_save<P>(path: P, content: &[u8]) -> io::Result<()>
where
P: AsRef<Path>,
{
let path = path.as_ref();
let temp_path = path.with_extension("tmp");
let backup_path = path.with_extension("backup");
if temp_path.exists() {
fs::remove_file(&temp_path)?;
}
if let Some(directory) = path.parent() {
if !directory.exists() {
fs::create_dir_all(&temp_path)?;
}
}
{
let file = fs::File::create(&temp_path)?;
let mut buffered = io::BufWriter::new(file);
buffered.write(content)?;
match buffered.into_inner() {
Err(err) => {
let io_err = err.error();
return Err(std::io::Error::new(io_err.kind(), format!("{}", io_err)));
}
Ok(file) => {
file.sync_all()?;
}
}
}
if path.exists() {
if backup_path.exists() {
fs::remove_file(&backup_path)?;
}
fs::rename(&path, &backup_path)?;
}
fs::rename(temp_path, path)?;
if backup_path.exists() {
fs::remove_file(&backup_path)?;
}
Ok(())
}
pub fn save_to_json<P, T>(path: P, t: &T) -> Result<(), JsonFileError>
where
P: AsRef<Path>,
T: serde::Serialize,
{
let string = serde_json::to_string_pretty(t)?;
safe_file_save(path, string.as_bytes())?;
Ok(())
}
pub fn load_from_json<'a, P, T>(path: P) -> Result<T, JsonFileError>
where
P: AsRef<Path>,
T: for<'de> serde::Deserialize<'de>,
{
let file = fs::File::open(path)?;
let t = serde_json::from_reader(file)?;
Ok(t)
}
pub fn save_to_toml<P, T>(path: P, t: &T) -> Result<(), TomlFileSaveError>
where
P: AsRef<Path>,
T: serde::Serialize,
{
let string = toml::to_string_pretty(t)?;
safe_file_save(path, string.as_bytes())?;
Ok(())
}
pub fn load_from_toml<'a, P, T>(path: P) -> Result<T, TomlFileLoadError>
where
P: AsRef<Path>,
T: for<'de> serde::Deserialize<'de>,
{
let file = fs::File::open(path)?;
let mut buffered = io::BufReader::new(file);
let mut string = String::new();
buffered.read_to_string(&mut string)?;
let t = toml::from_str(&string)?;
Ok(t)
}
pub fn walk_dir<P>(path: P) -> walkdir::WalkDir
where
P: AsRef<Path>,
{
walkdir::WalkDir::new(path)
}