use std::path::{Path, PathBuf};
use serde_json::{Map, Value};
use crate::migrate::ModelMeta;
use crate::orm::{DynError, DynQuerySet, Manager, Model};
#[derive(Debug)]
pub enum FixtureError {
Io(std::io::Error),
Json(serde_json::Error),
NotAnArray { path: PathBuf },
Write {
index: usize,
source: crate::orm::write::WriteError,
},
Read(DynError),
}
impl std::fmt::Display for FixtureError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(e) => write!(f, "fixture I/O error: {e}"),
Self::Json(e) => write!(f, "fixture JSON error: {e}"),
Self::NotAnArray { path } => write!(
f,
"fixture {} is not a JSON array of row objects",
path.display()
),
Self::Write { index, source } => {
write!(f, "fixture row #{index} insert failed: {source:?}")
}
Self::Read(e) => write!(f, "fixture read-back failed: {e:?}"),
}
}
}
impl std::error::Error for FixtureError {}
impl From<std::io::Error> for FixtureError {
fn from(e: std::io::Error) -> Self {
Self::Io(e)
}
}
impl From<serde_json::Error> for FixtureError {
fn from(e: serde_json::Error) -> Self {
Self::Json(e)
}
}
pub async fn load_fixture<T, P>(path: P) -> Result<usize, FixtureError>
where
T: Model,
P: AsRef<Path>,
{
let path = path.as_ref();
let bytes = std::fs::read(path)?;
let parsed: Value = serde_json::from_slice(&bytes)?;
let rows = match parsed {
Value::Array(rows) => rows,
_ => {
return Err(FixtureError::NotAnArray {
path: path.to_path_buf(),
});
}
};
let meta = ModelMeta::for_::<T>();
for (index, raw) in rows.iter().enumerate() {
let obj: Map<String, Value> = match raw {
Value::Object(map) => map.clone(),
_ => {
return Err(FixtureError::NotAnArray {
path: path.to_path_buf(),
});
}
};
DynQuerySet::for_meta(&meta)
.insert_json(&obj)
.await
.map_err(|source| FixtureError::Write { index, source })?;
}
Ok(rows.len())
}
pub async fn dump_fixture<T, P>(path: P) -> Result<usize, FixtureError>
where
T: Model,
P: AsRef<Path>,
{
let meta = ModelMeta::for_::<T>();
let rows = DynQuerySet::for_meta(&meta)
.fetch_as_json()
.await
.map_err(FixtureError::Read)?;
let json = serde_json::to_string_pretty(&rows)?;
std::fs::write(path.as_ref(), json)?;
Ok(rows.len())
}
impl<T: Model> Manager<T> {
pub async fn load_fixture<P: AsRef<Path>>(&self, path: P) -> Result<usize, FixtureError> {
load_fixture::<T, _>(path).await
}
pub async fn dump_fixture<P: AsRef<Path>>(&self, path: P) -> Result<usize, FixtureError> {
dump_fixture::<T, _>(path).await
}
}