use crate::adapters::{
Adapter, FromPath, Partitioned, PartitionedAdapter, Unified, UnifiedAdapter,
};
use crate::{JoydbError, state::State};
use crate::{Model, Relation};
use std::io::Read;
use std::path::{Path, PathBuf};
use super::fs_utils;
pub struct RonAdapter {
file_path: PathBuf,
pretty_config: Option<ron::ser::PrettyConfig>,
}
impl FromPath for RonAdapter {
fn from_path<P: AsRef<Path>>(file_path: P) -> Self {
Self::new(file_path, true)
}
}
impl RonAdapter {
pub fn new<P: AsRef<Path>>(file_path: P, pretty: bool) -> Self {
let pretty_config = if pretty {
Some(ron::ser::PrettyConfig::default())
} else {
None
};
Self {
file_path: file_path.as_ref().to_path_buf(),
pretty_config,
}
}
}
impl UnifiedAdapter for RonAdapter {
fn write_state<S: State>(&self, state: &S) -> Result<(), JoydbError> {
write_to_file(state, &self.file_path, self.pretty_config.clone())
}
fn load_state<S: State>(&self) -> Result<S, JoydbError> {
if self.file_path.exists() {
if !self.file_path.is_file() {
Err(JoydbError::NotFile(self.file_path.clone()))
} else {
let mut file = std::fs::File::open(&self.file_path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let state =
ron::from_str(&contents).map_err(|e| JoydbError::Deserialize(Box::new(e)))?;
Ok(state)
}
} else {
let empty_state = S::default();
UnifiedAdapter::write_state(self, &empty_state)?;
Ok(empty_state)
}
}
}
impl Adapter for RonAdapter {
type Target = Unified<Self>;
}
pub struct RonPartitionedAdapter {
dir_path: PathBuf,
pretty_config: Option<ron::ser::PrettyConfig>,
}
impl FromPath for RonPartitionedAdapter {
fn from_path<P: AsRef<Path>>(dir_path: P) -> Self {
Self::new(dir_path, true)
}
}
impl RonPartitionedAdapter {
pub fn new<P: AsRef<Path>>(dir_path: P, pretty: bool) -> Self {
let pretty_config = if pretty {
Some(ron::ser::PrettyConfig::default())
} else {
None
};
Self {
dir_path: dir_path.as_ref().to_path_buf(),
pretty_config,
}
}
fn relation_file_path<M: Model>(&self) -> PathBuf {
self.dir_path.join(format!("{}.ron", M::model_name()))
}
}
impl PartitionedAdapter for RonPartitionedAdapter {
fn write_relation<M: Model>(&self, relation: &Relation<M>) -> Result<(), JoydbError> {
write_to_file(
relation,
&self.relation_file_path::<M>(),
self.pretty_config.clone(),
)
}
fn load_relation<M: Model>(&self) -> Result<Relation<M>, JoydbError> {
let file_path = self.relation_file_path::<M>();
if file_path.exists() {
if !file_path.is_file() {
Err(JoydbError::NotFile(file_path))
} else {
read_from_file(&file_path)
}
} else {
let empty_relation = Relation::<M>::default();
self.write_relation(&empty_relation)?;
Ok(empty_relation)
}
}
fn load_state<S: State>(&self) -> Result<S, JoydbError> {
if self.dir_path.exists() {
if !self.dir_path.is_dir() {
return Err(JoydbError::NotDirectory(self.dir_path.clone()));
}
} else {
std::fs::create_dir_all(&self.dir_path)?;
}
S::load_with_partitioned_adapter(self)
}
}
impl Adapter for RonPartitionedAdapter {
type Target = Partitioned<Self>;
}
fn write_to_file<T: ::serde::Serialize>(
data: &T,
file_path: &PathBuf,
pretty_config: Option<ron::ser::PrettyConfig>,
) -> Result<(), JoydbError> {
let ron_string: String = if let Some(pretty_config) = pretty_config {
ron::ser::to_string_pretty(data, pretty_config)
} else {
ron::ser::to_string(data)
}
.map_err(|e| JoydbError::Serialize(Box::new(e)))?;
fs_utils::safe_write(file_path, ron_string.as_bytes())?;
Ok(())
}
fn read_from_file<T: ::serde::de::DeserializeOwned>(file_path: &PathBuf) -> Result<T, JoydbError> {
let content = fs_utils::read_file(file_path)?;
let data = ron::de::from_str(&content).map_err(|e| JoydbError::Deserialize(Box::new(e)))?;
Ok(data)
}