use std::{
fs::File,
io::Read,
path::{Path, PathBuf},
};
use serde::de::DeserializeOwned;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ReadJsonError {
#[error("{0} io error: {1}")]
IoError(PathBuf, #[source] std::io::Error),
#[error("failed to parse {0}: {1}")]
ParseError(PathBuf, #[source] serde_json::Error),
}
pub trait ReadJsonErrExt<T> {
fn err_path(self, path: impl AsRef<Path>) -> Result<T, ReadJsonError>;
}
impl<T> ReadJsonErrExt<T> for Result<T, std::io::Error> {
fn err_path(self, path: impl AsRef<Path>) -> Result<T, ReadJsonError> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(ReadJsonError::IoError(path.as_ref().to_owned(), e)),
}
}
}
impl<T> ReadJsonErrExt<T> for Result<T, serde_json::Error> {
fn err_path(self, path: impl AsRef<Path>) -> Result<T, ReadJsonError> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(ReadJsonError::ParseError(path.as_ref().to_owned(), e)),
}
}
}
pub fn read_json_file<T: DeserializeOwned>(path: impl AsRef<Path>) -> Result<T, ReadJsonError> {
let mut f = File::open(&path).err_path(&path)?;
let mut data = String::with_capacity(4096);
f.read_to_string(&mut data).err_path(&path)?;
serde_json::from_str(&data).err_path(&path)
}