Add a SQLite database backend #265
@ -13,7 +13,7 @@ pub enum AlbumLibIdDef {
|
|||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct SerdeAlbumLibId(#[serde(with = "AlbumLibIdDef")] AlbumLibId);
|
pub struct SerdeAlbumLibId(#[serde(with = "AlbumLibIdDef")] AlbumLibId);
|
||||||
|
|
||||||
impl From<SerdeAlbumLibId> for AlbumLibId {
|
impl From<SerdeAlbumLibId> for AlbumLibId {
|
||||||
@ -36,7 +36,7 @@ pub struct AlbumDateDef {
|
|||||||
day: Option<u8>,
|
day: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct SerdeAlbumDate(#[serde(with = "AlbumDateDef")] pub AlbumDate);
|
pub struct SerdeAlbumDate(#[serde(with = "AlbumDateDef")] pub AlbumDate);
|
||||||
|
|
||||||
impl From<SerdeAlbumDate> for AlbumDate {
|
impl From<SerdeAlbumDate> for AlbumDate {
|
||||||
@ -69,7 +69,7 @@ pub enum AlbumPrimaryTypeDef {
|
|||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct SerdeAlbumPrimaryType(#[serde(with = "AlbumPrimaryTypeDef")] AlbumPrimaryType);
|
pub struct SerdeAlbumPrimaryType(#[serde(with = "AlbumPrimaryTypeDef")] AlbumPrimaryType);
|
||||||
|
|
||||||
impl From<SerdeAlbumPrimaryType> for AlbumPrimaryType {
|
impl From<SerdeAlbumPrimaryType> for AlbumPrimaryType {
|
||||||
@ -101,7 +101,7 @@ pub enum AlbumSecondaryTypeDef {
|
|||||||
FieldRecording,
|
FieldRecording,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
pub struct SerdeAlbumSecondaryType(#[serde(with = "AlbumSecondaryTypeDef")] AlbumSecondaryType);
|
pub struct SerdeAlbumSecondaryType(#[serde(with = "AlbumSecondaryTypeDef")] AlbumSecondaryType);
|
||||||
|
|
||||||
impl From<SerdeAlbumSecondaryType> for AlbumSecondaryType {
|
impl From<SerdeAlbumSecondaryType> for AlbumSecondaryType {
|
||||||
|
@ -22,7 +22,7 @@ impl<'a> From<&'a Collection> for SerializeDatabase<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize, PartialEq, Eq)]
|
||||||
pub struct SerializeArtist<'a> {
|
pub struct SerializeArtist<'a> {
|
||||||
pub name: &'a str,
|
pub name: &'a str,
|
||||||
pub sort: Option<&'a str>,
|
pub sort: Option<&'a str>,
|
||||||
@ -31,7 +31,7 @@ pub struct SerializeArtist<'a> {
|
|||||||
pub albums: Vec<SerializeAlbum<'a>>,
|
pub albums: Vec<SerializeAlbum<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize, PartialEq, Eq)]
|
||||||
pub struct SerializeAlbum<'a> {
|
pub struct SerializeAlbum<'a> {
|
||||||
pub title: &'a str,
|
pub title: &'a str,
|
||||||
pub lib_id: SerdeAlbumLibId,
|
pub lib_id: SerdeAlbumLibId,
|
||||||
@ -42,12 +42,12 @@ pub struct SerializeAlbum<'a> {
|
|||||||
pub secondary_types: Vec<SerdeAlbumSecondaryType>,
|
pub secondary_types: Vec<SerdeAlbumSecondaryType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize, PartialEq, Eq)]
|
||||||
pub struct SerializeMbRefOption<'a>(
|
pub struct SerializeMbRefOption<'a>(
|
||||||
#[serde(with = "MbRefOptionDef")] MbRefOption<SerializeMbid<'a>>,
|
#[serde(with = "MbRefOptionDef")] MbRefOption<SerializeMbid<'a>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct SerializeMbid<'a>(&'a Mbid);
|
pub struct SerializeMbid<'a>(&'a Mbid);
|
||||||
|
|
||||||
impl<'a, T: IMusicBrainzRef> From<&'a MbRefOption<T>> for SerializeMbRefOption<'a> {
|
impl<'a, T: IMusicBrainzRef> From<&'a MbRefOption<T>> for SerializeMbRefOption<'a> {
|
||||||
|
@ -144,9 +144,9 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> SqlDatabase<SDB> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn drop_tables<Tx: ISqlTransactionBackend>(tx: &Tx) -> Result<(), Error> {
|
fn drop_tables<Tx: ISqlTransactionBackend>(tx: &Tx) -> Result<(), Error> {
|
||||||
tx.drop_database_metadata_table()?;
|
|
||||||
tx.drop_artists_table()?;
|
|
||||||
tx.drop_albums_table()?;
|
tx.drop_albums_table()?;
|
||||||
|
tx.drop_artists_table()?;
|
||||||
|
tx.drop_database_metadata_table()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,103 +201,186 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> IDatabase for SqlDatabase<SDB>
|
|||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
// pub mod testmod;
|
// pub mod testmod;
|
||||||
|
|
||||||
// #[cfg(test)]
|
#[cfg(test)]
|
||||||
// mod tests {
|
mod tests {
|
||||||
// use std::collections::HashMap;
|
// use std::collections::HashMap;
|
||||||
|
|
||||||
// use mockall::predicate;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
// use crate::core::{
|
use mockall::{predicate, Sequence};
|
||||||
// collection::{artist::Artist, Collection},
|
|
||||||
// testmod::FULL_COLLECTION,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// use super::*;
|
use crate::core::{
|
||||||
// use testmod::DATABASE_JSON;
|
// collection::{artist::Artist, Collection},
|
||||||
|
testmod::FULL_COLLECTION,
|
||||||
|
};
|
||||||
|
|
||||||
// fn expected() -> Collection {
|
use super::*;
|
||||||
// let mut expected = FULL_COLLECTION.to_owned();
|
|
||||||
// for artist in expected.iter_mut() {
|
|
||||||
// for album in artist.albums.iter_mut() {
|
|
||||||
// album.tracks.clear();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// expected
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
// fn expected() -> Collection {
|
||||||
// fn save() {
|
// let mut expected = FULL_COLLECTION.to_owned();
|
||||||
// let write_data = FULL_COLLECTION.to_owned();
|
// for artist in expected.iter_mut() {
|
||||||
// let input = DATABASE_JSON.to_owned();
|
// for album in artist.albums.iter_mut() {
|
||||||
|
// album.tracks.clear();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// expected
|
||||||
|
// }
|
||||||
|
|
||||||
// let mut backend = MockISqlDatabaseBackend::new();
|
struct SqlDatabaseTestBackend {
|
||||||
// backend
|
pub txs: VecDeque<MockISqlTransactionBackend>,
|
||||||
// .expect_write()
|
}
|
||||||
// .with(predicate::eq(input))
|
|
||||||
// .times(1)
|
|
||||||
// .return_once(|_| Ok(()));
|
|
||||||
|
|
||||||
// SqlDatabase::new(backend).save(&write_data).unwrap();
|
impl SqlDatabaseTestBackend {
|
||||||
// }
|
fn new(txs: VecDeque<MockISqlTransactionBackend>) -> Self {
|
||||||
|
SqlDatabaseTestBackend { txs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// #[test]
|
impl ISqlDatabaseBackend<'_> for SqlDatabaseTestBackend {
|
||||||
// fn load() {
|
type Tx = MockISqlTransactionBackend;
|
||||||
// let expected = expected();
|
|
||||||
// let result = Ok(DATABASE_JSON.to_owned());
|
|
||||||
// eprintln!("{DATABASE_JSON}");
|
|
||||||
|
|
||||||
// let mut backend = MockISqlDatabaseBackend::new();
|
fn transaction(&mut self) -> Result<Self::Tx, Error> {
|
||||||
// backend.expect_read().times(1).return_once(|| result);
|
Ok(self.txs.pop_front().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// let read_data: Vec<Artist> = SqlDatabase::new(backend).load().unwrap();
|
macro_rules! then {
|
||||||
|
($tx:ident, $seq:ident, $expect:ident) => {
|
||||||
|
$tx.$expect().times(1).in_sequence(&mut $seq)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// assert_eq!(read_data, expected);
|
macro_rules! then0 {
|
||||||
// }
|
($tx:ident, $seq:ident, $expect:ident) => {
|
||||||
|
then!($tx, $seq, $expect).return_once(|| Ok(()))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// #[test]
|
macro_rules! then1 {
|
||||||
// fn reverse() {
|
($tx:ident, $seq:ident, $expect:ident) => {
|
||||||
// let input = DATABASE_JSON.to_owned();
|
then!($tx, $seq, $expect).return_once(|_| Ok(()))
|
||||||
// let result = Ok(input.clone());
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// let mut backend = MockISqlDatabaseBackend::new();
|
macro_rules! then2 {
|
||||||
// backend
|
($tx:ident, $seq:ident, $expect:ident) => {
|
||||||
// .expect_write()
|
then!($tx, $seq, $expect).return_once(|_, _| Ok(()))
|
||||||
// .with(predicate::eq(input))
|
};
|
||||||
// .times(1)
|
}
|
||||||
// .return_once(|_| Ok(()));
|
|
||||||
// backend.expect_read().times(1).return_once(|| result);
|
|
||||||
// let mut database = SqlDatabase::new(backend);
|
|
||||||
|
|
||||||
// let write_data = FULL_COLLECTION.to_owned();
|
macro_rules! expect_create {
|
||||||
// database.save(&write_data).unwrap();
|
($tx:ident, $seq:ident) => {
|
||||||
// let read_data: Vec<Artist> = database.load().unwrap();
|
let mut seq = Sequence::new();
|
||||||
|
then0!($tx, seq, expect_create_database_metadata_table);
|
||||||
|
then0!($tx, seq, expect_create_artists_table);
|
||||||
|
then0!($tx, seq, expect_create_albums_table);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// // Album information is not saved to disk.
|
macro_rules! expect_drop {
|
||||||
// let expected = expected();
|
($tx:ident, $seq:ident) => {
|
||||||
// assert_eq!(read_data, expected);
|
let mut seq = Sequence::new();
|
||||||
// }
|
then0!($tx, seq, expect_drop_albums_table);
|
||||||
|
then0!($tx, seq, expect_drop_artists_table);
|
||||||
|
then0!($tx, seq, expect_drop_database_metadata_table);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// #[test]
|
fn database(txs: VecDeque<MockISqlTransactionBackend>) -> SqlDatabase<SqlDatabaseTestBackend> {
|
||||||
// fn load_errors() {
|
let mut backend = SqlDatabaseTestBackend::new(txs);
|
||||||
// let json = String::from("");
|
let mut tx = MockISqlTransactionBackend::new();
|
||||||
// let serde_err = serde_json::from_str::<DeserializeDatabase>(&json);
|
|
||||||
// assert!(serde_err.is_err());
|
|
||||||
|
|
||||||
// let serde_err: LoadError = serde_err.unwrap_err().into();
|
let mut seq = Sequence::new();
|
||||||
// assert!(!serde_err.to_string().is_empty());
|
expect_create!(tx, seq);
|
||||||
// assert!(!format!("{:?}", serde_err).is_empty());
|
then0!(tx, seq, expect_commit);
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
backend.txs.push_front(tx);
|
||||||
// fn save_errors() {
|
SqlDatabase::new(backend).unwrap()
|
||||||
// // serde_json will raise an error as it has certain requirements on keys.
|
}
|
||||||
// let mut object = HashMap::<Result<(), ()>, String>::new();
|
|
||||||
// object.insert(Ok(()), String::from("string"));
|
|
||||||
// let serde_err = serde_json::to_string(&object);
|
|
||||||
// assert!(serde_err.is_err());
|
|
||||||
|
|
||||||
// let serde_err: SaveError = serde_err.unwrap_err().into();
|
#[test]
|
||||||
// assert!(!serde_err.to_string().is_empty());
|
fn save() {
|
||||||
// assert!(!format!("{:?}", serde_err).is_empty());
|
let write_data = FULL_COLLECTION.to_owned();
|
||||||
// }
|
|
||||||
// }
|
let mut tx = MockISqlTransactionBackend::new();
|
||||||
|
|
||||||
|
let mut seq = Sequence::new();
|
||||||
|
expect_drop!(tx, seq);
|
||||||
|
expect_create!(tx, seq);
|
||||||
|
then1!(tx, seq, expect_insert_database_version).with(predicate::eq(V20250103));
|
||||||
|
for artist in write_data.iter() {
|
||||||
|
let ac = artist.clone();
|
||||||
|
then1!(tx, seq, expect_insert_artist)
|
||||||
|
.withf(move |a| a == &Into::<SerializeArtist>::into(&ac));
|
||||||
|
for album in artist.albums.iter() {
|
||||||
|
let (nc, ac) = (artist.meta.id.name.clone(), album.clone());
|
||||||
|
then2!(tx, seq, expect_insert_album)
|
||||||
|
.withf(move |n, a| n == nc && a == &Into::<SerializeAlbum>::into(&ac));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
then0!(tx, seq, expect_commit);
|
||||||
|
|
||||||
|
assert!(database(VecDeque::from([tx])).save(&write_data).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn load() {
|
||||||
|
// let expected = expected();
|
||||||
|
// let result = Ok(DATABASE_JSON.to_owned());
|
||||||
|
// eprintln!("{DATABASE_JSON}");
|
||||||
|
|
||||||
|
// let mut backend = MockISqlDatabaseBackend::new();
|
||||||
|
// backend.expect_read().times(1).return_once(|| result);
|
||||||
|
|
||||||
|
// let read_data: Vec<Artist> = SqlDatabase::new(backend).load().unwrap();
|
||||||
|
|
||||||
|
// assert_eq!(read_data, expected);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn reverse() {
|
||||||
|
// let input = DATABASE_JSON.to_owned();
|
||||||
|
// let result = Ok(input.clone());
|
||||||
|
|
||||||
|
// let mut backend = MockISqlDatabaseBackend::new();
|
||||||
|
// backend
|
||||||
|
// .expect_write()
|
||||||
|
// .with(predicate::eq(input))
|
||||||
|
// .times(1)
|
||||||
|
// .return_once(|_| Ok(()));
|
||||||
|
// backend.expect_read().times(1).return_once(|| result);
|
||||||
|
// let mut database = SqlDatabase::new(backend);
|
||||||
|
|
||||||
|
// let write_data = FULL_COLLECTION.to_owned();
|
||||||
|
// database.save(&write_data).unwrap();
|
||||||
|
// let read_data: Vec<Artist> = database.load().unwrap();
|
||||||
|
|
||||||
|
// // Album information is not saved to disk.
|
||||||
|
// let expected = expected();
|
||||||
|
// assert_eq!(read_data, expected);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn load_errors() {
|
||||||
|
// let json = String::from("");
|
||||||
|
// let serde_err = serde_json::from_str::<DeserializeDatabase>(&json);
|
||||||
|
// assert!(serde_err.is_err());
|
||||||
|
|
||||||
|
// let serde_err: LoadError = serde_err.unwrap_err().into();
|
||||||
|
// assert!(!serde_err.to_string().is_empty());
|
||||||
|
// assert!(!format!("{:?}", serde_err).is_empty());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn save_errors() {
|
||||||
|
// // serde_json will raise an error as it has certain requirements on keys.
|
||||||
|
// let mut object = HashMap::<Result<(), ()>, String>::new();
|
||||||
|
// object.insert(Ok(()), String::from("string"));
|
||||||
|
// let serde_err = serde_json::to_string(&object);
|
||||||
|
// assert!(serde_err.is_err());
|
||||||
|
|
||||||
|
// let serde_err: SaveError = serde_err.unwrap_err().into();
|
||||||
|
// assert!(!serde_err.to_string().is_empty());
|
||||||
|
// assert!(!format!("{:?}", serde_err).is_empty());
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user