Add a SQLite database backend #265
@ -14,7 +14,7 @@ pub enum AlbumLibIdDef {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct SerdeAlbumLibId(#[serde(with = "AlbumLibIdDef")] AlbumLibId);
|
||||
pub struct SerdeAlbumLibId(#[serde(with = "AlbumLibIdDef")] pub AlbumLibId);
|
||||
|
||||
impl From<SerdeAlbumLibId> for AlbumLibId {
|
||||
fn from(value: SerdeAlbumLibId) -> Self {
|
||||
@ -70,7 +70,7 @@ pub enum AlbumPrimaryTypeDef {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct SerdeAlbumPrimaryType(#[serde(with = "AlbumPrimaryTypeDef")] AlbumPrimaryType);
|
||||
pub struct SerdeAlbumPrimaryType(#[serde(with = "AlbumPrimaryTypeDef")] pub AlbumPrimaryType);
|
||||
|
||||
impl From<SerdeAlbumPrimaryType> for AlbumPrimaryType {
|
||||
fn from(value: SerdeAlbumPrimaryType) -> Self {
|
||||
@ -102,7 +102,7 @@ pub enum AlbumSecondaryTypeDef {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct SerdeAlbumSecondaryType(#[serde(with = "AlbumSecondaryTypeDef")] AlbumSecondaryType);
|
||||
pub struct SerdeAlbumSecondaryType(#[serde(with = "AlbumSecondaryTypeDef")] pub AlbumSecondaryType);
|
||||
|
||||
impl From<SerdeAlbumSecondaryType> for AlbumSecondaryType {
|
||||
fn from(value: SerdeAlbumSecondaryType) -> Self {
|
||||
|
@ -51,10 +51,12 @@ pub struct DeserializeAlbum {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct DeserializeMbRefOption(#[serde(with = "MbRefOptionDef")] MbRefOption<DeserializeMbid>);
|
||||
pub struct DeserializeMbRefOption(
|
||||
#[serde(with = "MbRefOptionDef")] pub MbRefOption<DeserializeMbid>,
|
||||
);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeserializeMbid(Mbid);
|
||||
pub struct DeserializeMbid(pub Mbid);
|
||||
|
||||
macro_rules! impl_from_for_mb_ref_option {
|
||||
($ref:ty) => {
|
||||
|
@ -198,19 +198,21 @@ impl<SDB: for<'conn> ISqlDatabaseBackend<'conn>> IDatabase for SqlDatabase<SDB>
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// pub mod testmod;
|
||||
#[cfg(test)]
|
||||
pub mod testmod;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use mockall::{predicate, Sequence};
|
||||
use rusqlite::Connection;
|
||||
|
||||
use crate::core::{collection::Collection, testmod::FULL_COLLECTION};
|
||||
use crate::{
|
||||
core::{collection::Collection, testmod::FULL_COLLECTION},
|
||||
external::database::sql::testmod::{
|
||||
DATABASE_SQL_ALBUMS, DATABASE_SQL_ARTISTS, DATABASE_SQL_VERSION,
|
||||
},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -323,39 +325,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn load() {
|
||||
let path = fs::canonicalize("./src/external/database/sql/testmod.db").unwrap();
|
||||
let db = Connection::open(path).unwrap();
|
||||
|
||||
let mut tx = MockISqlTransactionBackend::new();
|
||||
let mut seq = Sequence::new();
|
||||
|
||||
let version_query = "SELECT value FROM database_metadata WHERE name = 'version'";
|
||||
let version: String = db.query_row(&version_query, (), |row| row.get(0)).unwrap();
|
||||
then!(tx, seq, expect_select_database_version).return_once(|| Ok(Some(version)));
|
||||
then!(tx, seq, expect_select_database_version)
|
||||
.return_once(|| Ok(Some(DATABASE_SQL_VERSION.to_string())));
|
||||
|
||||
let artist_query = "SELECT name, sort, mbid, properties FROM artists";
|
||||
let mut artist_query = db.prepare(&artist_query).unwrap();
|
||||
let mut artist_rows = artist_query.query(()).unwrap();
|
||||
let mut de_artists = vec![];
|
||||
while let Some(row) = artist_rows.next().unwrap() {
|
||||
de_artists.push(row.try_into().unwrap());
|
||||
}
|
||||
let de_artists = DATABASE_SQL_ARTISTS.to_owned();
|
||||
let artists: Collection = de_artists.iter().cloned().map(Into::into).collect();
|
||||
then!(tx, seq, expect_select_all_artists).return_once(|| Ok(de_artists));
|
||||
|
||||
for artist in artists.iter() {
|
||||
let album_query =
|
||||
"SELECT title, lib_id, year, month, day, seq, mbid, primary_type, secondary_types
|
||||
FROM albums WHERE artist_name = ?1";
|
||||
let mut album_query = db.prepare(&album_query).unwrap();
|
||||
let mut album_rows = album_query.query([artist.meta.id.name.clone()]).unwrap();
|
||||
let mut de_albums = vec![];
|
||||
while let Some(row) = album_rows.next().unwrap() {
|
||||
de_albums.push(row.try_into().unwrap());
|
||||
}
|
||||
let de_albums = DATABASE_SQL_ALBUMS.get(&artist.meta.id.name).unwrap();
|
||||
then!(tx, seq, expect_select_artist_albums)
|
||||
.with(predicate::eq(artist.meta.id.name.clone()))
|
||||
.return_once(|_| Ok(de_albums));
|
||||
.return_once(|_| Ok(de_albums.to_owned()));
|
||||
}
|
||||
|
||||
then0!(tx, seq, expect_commit);
|
||||
@ -366,29 +350,6 @@ mod tests {
|
||||
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("");
|
||||
|
202
src/external/database/sql/testmod.rs
Normal file
202
src/external/database/sql/testmod.rs
Normal file
@ -0,0 +1,202 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
collection::{
|
||||
album::{AlbumDate, AlbumLibId, AlbumPrimaryType},
|
||||
musicbrainz::MbRefOption,
|
||||
},
|
||||
external::database::serde::{
|
||||
common::{SerdeAlbumDate, SerdeAlbumLibId, SerdeAlbumPrimaryType},
|
||||
deserialize::{
|
||||
DeserializeAlbum, DeserializeArtist, DeserializeMbRefOption, DeserializeMbid,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub static DATABASE_SQL_VERSION: &str = "V20250103";
|
||||
|
||||
pub static DATABASE_SQL_ARTISTS: Lazy<Vec<DeserializeArtist>> = Lazy::new(|| {
|
||||
vec![
|
||||
DeserializeArtist {
|
||||
name: String::from("Album_Artist ‘A’"),
|
||||
sort: None,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::Some(DeserializeMbid(
|
||||
"00000000-0000-0000-0000-000000000000".try_into().unwrap(),
|
||||
))),
|
||||
properties: HashMap::from([
|
||||
(
|
||||
String::from("MusicButler"),
|
||||
vec![String::from(
|
||||
"https://www.musicbutler.io/artist-page/000000000",
|
||||
)],
|
||||
),
|
||||
(
|
||||
String::from("Qobuz"),
|
||||
vec![String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums",
|
||||
)],
|
||||
),
|
||||
]),
|
||||
albums: vec![],
|
||||
},
|
||||
DeserializeArtist {
|
||||
name: String::from("Album_Artist ‘B’"),
|
||||
sort: None,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::Some(DeserializeMbid(
|
||||
"11111111-1111-1111-1111-111111111111".try_into().unwrap(),
|
||||
))),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/111111111"),
|
||||
String::from("https://www.musicbutler.io/artist-page/111111112"),
|
||||
]),
|
||||
(String::from("Bandcamp"), vec![
|
||||
String::from("https://artist-b.bandcamp.com/")
|
||||
]),
|
||||
(String::from("Qobuz"), vec![
|
||||
String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/artist-b/download-streaming-albums",
|
||||
)
|
||||
]),
|
||||
]),
|
||||
albums: vec![],
|
||||
},
|
||||
DeserializeArtist {
|
||||
name: String::from("The Album_Artist ‘C’"),
|
||||
sort: Some(String::from("Album_Artist ‘C’, The")),
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::CannotHaveMbid),
|
||||
properties: HashMap::new(),
|
||||
albums: vec![],
|
||||
},
|
||||
DeserializeArtist {
|
||||
name: String::from("Album_Artist ‘D’"),
|
||||
sort: None,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::None),
|
||||
properties: HashMap::new(),
|
||||
albums: vec![],
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
pub static DATABASE_SQL_ALBUMS: Lazy<HashMap<String, Vec<DeserializeAlbum>>> = Lazy::new(|| {
|
||||
HashMap::from([
|
||||
(
|
||||
String::from("Album_Artist ‘A’"),
|
||||
vec![
|
||||
DeserializeAlbum {
|
||||
title: String::from("album_title a.a"),
|
||||
lib_id: SerdeAlbumLibId(AlbumLibId::Value(1)),
|
||||
date: SerdeAlbumDate(AlbumDate::new(Some(1998), None, None)),
|
||||
seq: 1,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::Some(DeserializeMbid(
|
||||
"00000000-0000-0000-0000-000000000000".try_into().unwrap(),
|
||||
))),
|
||||
primary_type: Some(SerdeAlbumPrimaryType(AlbumPrimaryType::Album)),
|
||||
secondary_types: vec![],
|
||||
},
|
||||
DeserializeAlbum {
|
||||
title: String::from("album_title a.b"),
|
||||
lib_id: SerdeAlbumLibId(AlbumLibId::Value(2)),
|
||||
date: SerdeAlbumDate(AlbumDate::new(Some(2015), Some(4), None)),
|
||||
seq: 1,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::None),
|
||||
primary_type: Some(SerdeAlbumPrimaryType(AlbumPrimaryType::Album)),
|
||||
secondary_types: vec![],
|
||||
},
|
||||
],
|
||||
),
|
||||
(
|
||||
String::from("Album_Artist ‘B’"),
|
||||
vec![
|
||||
DeserializeAlbum {
|
||||
title: String::from("album_title b.a"),
|
||||
lib_id: SerdeAlbumLibId(AlbumLibId::Value(3)),
|
||||
date: SerdeAlbumDate(AlbumDate::new(Some(2003), Some(6), Some(6))),
|
||||
seq: 1,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::None),
|
||||
primary_type: Some(SerdeAlbumPrimaryType(AlbumPrimaryType::Album)),
|
||||
secondary_types: vec![],
|
||||
},
|
||||
DeserializeAlbum {
|
||||
title: String::from("album_title b.b"),
|
||||
lib_id: SerdeAlbumLibId(AlbumLibId::Value(4)),
|
||||
date: SerdeAlbumDate(AlbumDate::new(Some(2008), None, None)),
|
||||
seq: 3,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::Some(DeserializeMbid(
|
||||
"11111111-1111-1111-1111-111111111111".try_into().unwrap(),
|
||||
))),
|
||||
primary_type: Some(SerdeAlbumPrimaryType(AlbumPrimaryType::Album)),
|
||||
secondary_types: vec![],
|
||||
},
|
||||
DeserializeAlbum {
|
||||
title: String::from("album_title b.c"),
|
||||
lib_id: SerdeAlbumLibId(AlbumLibId::Value(5)),
|
||||
date: SerdeAlbumDate(AlbumDate::new(Some(2009), None, None)),
|
||||
seq: 2,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::Some(DeserializeMbid(
|
||||
"11111111-1111-1111-1111-111111111112".try_into().unwrap(),
|
||||
))),
|
||||
primary_type: Some(SerdeAlbumPrimaryType(AlbumPrimaryType::Album)),
|
||||
secondary_types: vec![],
|
||||
},
|
||||
DeserializeAlbum {
|
||||
title: String::from("album_title b.d"),
|
||||
lib_id: SerdeAlbumLibId(AlbumLibId::Value(6)),
|
||||
date: SerdeAlbumDate(AlbumDate::new(Some(2015), None, None)),
|
||||
seq: 4,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::None),
|
||||
primary_type: Some(SerdeAlbumPrimaryType(AlbumPrimaryType::Album)),
|
||||
secondary_types: vec![],
|
||||
},
|
||||
],
|
||||
),
|
||||
(
|
||||
String::from("The Album_Artist ‘C’"),
|
||||
vec![
|
||||
DeserializeAlbum {
|
||||
title: String::from("album_title c.a"),
|
||||
lib_id: SerdeAlbumLibId(AlbumLibId::Value(7)),
|
||||
date: SerdeAlbumDate(AlbumDate::new(Some(1985), None, None)),
|
||||
seq: 0,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::None),
|
||||
primary_type: Some(SerdeAlbumPrimaryType(AlbumPrimaryType::Album)),
|
||||
secondary_types: vec![],
|
||||
},
|
||||
DeserializeAlbum {
|
||||
title: String::from("album_title c.b"),
|
||||
lib_id: SerdeAlbumLibId(AlbumLibId::Value(8)),
|
||||
date: SerdeAlbumDate(AlbumDate::new(Some(2018), None, None)),
|
||||
seq: 0,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::None),
|
||||
primary_type: Some(SerdeAlbumPrimaryType(AlbumPrimaryType::Album)),
|
||||
secondary_types: vec![],
|
||||
},
|
||||
],
|
||||
),
|
||||
(
|
||||
String::from("Album_Artist ‘D’"),
|
||||
vec![
|
||||
DeserializeAlbum {
|
||||
title: String::from("album_title d.a"),
|
||||
lib_id: SerdeAlbumLibId(AlbumLibId::Value(9)),
|
||||
date: SerdeAlbumDate(AlbumDate::new(Some(1995), None, None)),
|
||||
seq: 0,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::None),
|
||||
primary_type: Some(SerdeAlbumPrimaryType(AlbumPrimaryType::Album)),
|
||||
secondary_types: vec![],
|
||||
},
|
||||
DeserializeAlbum {
|
||||
title: String::from("album_title d.b"),
|
||||
lib_id: SerdeAlbumLibId(AlbumLibId::Value(10)),
|
||||
date: SerdeAlbumDate(AlbumDate::new(Some(2028), None, None)),
|
||||
seq: 0,
|
||||
musicbrainz: DeserializeMbRefOption(MbRefOption::None),
|
||||
primary_type: Some(SerdeAlbumPrimaryType(AlbumPrimaryType::Album)),
|
||||
secondary_types: vec![],
|
||||
},
|
||||
],
|
||||
),
|
||||
])
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user