#[cfg(feature = "json")]
#[derive(Default)]
pub struct Json<T> {
_phantom: std::marker::PhantomData<T>,
}
#[cfg(feature = "json")]
impl<T> crate::ConfigFormat for Json<T>
where
T: serde::Serialize + serde::de::DeserializeOwned,
{
type Data = T;
fn serialize(&self, data: &Self::Data) -> Result<String, crate::error::SaveError> {
Ok(serde_json::to_string_pretty(data)?)
}
fn deserialize(&self, data: &str) -> Result<Self::Data, crate::error::LoadError> {
Ok(serde_json::from_str(data)?)
}
}
#[cfg(feature = "toml")]
#[derive(Default)]
pub struct Toml<T> {
_phantom: std::marker::PhantomData<T>,
}
#[cfg(feature = "toml")]
impl<T> crate::ConfigFormat for Toml<T>
where
T: serde::Serialize + serde::de::DeserializeOwned,
{
type Data = T;
fn serialize(&self, data: &Self::Data) -> Result<String, crate::error::SaveError> {
Ok(toml::to_string_pretty(data)?)
}
fn deserialize(&self, data: &str) -> Result<Self::Data, crate::error::LoadError> {
Ok(toml::from_str(data)?)
}
}
#[cfg(feature = "yaml")]
#[derive(Default)]
pub struct Yaml<T> {
_phantom: std::marker::PhantomData<T>,
}
#[cfg(feature = "yaml")]
impl<T> crate::ConfigFormat for Yaml<T>
where
T: serde::Serialize + serde::de::DeserializeOwned,
{
type Data = T;
fn serialize(&self, data: &Self::Data) -> Result<String, crate::error::SaveError> {
Ok(serde_saphyr::to_string(data)?)
}
fn deserialize(&self, data: &str) -> Result<Self::Data, crate::error::LoadError> {
Ok(serde_saphyr::from_str(data)?)
}
}
#[cfg(test)]
mod tests {
use crate::ConfigFormat;
use crate::error::{LoadError, SaveError};
use anyhow::anyhow;
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::Write;
use temp_testdir::TempDir;
#[derive(Default, Serialize, Deserialize, PartialEq, Debug)]
pub struct TestData {
test: bool,
}
mockall::mock! {
pub Format {}
impl ConfigFormat for Format {
type Data = TestData;
fn serialize(&self, data: &TestData) -> Result<String, SaveError>;
fn deserialize(&self, data: &str) -> Result<TestData, LoadError>;
}
}
#[test]
fn load_file_is_non_existent_returns_io_error() {
let path = TempDir::default().join("non-exist");
let format = MockFormat::new();
assert!(matches!(format.try_load(&path), Err(LoadError::Io(_))))
}
#[test]
fn load_file_is_dir_returns_io_error() {
let path = TempDir::default();
let format = MockFormat::new();
assert!(matches!(format.try_load(&path), Err(LoadError::Io(_))))
}
#[test]
fn load_file_exists_contains_garbage_returns_serde_error() {
let path = TempDir::default();
let path = path.join("test.txt");
{
let mut file = File::create(&path).unwrap();
file.write_all("garbage data".as_bytes()).unwrap();
}
let mut format = MockFormat::new();
format
.expect_deserialize()
.times(1)
.returning(|_| Err(LoadError::Deserialize(anyhow!(""))));
assert!(matches!(
format.try_load(&path),
Err(LoadError::Deserialize(_))
))
}
#[test]
fn save_file_is_dir_returns_io_error() {
let path = TempDir::default();
let data = TestData::default();
let mut format = MockFormat::default();
format
.expect_serialize()
.times(1)
.returning(|_| Ok(String::from("")));
assert!(matches!(
format.try_save(&path, &data),
Err(SaveError::Io(_))
))
}
#[test]
fn save_file_non_existent_file_is_created() {
let path = TempDir::default();
let path = path.join("test.txt");
assert!(!path.exists());
let data = TestData::default();
let mut format = MockFormat::default();
format
.expect_serialize()
.times(1)
.returning(|_| Ok(String::from("")));
format
.expect_deserialize()
.times(1)
.returning(|_| Ok(TestData::default()));
assert!(matches!(format.try_save(&path, &data), Ok(())));
assert!(path.exists());
assert_eq!(data, format.try_load(&path).unwrap());
}
#[test]
fn save_file_existent_file_is_created() {
let path = TempDir::default();
let path = path.join("test.txt");
{
let mut file = File::create(&path).unwrap();
file.write_all("for_such_invalid_yml:{]".as_bytes())
.unwrap();
}
assert!(path.exists());
let data = TestData::default();
let mut format = MockFormat::default();
format
.expect_serialize()
.times(1)
.returning(|_| Ok(String::from("")));
format
.expect_deserialize()
.times(1)
.returning(|_| Ok(TestData::default()));
assert!(matches!(format.try_save(&path, &data), Ok(())));
assert!(path.exists());
assert_eq!(data, format.try_load(&path).unwrap());
}
}