Generic json database backend

This commit is contained in:
Wojciech Kozlowski 2023-04-13 15:04:28 +02:00
parent ea736cebed
commit 23d4aba09f
5 changed files with 23 additions and 27 deletions

View File

@ -94,6 +94,7 @@ mod tests {
use mockall::predicate; use mockall::predicate;
use crate::{ use crate::{
collection::Collection,
database::{self, MockDatabase}, database::{self, MockDatabase},
library::{self, MockLibrary, Query}, library::{self, MockLibrary, Query},
tests::COLLECTION, tests::COLLECTION,
@ -122,7 +123,7 @@ mod tests {
.expect_write() .expect_write()
.with(predicate::eq(database_input)) .with(predicate::eq(database_input))
.times(1) .times(1)
.return_once(|_| database_result); .return_once(|_: &Collection| database_result);
let mut collection_manager = MhCollectionManager::new(library, database); let mut collection_manager = MhCollectionManager::new(library, database);
@ -164,7 +165,7 @@ mod tests {
database database
.expect_write() .expect_write()
.times(1) .times(1)
.return_once(|_| database_result); .return_once(|_: &Collection| database_result);
let mut collection_manager = MhCollectionManager::new(library, database); let mut collection_manager = MhCollectionManager::new(library, database);

View File

@ -3,7 +3,8 @@
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use crate::collection::Collection; use serde::de::DeserializeOwned;
use serde::Serialize;
#[cfg(test)] #[cfg(test)]
use mockall::automock; use mockall::automock;
@ -27,25 +28,25 @@ pub trait JsonDatabaseBackend {
} }
/// JSON database. /// JSON database.
pub struct JsonDatabase { pub struct JsonDatabase<JDB> {
backend: Box<dyn JsonDatabaseBackend + Send + Sync>, backend: JDB,
} }
impl JsonDatabase { impl<JDB: JsonDatabaseBackend> JsonDatabase<JDB> {
/// Create a new JSON database with the provided backend, e.g. [`JsonDatabaseFileBackend`]. /// Create a new JSON database with the provided backend, e.g. [`JsonDatabaseFileBackend`].
pub fn new(backend: Box<dyn JsonDatabaseBackend + Send + Sync>) -> Self { pub fn new(backend: JDB) -> Self {
JsonDatabase { backend } JsonDatabase { backend }
} }
} }
impl Database for JsonDatabase { impl<JDB: JsonDatabaseBackend> Database for JsonDatabase<JDB> {
fn read(&self, collection: &mut Collection) -> Result<(), Error> { fn read<D: DeserializeOwned>(&self, collection: &mut D) -> Result<(), Error> {
let serialized = self.backend.read()?; let serialized = self.backend.read()?;
*collection = serde_json::from_str(&serialized)?; *collection = serde_json::from_str(&serialized)?;
Ok(()) Ok(())
} }
fn write(&mut self, collection: &Collection) -> Result<(), Error> { fn write<S: Serialize>(&mut self, collection: &S) -> Result<(), Error> {
let serialized = serde_json::to_string(&collection)?; let serialized = serde_json::to_string(&collection)?;
self.backend.write(&serialized)?; self.backend.write(&serialized)?;
Ok(()) Ok(())
@ -162,9 +163,7 @@ mod tests {
.times(1) .times(1)
.return_once(|_| Ok(())); .return_once(|_| Ok(()));
JsonDatabase::new(Box::new(backend)) JsonDatabase::new(backend).write(&write_data).unwrap();
.write(&write_data)
.unwrap();
} }
#[test] #[test]
@ -176,9 +175,7 @@ mod tests {
backend.expect_read().times(1).return_once(|| result); backend.expect_read().times(1).return_once(|| result);
let mut read_data: Vec<Artist> = vec![]; let mut read_data: Vec<Artist> = vec![];
JsonDatabase::new(Box::new(backend)) JsonDatabase::new(backend).read(&mut read_data).unwrap();
.read(&mut read_data)
.unwrap();
assert_eq!(read_data, expected); assert_eq!(read_data, expected);
} }
@ -197,7 +194,7 @@ mod tests {
.return_once(|_| Ok(())); .return_once(|_| Ok(()));
backend.expect_read().times(1).return_once(|| result); backend.expect_read().times(1).return_once(|| result);
let mut database = JsonDatabase::new(Box::new(backend)); let mut database = JsonDatabase::new(backend);
let write_data = COLLECTION.to_owned(); let write_data = COLLECTION.to_owned();
let mut read_data: Vec<Artist> = vec![]; let mut read_data: Vec<Artist> = vec![];

View File

@ -2,11 +2,11 @@
use std::fmt; use std::fmt;
use serde::{de::DeserializeOwned, Serialize};
#[cfg(test)] #[cfg(test)]
use mockall::automock; use mockall::automock;
use crate::collection::Collection;
pub mod json; pub mod json;
/// Error type for database calls. /// Error type for database calls.
@ -39,8 +39,8 @@ impl From<std::io::Error> for Error {
#[cfg_attr(test, automock)] #[cfg_attr(test, automock)]
pub trait Database { pub trait Database {
/// Read collection from the database. /// Read collection from the database.
fn read(&self, collection: &mut Collection) -> Result<(), Error>; fn read<D: DeserializeOwned + 'static>(&self, collection: &mut D) -> Result<(), Error>;
/// Write collection to the database. /// Write collection to the database.
fn write(&mut self, collection: &Collection) -> Result<(), Error>; fn write<S: Serialize + 'static>(&mut self, collection: &S) -> Result<(), Error>;
} }

View File

@ -45,9 +45,7 @@ fn main() {
BeetsLibraryCommandExecutor::default().config(opt.beets_config_file_path.as_deref()), BeetsLibraryCommandExecutor::default().config(opt.beets_config_file_path.as_deref()),
); );
let database = JsonDatabase::new(Box::new(JsonDatabaseFileBackend::new( let database = JsonDatabase::new(JsonDatabaseFileBackend::new(&opt.database_file_path));
&opt.database_file_path,
)));
let collection_manager = MhCollectionManager::new(beets, database); let collection_manager = MhCollectionManager::new(beets, database);

View File

@ -20,7 +20,7 @@ fn write() {
let file = NamedTempFile::new().unwrap(); let file = NamedTempFile::new().unwrap();
let backend = JsonDatabaseFileBackend::new(file.path()); let backend = JsonDatabaseFileBackend::new(file.path());
let mut database = JsonDatabase::new(Box::new(backend)); let mut database = JsonDatabase::new(backend);
let write_data = COLLECTION.to_owned(); let write_data = COLLECTION.to_owned();
database.write(&write_data).unwrap(); database.write(&write_data).unwrap();
@ -34,7 +34,7 @@ fn write() {
#[test] #[test]
fn read() { fn read() {
let backend = JsonDatabaseFileBackend::new(&*DATABASE_TEST_FILE); let backend = JsonDatabaseFileBackend::new(&*DATABASE_TEST_FILE);
let database = JsonDatabase::new(Box::new(backend)); let database = JsonDatabase::new(backend);
let mut read_data: Vec<Artist> = vec![]; let mut read_data: Vec<Artist> = vec![];
database.read(&mut read_data).unwrap(); database.read(&mut read_data).unwrap();
@ -48,7 +48,7 @@ fn reverse() {
let file = NamedTempFile::new().unwrap(); let file = NamedTempFile::new().unwrap();
let backend = JsonDatabaseFileBackend::new(file.path()); let backend = JsonDatabaseFileBackend::new(file.path());
let mut database = JsonDatabase::new(Box::new(backend)); let mut database = JsonDatabase::new(backend);
let write_data = COLLECTION.to_owned(); let write_data = COLLECTION.to_owned();
database.write(&write_data).unwrap(); database.write(&write_data).unwrap();