diff --git a/src/external/database/serde/deserialize.rs b/src/external/database/serde/deserialize.rs index 007099e..89b8ef2 100644 --- a/src/external/database/serde/deserialize.rs +++ b/src/external/database/serde/deserialize.rs @@ -35,8 +35,8 @@ impl From for Collection { #[derive(Clone, Debug, Deserialize)] pub struct DeserializeArtist { pub name: String, - pub sort: Option, pub musicbrainz: DeserializeMbRefOption, + pub sort: Option, pub properties: HashMap>, pub albums: Vec, } @@ -45,9 +45,9 @@ pub struct DeserializeArtist { pub struct DeserializeAlbum { pub title: String, pub lib_id: SerdeAlbumLibId, + pub musicbrainz: DeserializeMbRefOption, pub date: SerdeAlbumDate, pub seq: u8, - pub musicbrainz: DeserializeMbRefOption, pub primary_type: Option, pub secondary_types: Vec, } diff --git a/src/external/database/serde/serialize.rs b/src/external/database/serde/serialize.rs index 197fc71..16d6dbb 100644 --- a/src/external/database/serde/serialize.rs +++ b/src/external/database/serde/serialize.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::HashMap; use serde::Serialize; @@ -25,9 +25,9 @@ impl<'a> From<&'a Collection> for SerializeDatabase<'a> { #[derive(Debug, Serialize, PartialEq, Eq)] pub struct SerializeArtist<'a> { pub name: &'a str, - pub sort: Option<&'a str>, + pub sort: &'a Option, pub musicbrainz: SerializeMbRefOption<'a>, - pub properties: BTreeMap<&'a str, &'a Vec>, + pub properties: &'a HashMap>, pub albums: Vec>, } @@ -35,9 +35,9 @@ pub struct SerializeArtist<'a> { pub struct SerializeAlbum<'a> { pub title: &'a str, pub lib_id: SerdeAlbumLibId, + pub musicbrainz: SerializeMbRefOption<'a>, pub date: SerdeAlbumDate, pub seq: u8, - pub musicbrainz: SerializeMbRefOption<'a>, pub primary_type: Option, pub secondary_types: Vec, } @@ -75,15 +75,9 @@ impl<'a> From<&'a Artist> for SerializeArtist<'a> { fn from(artist: &'a Artist) -> Self { SerializeArtist { name: &artist.meta.id.name, - sort: artist.meta.sort.as_deref(), musicbrainz: (&artist.meta.id.mb_ref).into(), - properties: artist - .meta - .info - .properties - .iter() - .map(|(k, v)| (k.as_ref(), v)) - .collect(), + sort: &artist.meta.sort, + properties: &artist.meta.info.properties, albums: artist.albums.iter().map(Into::into).collect(), } } @@ -94,9 +88,9 @@ impl<'a> From<&'a Album> for SerializeAlbum<'a> { SerializeAlbum { title: &album.meta.id.title, lib_id: album.meta.id.lib_id.into(), + musicbrainz: (&album.meta.id.mb_ref).into(), date: album.meta.date.into(), seq: album.meta.seq.0, - musicbrainz: (&album.meta.id.mb_ref).into(), primary_type: album.meta.info.primary_type.map(Into::into), secondary_types: album .meta diff --git a/src/external/database/sql/backend.rs b/src/external/database/sql/backend.rs index f7af8fe..7d1b00a 100644 --- a/src/external/database/sql/backend.rs +++ b/src/external/database/sql/backend.rs @@ -105,9 +105,9 @@ impl ISqlTransactionBackend for SqlTransactionSqliteBackend<'_> { fn create_artists_table(&self) -> Result<(), Error> { let mut stmt = self.prepare( "CREATE TABLE IF NOT EXISTS artists ( - name TEXT NOT NULL PRIMARY KEY, - sort TEXT NULL, + name TEXT NOT NULL, mbid JSON NOT NULL DEFAULT '\"None\"', + sort TEXT NULL, properties JSON NOT NULL DEFAULT '{}' )", ); @@ -131,8 +131,7 @@ impl ISqlTransactionBackend for SqlTransactionSqliteBackend<'_> { day INT NULL, seq INT NOT NULL, primary_type JSON NOT NULL DEFAULT 'null', - secondary_types JSON NOT NULL DEFAULT '[]', - FOREIGN KEY (artist_name) REFERENCES artists(name) ON DELETE CASCADE ON UPDATE NO ACTION + secondary_types JSON NOT NULL DEFAULT '[]' )", ); Self::execute(&mut stmt, ()) @@ -178,12 +177,18 @@ impl ISqlTransactionBackend for SqlTransactionSqliteBackend<'_> { } fn select_all_artists(&self) -> Result, Error> { - let mut stmt = self.prepare_cached("SELECT name, sort, mbid, properties FROM artists"); + let mut stmt = self.prepare_cached("SELECT name, mbid, sort, properties FROM artists"); let mut rows = Self::query(&mut stmt, ())?; let mut artists = vec![]; while let Some(row) = Self::next_row(&mut rows)? { - artists.push(row.try_into()?); + artists.push(DeserializeArtist { + name: Self::get_value(row, 0)?, + musicbrainz: serde_json::from_str(&Self::get_value::(row, 1)?)?, + sort: Self::get_value(row, 2)?, + properties: serde_json::from_str(&Self::get_value::(row, 3)?)?, + albums: vec![], + }); } Ok(artists) @@ -214,52 +219,28 @@ impl ISqlTransactionBackend for SqlTransactionSqliteBackend<'_> { fn select_artist_albums(&self, artist_name: &str) -> Result, Error> { let mut stmt = self.prepare_cached( - "SELECT title, lib_id, year, month, day, seq, mbid, primary_type, secondary_types + "SELECT title, lib_id, mbid, year, month, day, seq, primary_type, secondary_types FROM albums WHERE artist_name = ?1", ); let mut rows = Self::query(&mut stmt, [artist_name])?; let mut albums = vec![]; while let Some(row) = Self::next_row(&mut rows)? { - albums.push(row.try_into()?); + albums.push(DeserializeAlbum { + title: Self::get_value(row, 0)?, + lib_id: serde_json::from_str(&Self::get_value::(row, 1)?)?, + musicbrainz: serde_json::from_str(&Self::get_value::(row, 2)?)?, + date: SerdeAlbumDate(AlbumDate::new( + Self::get_value(row, 3)?, + Self::get_value(row, 4)?, + Self::get_value(row, 5)?, + )), + seq: Self::get_value(row, 6)?, + primary_type: serde_json::from_str(&Self::get_value::(row, 7)?)?, + secondary_types: serde_json::from_str(&Self::get_value::(row, 8)?)?, + }); } Ok(albums) } } - -impl TryFrom<&Row<'_>> for DeserializeArtist { - type Error = Error; - - fn try_from(row: &Row<'_>) -> Result { - type Backend<'a> = SqlTransactionSqliteBackend<'a>; - Ok(DeserializeArtist { - name: Backend::get_value(row, 0)?, - sort: Backend::get_value(row, 1)?, - musicbrainz: serde_json::from_str(&Backend::get_value::(row, 2)?)?, - properties: serde_json::from_str(&Backend::get_value::(row, 3)?)?, - albums: vec![], - }) - } -} - -impl TryFrom<&Row<'_>> for DeserializeAlbum { - type Error = Error; - - fn try_from(row: &Row<'_>) -> Result { - type Backend<'a> = SqlTransactionSqliteBackend<'a>; - Ok(DeserializeAlbum { - title: Backend::get_value(row, 0)?, - lib_id: serde_json::from_str(&Backend::get_value::(row, 1)?)?, - date: SerdeAlbumDate(AlbumDate::new( - Backend::get_value(row, 2)?, - Backend::get_value(row, 3)?, - Backend::get_value(row, 4)?, - )), - seq: Backend::get_value(row, 5)?, - musicbrainz: serde_json::from_str(&Backend::get_value::(row, 6)?)?, - primary_type: serde_json::from_str(&Backend::get_value::(row, 7)?)?, - secondary_types: serde_json::from_str(&Backend::get_value::(row, 8)?)?, - }) - } -} diff --git a/src/main.rs b/src/main.rs index b7efbfa..655c47b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use musichoard::{ track::TrackFormat, }, external::{ - database::json::{backend::JsonDatabaseFileBackend, JsonDatabase}, + database::sql::{backend::SqlDatabaseSqliteBackend, SqlDatabase}, library::beets::{ executor::{ssh::BeetsLibrarySshExecutor, BeetsLibraryProcessExecutor}, BeetsLibrary, @@ -141,7 +141,10 @@ fn with_database( { Ok(f) => { drop(f); - JsonDatabase::new(JsonDatabaseFileBackend::new(&db_opt.database_file_path)) + let db_exec = SqlDatabaseSqliteBackend::new(&db_opt.database_file_path) + .expect("failed to initialise SQLite database backend"); + SqlDatabase::new(db_exec) + .expect("failed to open new database") .save(&vec![]) .expect("failed to create empty database"); } @@ -151,8 +154,10 @@ fn with_database( }, } - let db_exec = JsonDatabaseFileBackend::new(&db_opt.database_file_path); - with(builder.set_database(JsonDatabase::new(db_exec))); + let db_exec = SqlDatabaseSqliteBackend::new(&db_opt.database_file_path) + .expect("failed to initialise SQLite database backend"); + let db = SqlDatabase::new(db_exec).expect("failed to open database"); + with(builder.set_database(db)); }; } diff --git a/tests/files/database/database.db b/tests/files/database/database.db index bf7a0cb..d611935 100644 Binary files a/tests/files/database/database.db and b/tests/files/database/database.db differ diff --git a/tests/lib.rs b/tests/lib.rs index fbcc6e9..ab9c7bb 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -8,7 +8,7 @@ mod testlib; use musichoard::{ external::{ - database::json::{backend::JsonDatabaseFileBackend, JsonDatabase}, + database::sql::{backend::SqlDatabaseSqliteBackend, SqlDatabase}, library::beets::{executor::BeetsLibraryProcessExecutor, BeetsLibrary}, }, IMusicHoardBase, IMusicHoardDatabase, IMusicHoardLibrary, MusicHoard, @@ -28,8 +28,8 @@ fn merge_library_then_database() { .config(Some(&*library::beets::BEETS_TEST_CONFIG_PATH)); let library = BeetsLibrary::new(executor); - let backend = JsonDatabaseFileBackend::new(&*database::json::DATABASE_TEST_FILE); - let database = JsonDatabase::new(backend); + let backend = SqlDatabaseSqliteBackend::new(&*database::sql::DATABASE_TEST_FILE).unwrap(); + let database = SqlDatabase::new(backend).unwrap(); let mut music_hoard = MusicHoard::new(database, library); @@ -51,8 +51,8 @@ fn merge_database_then_library() { .config(Some(&*library::beets::BEETS_TEST_CONFIG_PATH)); let library = BeetsLibrary::new(executor); - let backend = JsonDatabaseFileBackend::new(&*database::json::DATABASE_TEST_FILE); - let database = JsonDatabase::new(backend); + let backend = SqlDatabaseSqliteBackend::new(&*database::sql::DATABASE_TEST_FILE).unwrap(); + let database = SqlDatabase::new(backend).unwrap(); let mut music_hoard = MusicHoard::new(database, library);