use std::marker::PhantomData;
use std::path::PathBuf;
use crate::Uri;
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum JsonError {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("serialize error: {0}")]
Serialize(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("deserialize error: {0}")]
Deserialize(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error(transparent)]
BinaryReader(#[from] crate::formats::binary::BinaryError),
}
#[derive(Debug)]
pub struct JsonWriter<T> {
path: PathBuf,
_marker: PhantomData<T>,
}
impl<T: serde::Serialize> JsonWriter<T> {
pub fn new(path: impl Into<Uri>) -> Result<Self, JsonError> {
let uri: Uri = path.into();
let p = uri.as_path().ok_or_else(|| {
JsonError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"not a local path",
))
})?;
Ok(JsonWriter {
path: p,
_marker: PhantomData,
})
}
pub fn write(&mut self, value: T) -> Result<(), JsonError> {
let file = std::fs::File::create(&self.path)?;
serde_json::to_writer(file, &value)
.map_err(|e| JsonError::Serialize(Box::new(e)))?;
Ok(())
}
}
#[derive(Debug)]
pub struct JsonReader<T> {
data: Vec<u8>,
_marker: PhantomData<T>,
}
impl<T: for<'de> serde::Deserialize<'de>> JsonReader<T> {
pub fn from(uri: impl Into<Uri>) -> Result<Self, JsonError> {
let binary_reader = crate::formats::binary::BinaryReader::from(uri)?;
let data = binary_reader.read_range(..)?;
Ok(JsonReader {
data,
_marker: PhantomData,
})
}
pub fn read_all(&self) -> Result<T, JsonError> {
serde_json::from_slice(&self.data)
.map_err(|e| JsonError::Deserialize(Box::new(e)))
}
}
pub trait JsonFormat: Sized {
fn write_to_json(self, uri: impl Into<Uri>) -> Result<(), JsonError>
where
Self: serde::Serialize,
{
let mut writer = JsonWriter::new(uri)?;
writer.write(self)
}
fn read_from_json(uri: impl Into<Uri>) -> Result<Self, JsonError>
where
Self: for<'de> serde::Deserialize<'de>,
{
JsonReader::from(uri)?.read_all()
}
}