From c7471bda0e1ed2136e84fae064cab055c0775e81 Mon Sep 17 00:00:00 2001 From: Wojciech Kozlowski Date: Sat, 31 Aug 2024 21:26:39 +0200 Subject: [PATCH] Split artist/album metadata from collection --- src/core/collection/album.rs | 70 +++-- src/core/collection/artist.rs | 134 +++++---- src/core/musichoard/base.rs | 22 +- src/core/musichoard/database.rs | 40 +-- src/core/musichoard/library.rs | 22 +- src/core/testmod.rs | 4 +- src/external/database/json/mod.rs | 2 +- src/external/database/serde/deserialize.rs | 26 +- src/external/database/serde/serialize.rs | 14 +- src/testmod/full.rs | 280 ++++++++++--------- src/testmod/library.rs | 212 ++++++++------ src/tui/app/machine/browse.rs | 4 +- src/tui/app/machine/matches.rs | 4 +- src/tui/app/machine/search.rs | 6 +- src/tui/lib/external/musicbrainz/mod.rs | 6 +- src/tui/testmod.rs | 4 +- src/tui/ui/browse.rs | 14 +- src/tui/ui/display.rs | 16 +- src/tui/ui/info.rs | 10 +- tests/database/json.rs | 2 +- tests/testlib.rs | 308 +++++++++++---------- 21 files changed, 658 insertions(+), 542 deletions(-) diff --git a/src/core/collection/album.rs b/src/core/collection/album.rs index e272894..ab2403e 100644 --- a/src/core/collection/album.rs +++ b/src/core/collection/album.rs @@ -12,20 +12,26 @@ use crate::core::collection::{ /// An album is a collection of tracks that were released together. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Album { + pub meta: AlbumMeta, + pub tracks: Vec, +} + +/// Album metadata. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AlbumMeta { pub id: AlbumId, pub date: AlbumDate, pub seq: AlbumSeq, pub musicbrainz: Option, pub primary_type: Option, pub secondary_types: Vec, - pub tracks: Vec, } impl WithId for Album { type Id = AlbumId; fn id(&self) -> &Self::Id { - &self.id + &self.meta.id } } @@ -139,18 +145,20 @@ impl Album { secondary_types: Vec, ) -> Self { Album { - id: id.into(), - date: date.into(), - seq: AlbumSeq::default(), - musicbrainz: None, - primary_type, - secondary_types, + meta: AlbumMeta { + id: id.into(), + date: date.into(), + seq: AlbumSeq::default(), + musicbrainz: None, + primary_type, + secondary_types, + }, tracks: vec![], } } pub fn get_sort_key(&self) -> (&AlbumDate, &AlbumSeq, &AlbumId) { - (&self.date, &self.seq, &self.id) + (&self.meta.date, &self.meta.seq, &self.meta.id) } pub fn get_status(&self) -> AlbumStatus { @@ -158,19 +166,19 @@ impl Album { } pub fn set_seq(&mut self, seq: AlbumSeq) { - self.seq = seq; + self.meta.seq = seq; } pub fn clear_seq(&mut self) { - self.seq = AlbumSeq::default(); + self.meta.seq = AlbumSeq::default(); } pub fn set_musicbrainz_ref(&mut self, mbref: MbAlbumRef) { - _ = self.musicbrainz.insert(mbref); + _ = self.meta.musicbrainz.insert(mbref); } pub fn clear_musicbrainz_ref(&mut self) { - _ = self.musicbrainz.take(); + _ = self.meta.musicbrainz.take(); } } @@ -188,12 +196,14 @@ impl Ord for Album { impl Merge for Album { fn merge_in_place(&mut self, other: Self) { - assert_eq!(self.id, other.id); - self.seq = std::cmp::max(self.seq, other.seq); + assert_eq!(self.meta.id, other.meta.id); + self.meta.seq = std::cmp::max(self.meta.seq, other.meta.seq); - self.musicbrainz = self.musicbrainz.take().or(other.musicbrainz); - self.primary_type = self.primary_type.take().or(other.primary_type); - self.secondary_types.merge_in_place(other.secondary_types); + self.meta.musicbrainz = self.meta.musicbrainz.take().or(other.meta.musicbrainz); + self.meta.primary_type = self.meta.primary_type.take().or(other.meta.primary_type); + self.meta + .secondary_types + .merge_in_place(other.meta.secondary_types); let tracks = mem::take(&mut self.tracks); self.tracks = MergeSorted::new(tracks.into_iter(), other.tracks.into_iter()).collect(); @@ -266,28 +276,28 @@ mod tests { fn set_clear_seq() { let mut album = Album::new("An album", AlbumDate::default(), None, vec![]); - assert_eq!(album.seq, AlbumSeq(0)); + assert_eq!(album.meta.seq, AlbumSeq(0)); // Setting a seq on an album. album.set_seq(AlbumSeq(6)); - assert_eq!(album.seq, AlbumSeq(6)); + assert_eq!(album.meta.seq, AlbumSeq(6)); album.set_seq(AlbumSeq(6)); - assert_eq!(album.seq, AlbumSeq(6)); + assert_eq!(album.meta.seq, AlbumSeq(6)); album.set_seq(AlbumSeq(8)); - assert_eq!(album.seq, AlbumSeq(8)); + assert_eq!(album.meta.seq, AlbumSeq(8)); // Clearing seq. album.clear_seq(); - assert_eq!(album.seq, AlbumSeq(0)); + assert_eq!(album.meta.seq, AlbumSeq(0)); } #[test] fn merge_album_no_overlap() { let left = FULL_COLLECTION[0].albums[0].to_owned(); let mut right = FULL_COLLECTION[0].albums[1].to_owned(); - right.id = left.id.clone(); + right.meta.id = left.meta.id.clone(); let mut expected = left.clone(); expected.tracks.append(&mut right.tracks.clone()); @@ -305,7 +315,7 @@ mod tests { fn merge_album_overlap() { let mut left = FULL_COLLECTION[0].albums[0].to_owned(); let mut right = FULL_COLLECTION[0].albums[1].to_owned(); - right.id = left.id.clone(); + right.meta.id = left.meta.id.clone(); left.tracks.push(right.tracks[0].clone()); left.tracks.sort_unstable(); @@ -328,23 +338,23 @@ mod tests { let mut album = Album::new(AlbumId::new("an album"), AlbumDate::default(), None, vec![]); let mut expected: Option = None; - assert_eq!(album.musicbrainz, expected); + assert_eq!(album.meta.musicbrainz, expected); // Setting a URL on an album. album.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap()); _ = expected.insert(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap()); - assert_eq!(album.musicbrainz, expected); + assert_eq!(album.meta.musicbrainz, expected); album.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap()); - assert_eq!(album.musicbrainz, expected); + assert_eq!(album.meta.musicbrainz, expected); album.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ_2).unwrap()); _ = expected.insert(MbAlbumRef::from_url_str(MUSICBRAINZ_2).unwrap()); - assert_eq!(album.musicbrainz, expected); + assert_eq!(album.meta.musicbrainz, expected); // Clearing URLs. album.clear_musicbrainz_ref(); _ = expected.take(); - assert_eq!(album.musicbrainz, expected); + assert_eq!(album.meta.musicbrainz, expected); } } diff --git a/src/core/collection/artist.rs b/src/core/collection/artist.rs index dd0c851..c19d8b0 100644 --- a/src/core/collection/artist.rs +++ b/src/core/collection/artist.rs @@ -13,18 +13,24 @@ use crate::core::collection::{ /// An artist. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Artist { + pub meta: ArtistMeta, + pub albums: Vec, +} + +/// Artist metadata. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ArtistMeta { pub id: ArtistId, pub sort: Option, pub musicbrainz: Option, pub properties: HashMap>, - pub albums: Vec, } impl WithId for Artist { type Id = ArtistId; fn id(&self) -> &Self::Id { - &self.id + &self.meta.id } } @@ -38,32 +44,34 @@ impl Artist { /// Create new [`Artist`] with the given [`ArtistId`]. pub fn new>(id: Id) -> Self { Artist { - id: id.into(), - sort: None, - musicbrainz: None, - properties: HashMap::new(), + meta: ArtistMeta { + id: id.into(), + sort: None, + musicbrainz: None, + properties: HashMap::new(), + }, albums: vec![], } } pub fn get_sort_key(&self) -> (&ArtistId,) { - (self.sort.as_ref().unwrap_or(&self.id),) + (self.meta.sort.as_ref().unwrap_or(&self.meta.id),) } pub fn set_sort_key>(&mut self, sort: SORT) { - self.sort = Some(sort.into()); + self.meta.sort = Some(sort.into()); } pub fn clear_sort_key(&mut self) { - _ = self.sort.take(); + _ = self.meta.sort.take(); } pub fn set_musicbrainz_ref(&mut self, mbref: MbArtistRef) { - _ = self.musicbrainz.insert(mbref); + _ = self.meta.musicbrainz.insert(mbref); } pub fn clear_musicbrainz_ref(&mut self) { - _ = self.musicbrainz.take(); + _ = self.meta.musicbrainz.take(); } // In the functions below, it would be better to use `contains` instead of `iter().any`, but for @@ -71,7 +79,7 @@ impl Artist { // https://stackoverflow.com/questions/48985924/why-does-a-str-not-coerce-to-a-string-when-using-veccontains pub fn add_to_property + Into>(&mut self, property: S, values: Vec) { - match self.properties.get_mut(property.as_ref()) { + match self.meta.properties.get_mut(property.as_ref()) { Some(container) => { container.append( &mut values @@ -82,7 +90,7 @@ impl Artist { ); } None => { - self.properties.insert( + self.meta.properties.insert( property.into(), values.into_iter().map(|s| s.into()).collect(), ); @@ -91,23 +99,23 @@ impl Artist { } pub fn remove_from_property>(&mut self, property: S, values: Vec) { - if let Some(container) = self.properties.get_mut(property.as_ref()) { + if let Some(container) = self.meta.properties.get_mut(property.as_ref()) { container.retain(|val| !values.iter().any(|x| x.as_ref() == val)); if container.is_empty() { - self.properties.remove(property.as_ref()); + self.meta.properties.remove(property.as_ref()); } } } pub fn set_property + Into>(&mut self, property: S, values: Vec) { - self.properties.insert( + self.meta.properties.insert( property.into(), values.into_iter().map(|s| s.into()).collect(), ); } pub fn clear_property>(&mut self, property: S) { - self.properties.remove(property.as_ref()); + self.meta.properties.remove(property.as_ref()); } } @@ -125,11 +133,11 @@ impl Ord for Artist { impl Merge for Artist { fn merge_in_place(&mut self, other: Self) { - assert_eq!(self.id, other.id); + assert_eq!(self.meta.id, other.meta.id); - self.sort = self.sort.take().or(other.sort); - self.musicbrainz = self.musicbrainz.take().or(other.musicbrainz); - self.properties.merge_in_place(other.properties); + self.meta.sort = self.meta.sort.take().or(other.meta.sort); + self.meta.musicbrainz = self.meta.musicbrainz.take().or(other.meta.musicbrainz); + self.meta.properties.merge_in_place(other.meta.properties); let albums = mem::take(&mut self.albums); self.albums = MergeCollections::merge_iter(albums, other.albums); @@ -181,32 +189,32 @@ mod tests { let mut artist = Artist::new(&artist_id.name); - assert_eq!(artist.id, artist_id); - assert_eq!(artist.sort, None); + assert_eq!(artist.meta.id, artist_id); + assert_eq!(artist.meta.sort, None); assert_eq!(artist.get_sort_key(), (&artist_id,)); assert!(artist < Artist::new(sort_id_1.clone())); assert!(artist < Artist::new(sort_id_2.clone())); artist.set_sort_key(sort_id_1.clone()); - assert_eq!(artist.id, artist_id); - assert_eq!(artist.sort.as_ref(), Some(&sort_id_1)); + assert_eq!(artist.meta.id, artist_id); + assert_eq!(artist.meta.sort.as_ref(), Some(&sort_id_1)); assert_eq!(artist.get_sort_key(), (&sort_id_1,)); assert!(artist > Artist::new(artist_id.clone())); assert!(artist < Artist::new(sort_id_2.clone())); artist.set_sort_key(sort_id_2.clone()); - assert_eq!(artist.id, artist_id); - assert_eq!(artist.sort.as_ref(), Some(&sort_id_2)); + assert_eq!(artist.meta.id, artist_id); + assert_eq!(artist.meta.sort.as_ref(), Some(&sort_id_2)); assert_eq!(artist.get_sort_key(), (&sort_id_2,)); assert!(artist > Artist::new(artist_id.clone())); assert!(artist > Artist::new(sort_id_1.clone())); artist.clear_sort_key(); - assert_eq!(artist.id, artist_id); - assert_eq!(artist.sort, None); + assert_eq!(artist.meta.id, artist_id); + assert_eq!(artist.meta.sort, None); assert_eq!(artist.get_sort_key(), (&artist_id,)); assert!(artist < Artist::new(sort_id_1.clone())); assert!(artist < Artist::new(sort_id_2.clone())); @@ -217,24 +225,24 @@ mod tests { let mut artist = Artist::new(ArtistId::new("an artist")); let mut expected: Option = None; - assert_eq!(artist.musicbrainz, expected); + assert_eq!(artist.meta.musicbrainz, expected); // Setting a URL on an artist. artist.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap()); _ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap()); - assert_eq!(artist.musicbrainz, expected); + assert_eq!(artist.meta.musicbrainz, expected); artist.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap()); - assert_eq!(artist.musicbrainz, expected); + assert_eq!(artist.meta.musicbrainz, expected); artist.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap()); _ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap()); - assert_eq!(artist.musicbrainz, expected); + assert_eq!(artist.meta.musicbrainz, expected); // Clearing URLs. artist.clear_musicbrainz_ref(); _ = expected.take(); - assert_eq!(artist.musicbrainz, expected); + assert_eq!(artist.meta.musicbrainz, expected); } #[test] @@ -242,70 +250,70 @@ mod tests { let mut artist = Artist::new(ArtistId::new("an artist")); let mut expected: Vec = vec![]; - assert!(artist.properties.is_empty()); + assert!(artist.meta.properties.is_empty()); // Adding a single URL. artist.add_to_property("MusicButler", vec![MUSICBUTLER]); expected.push(MUSICBUTLER.to_owned()); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); // Adding a URL that already exists is ok, but does not do anything. artist.add_to_property("MusicButler", vec![MUSICBUTLER]); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); // Adding another single URL. artist.add_to_property("MusicButler", vec![MUSICBUTLER_2]); expected.push(MUSICBUTLER_2.to_owned()); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); artist.add_to_property("MusicButler", vec![MUSICBUTLER_2]); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); // Removing a URL. artist.remove_from_property("MusicButler", vec![MUSICBUTLER]); expected.retain(|url| url != MUSICBUTLER); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); // Removing URls that do not exist is okay, they will be ignored. artist.remove_from_property("MusicButler", vec![MUSICBUTLER]); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); // Removing a URL. artist.remove_from_property("MusicButler", vec![MUSICBUTLER_2]); expected.retain(|url| url.as_str() != MUSICBUTLER_2); - assert!(artist.properties.is_empty()); + assert!(artist.meta.properties.is_empty()); artist.remove_from_property("MusicButler", vec![MUSICBUTLER_2]); - assert!(artist.properties.is_empty()); + assert!(artist.meta.properties.is_empty()); // Adding URLs if some exist is okay, they will be ignored. artist.add_to_property("MusicButler", vec![MUSICBUTLER]); expected.push(MUSICBUTLER.to_owned()); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); artist.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]); expected.push(MUSICBUTLER_2.to_owned()); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); // Removing URLs if some do not exist is okay, they will be ignored. artist.remove_from_property("MusicButler", vec![MUSICBUTLER]); expected.retain(|url| url.as_str() != MUSICBUTLER); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); artist.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]); expected.retain(|url| url.as_str() != MUSICBUTLER_2); - assert!(artist.properties.is_empty()); + assert!(artist.meta.properties.is_empty()); // Adding mutliple URLs without clashes. artist.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]); expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER_2.to_owned()); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); // Removing multiple URLs without clashes. artist.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]); expected.clear(); - assert!(artist.properties.is_empty()); + assert!(artist.meta.properties.is_empty()); } #[test] @@ -313,40 +321,43 @@ mod tests { let mut artist = Artist::new(ArtistId::new("an artist")); let mut expected: Vec = vec![]; - assert!(artist.properties.is_empty()); + assert!(artist.meta.properties.is_empty()); // Set URLs. artist.set_property("MusicButler", vec![MUSICBUTLER]); expected.push(MUSICBUTLER.to_owned()); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); artist.set_property("MusicButler", vec![MUSICBUTLER_2]); expected.clear(); expected.push(MUSICBUTLER_2.to_owned()); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); artist.set_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]); expected.clear(); expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER_2.to_owned()); - assert_eq!(artist.properties.get("MusicButler"), Some(&expected)); + assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); // Clear URLs. artist.clear_property("MusicButler"); expected.clear(); - assert!(artist.properties.is_empty()); + assert!(artist.meta.properties.is_empty()); } #[test] fn merge_artist_no_overlap() { let left = FULL_COLLECTION[0].to_owned(); let mut right = FULL_COLLECTION[1].to_owned(); - right.id = left.id.clone(); - right.musicbrainz = None; - right.properties = HashMap::new(); + right.meta.id = left.meta.id.clone(); + right.meta.musicbrainz = None; + right.meta.properties = HashMap::new(); let mut expected = left.clone(); - expected.properties = expected.properties.merge(right.clone().properties); + expected.meta.properties = expected + .meta + .properties + .merge(right.clone().meta.properties); expected.albums.append(&mut right.albums.clone()); expected.albums.sort_unstable(); @@ -362,12 +373,15 @@ mod tests { fn merge_artist_overlap() { let mut left = FULL_COLLECTION[0].to_owned(); let mut right = FULL_COLLECTION[1].to_owned(); - right.id = left.id.clone(); + right.meta.id = left.meta.id.clone(); left.albums.push(right.albums[0].clone()); left.albums.sort_unstable(); let mut expected = left.clone(); - expected.properties = expected.properties.merge(right.clone().properties); + expected.meta.properties = expected + .meta + .properties + .merge(right.clone().meta.properties); expected.albums.append(&mut right.albums.clone()); expected.albums.sort_unstable(); expected.albums.dedup(); diff --git a/src/core/musichoard/base.rs b/src/core/musichoard/base.rs index 1919924..70baae9 100644 --- a/src/core/musichoard/base.rs +++ b/src/core/musichoard/base.rs @@ -59,14 +59,14 @@ impl IMusicHoardBasePrivate for MusicHoard } fn get_artist<'a>(collection: &'a Collection, artist_id: &ArtistId) -> Option<&'a Artist> { - collection.iter().find(|a| &a.id == artist_id) + collection.iter().find(|a| &a.meta.id == artist_id) } fn get_artist_mut<'a>( collection: &'a mut Collection, artist_id: &ArtistId, ) -> Option<&'a mut Artist> { - collection.iter_mut().find(|a| &a.id == artist_id) + collection.iter_mut().find(|a| &a.meta.id == artist_id) } fn get_artist_mut_or_err<'a>( @@ -79,7 +79,7 @@ impl IMusicHoardBasePrivate for MusicHoard } fn get_album_mut<'a>(artist: &'a mut Artist, album_id: &AlbumId) -> Option<&'a mut Album> { - artist.albums.iter_mut().find(|a| &a.id == album_id) + artist.albums.iter_mut().find(|a| &a.meta.id == album_id) } fn get_album_mut_or_err<'a>( @@ -115,7 +115,7 @@ mod tests { library_cache: left .clone() .into_iter() - .map(|a| (a.id.clone(), a)) + .map(|a| (a.meta.id.clone(), a)) .collect(), database_cache: right.clone(), ..Default::default() @@ -129,7 +129,7 @@ mod tests { library_cache: right .clone() .into_iter() - .map(|a| (a.id.clone(), a)) + .map(|a| (a.meta.id.clone(), a)) .collect(), database_cache: left.clone(), ..Default::default() @@ -153,7 +153,7 @@ mod tests { library_cache: left .clone() .into_iter() - .map(|a| (a.id.clone(), a)) + .map(|a| (a.meta.id.clone(), a)) .collect(), database_cache: right.clone(), ..Default::default() @@ -167,7 +167,7 @@ mod tests { library_cache: right .clone() .into_iter() - .map(|a| (a.id.clone(), a)) + .map(|a| (a.meta.id.clone(), a)) .collect(), database_cache: left.clone(), ..Default::default() @@ -191,20 +191,20 @@ mod tests { assert!(right.first().unwrap() > left.first().unwrap()); let artist_sort = Some(ArtistId::new("Album_Artist 0")); - right[0].sort = artist_sort.clone(); + right[0].meta.sort = artist_sort.clone(); assert!(right.first().unwrap() < left.first().unwrap()); // The result of the merge should be the same list of artists, but with the last artist now // in first place. let mut expected = left.to_owned(); - expected.last_mut().as_mut().unwrap().sort = artist_sort.clone(); + expected.last_mut().as_mut().unwrap().meta.sort = artist_sort.clone(); expected.rotate_right(1); let mut mh = MusicHoard { library_cache: left .clone() .into_iter() - .map(|a| (a.id.clone(), a)) + .map(|a| (a.meta.id.clone(), a)) .collect(), database_cache: right.clone(), ..Default::default() @@ -218,7 +218,7 @@ mod tests { library_cache: right .clone() .into_iter() - .map(|a| (a.id.clone(), a)) + .map(|a| (a.meta.id.clone(), a)) .collect(), database_cache: left.clone(), ..Default::default() diff --git a/src/core/musichoard/database.rs b/src/core/musichoard/database.rs index 8303144..9926966 100644 --- a/src/core/musichoard/database.rs +++ b/src/core/musichoard/database.rs @@ -91,7 +91,9 @@ impl IMusicHoardDatabase for MusicHoard>(&mut self, artist_id: Id) -> Result<(), Error> { self.update_collection(|collection| { - let index_opt = collection.iter().position(|a| &a.id == artist_id.as_ref()); + let index_opt = collection + .iter() + .position(|a| &a.meta.id == artist_id.as_ref()); if let Some(index) = index_opt { collection.remove(index); } @@ -434,29 +436,29 @@ mod tests { assert!(music_hoard.add_artist(artist_id.clone()).is_ok()); let mut expected: Option = None; - assert_eq!(music_hoard.collection[0].musicbrainz, expected); + assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); // Setting a URL on an artist not in the collection is an error. assert!(music_hoard .set_artist_musicbrainz(&artist_id_2, MUSICBRAINZ) .is_err()); - assert_eq!(music_hoard.collection[0].musicbrainz, expected); + assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); // Setting a URL on an artist. assert!(music_hoard .set_artist_musicbrainz(&artist_id, MUSICBRAINZ) .is_ok()); _ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap()); - assert_eq!(music_hoard.collection[0].musicbrainz, expected); + assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); // Clearing URLs on an artist that does not exist is an error. assert!(music_hoard.clear_artist_musicbrainz(&artist_id_2).is_err()); - assert_eq!(music_hoard.collection[0].musicbrainz, expected); + assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); // Clearing URLs. assert!(music_hoard.clear_artist_musicbrainz(&artist_id).is_ok()); _ = expected.take(); - assert_eq!(music_hoard.collection[0].musicbrainz, expected); + assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); } #[test] @@ -472,13 +474,13 @@ mod tests { assert!(music_hoard.add_artist(artist_id.clone()).is_ok()); let mut expected: Vec = vec![]; - assert!(music_hoard.collection[0].properties.is_empty()); + assert!(music_hoard.collection[0].meta.properties.is_empty()); // Adding URLs to an artist not in the collection is an error. assert!(music_hoard .add_to_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER]) .is_err()); - assert!(music_hoard.collection[0].properties.is_empty()); + assert!(music_hoard.collection[0].meta.properties.is_empty()); // Adding mutliple URLs without clashes. assert!(music_hoard @@ -487,7 +489,7 @@ mod tests { expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER_2.to_owned()); assert_eq!( - music_hoard.collection[0].properties.get("MusicButler"), + music_hoard.collection[0].meta.properties.get("MusicButler"), Some(&expected) ); @@ -496,7 +498,7 @@ mod tests { .remove_from_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER]) .is_err()); assert_eq!( - music_hoard.collection[0].properties.get("MusicButler"), + music_hoard.collection[0].meta.properties.get("MusicButler"), Some(&expected) ); @@ -509,7 +511,7 @@ mod tests { ) .is_ok()); expected.clear(); - assert!(music_hoard.collection[0].properties.is_empty()); + assert!(music_hoard.collection[0].meta.properties.is_empty()); } #[test] @@ -525,13 +527,13 @@ mod tests { assert!(music_hoard.add_artist(artist_id.clone()).is_ok()); let mut expected: Vec = vec![]; - assert!(music_hoard.collection[0].properties.is_empty()); + assert!(music_hoard.collection[0].meta.properties.is_empty()); // Seting URL on an artist not in the collection is an error. assert!(music_hoard .set_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER]) .is_err()); - assert!(music_hoard.collection[0].properties.is_empty()); + assert!(music_hoard.collection[0].meta.properties.is_empty()); // Set URLs. assert!(music_hoard @@ -541,7 +543,7 @@ mod tests { expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER_2.to_owned()); assert_eq!( - music_hoard.collection[0].properties.get("MusicButler"), + music_hoard.collection[0].meta.properties.get("MusicButler"), Some(&expected) ); @@ -555,7 +557,7 @@ mod tests { .clear_artist_property(&artist_id, "MusicButler") .is_ok()); expected.clear(); - assert!(music_hoard.collection[0].properties.is_empty()); + assert!(music_hoard.collection[0].meta.properties.is_empty()); } #[test] @@ -581,17 +583,17 @@ mod tests { database.expect_save().times(2).returning(|_| Ok(())); let mut music_hoard = MusicHoard::database(database).unwrap(); - assert_eq!(music_hoard.collection[0].albums[0].seq, AlbumSeq(0)); + assert_eq!(music_hoard.collection[0].albums[0].meta.seq, AlbumSeq(0)); // Seting seq on an album not belonging to the artist is an error. assert!(music_hoard .set_album_seq(&artist_id, &album_id_2, 6) .is_err()); - assert_eq!(music_hoard.collection[0].albums[0].seq, AlbumSeq(0)); + assert_eq!(music_hoard.collection[0].albums[0].meta.seq, AlbumSeq(0)); // Set seq. assert!(music_hoard.set_album_seq(&artist_id, &album_id, 6).is_ok()); - assert_eq!(music_hoard.collection[0].albums[0].seq, AlbumSeq(6)); + assert_eq!(music_hoard.collection[0].albums[0].meta.seq, AlbumSeq(6)); // Clearing seq on an album that does not exist is an error. assert!(music_hoard @@ -600,7 +602,7 @@ mod tests { // Clear seq. assert!(music_hoard.clear_album_seq(&artist_id, &album_id).is_ok()); - assert_eq!(music_hoard.collection[0].albums[0].seq, AlbumSeq(0)); + assert_eq!(music_hoard.collection[0].albums[0].meta.seq, AlbumSeq(0)); } #[test] diff --git a/src/core/musichoard/library.rs b/src/core/musichoard/library.rs index d2e9888..47e9357 100644 --- a/src/core/musichoard/library.rs +++ b/src/core/musichoard/library.rs @@ -86,17 +86,17 @@ impl MusicHoard { .or_insert_with(|| Artist::new(artist_id)), }; - if artist.sort.is_some() { - if artist_sort.is_some() && (artist.sort != artist_sort) { + if artist.meta.sort.is_some() { + if artist_sort.is_some() && (artist.meta.sort != artist_sort) { return Err(Error::CollectionError(format!( "multiple album_artist_sort found for artist '{}': '{}' != '{}'", - artist.id, - artist.sort.as_ref().unwrap(), + artist.meta.id, + artist.meta.sort.as_ref().unwrap(), artist_sort.as_ref().unwrap() ))); } } else if artist_sort.is_some() { - artist.sort = artist_sort; + artist.meta.sort = artist_sort; } // Do a linear search as few artists have more than a handful of albums. Search from the @@ -105,7 +105,7 @@ impl MusicHoard { .albums .iter_mut() .rev() - .find(|album| album.id == album_id) + .find(|album| album.meta.id == album_id) { Some(album) => album.tracks.push(track), None => { @@ -197,13 +197,13 @@ mod tests { assert!(music_hoard.get_collection()[0] .albums .iter() - .any(|album| album.id.title == "album_title a.a")); + .any(|album| album.meta.id.title == "album_title a.a")); music_hoard.rescan_library().unwrap(); assert!(!music_hoard.get_collection()[0] .albums .iter() - .any(|album| album.id.title == "album_title a.a")); + .any(|album| album.meta.id.title == "album_title a.a")); } #[test] @@ -234,8 +234,8 @@ mod tests { let mut library = MockILibrary::new(); let mut expected = LIBRARY_COLLECTION.to_owned(); - let removed_album_id = expected[0].albums[0].id.clone(); - let clashed_album_id = &expected[1].albums[0].id; + let removed_album_id = expected[0].albums[0].meta.id.clone(); + let clashed_album_id = &expected[1].albums[0].meta.id; let mut items = LIBRARY_ITEMS.to_owned(); for item in items @@ -245,7 +245,7 @@ mod tests { item.album_title = clashed_album_id.title.clone(); } - expected[0].albums[0].id = clashed_album_id.clone(); + expected[0].albums[0].meta.id = clashed_album_id.clone(); let library_input = Query::new(); let library_result = Ok(items); diff --git a/src/core/testmod.rs b/src/core/testmod.rs index 7503316..2b0db1d 100644 --- a/src/core/testmod.rs +++ b/src/core/testmod.rs @@ -2,8 +2,8 @@ use once_cell::sync::Lazy; use std::collections::HashMap; use crate::core::collection::{ - album::{Album, AlbumId, AlbumPrimaryType, AlbumSeq}, - artist::{Artist, ArtistId}, + album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSeq}, + artist::{Artist, ArtistId, ArtistMeta}, musicbrainz::{MbAlbumRef, MbArtistRef}, track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality}, }; diff --git a/src/external/database/json/mod.rs b/src/external/database/json/mod.rs index 3347c5c..924220e 100644 --- a/src/external/database/json/mod.rs +++ b/src/external/database/json/mod.rs @@ -83,7 +83,7 @@ mod tests { let mut expected = FULL_COLLECTION.to_owned(); for artist in expected.iter_mut() { for album in artist.albums.iter_mut() { - album.date = AlbumDate::default(); + album.meta.date = AlbumDate::default(); album.tracks.clear(); } } diff --git a/src/external/database/serde/deserialize.rs b/src/external/database/serde/deserialize.rs index 82cb220..ea63e2c 100644 --- a/src/external/database/serde/deserialize.rs +++ b/src/external/database/serde/deserialize.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt}; use serde::{de::Visitor, Deserialize, Deserializer}; use crate::{ - collection::musicbrainz::Mbid, + collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid}, core::collection::{ album::{Album, AlbumDate, AlbumId, AlbumSeq}, artist::{Artist, ArtistId}, @@ -86,10 +86,12 @@ impl<'de> Deserialize<'de> for DeserializeMbid { impl From for Artist { fn from(artist: DeserializeArtist) -> Self { Artist { - id: ArtistId::new(artist.name), - sort: artist.sort.map(ArtistId::new), - musicbrainz: artist.musicbrainz.map(Into::::into).map(Into::into), - properties: artist.properties, + meta: ArtistMeta { + id: ArtistId::new(artist.name), + sort: artist.sort.map(ArtistId::new), + musicbrainz: artist.musicbrainz.map(Into::::into).map(Into::into), + properties: artist.properties, + }, albums: artist.albums.into_iter().map(Into::into).collect(), } } @@ -98,12 +100,14 @@ impl From for Artist { impl From for Album { fn from(album: DeserializeAlbum) -> Self { Album { - id: AlbumId { title: album.title }, - date: AlbumDate::default(), - seq: AlbumSeq(album.seq), - musicbrainz: album.musicbrainz.map(Into::::into).map(Into::into), - primary_type: album.primary_type.map(Into::into), - secondary_types: album.secondary_types.into_iter().map(Into::into).collect(), + meta: AlbumMeta { + id: AlbumId { title: album.title }, + date: AlbumDate::default(), + seq: AlbumSeq(album.seq), + musicbrainz: album.musicbrainz.map(Into::::into).map(Into::into), + primary_type: album.primary_type.map(Into::into), + secondary_types: album.secondary_types.into_iter().map(Into::into).collect(), + }, tracks: vec![], } } diff --git a/src/external/database/serde/serialize.rs b/src/external/database/serde/serialize.rs index 874a01c..8abf5f3 100644 --- a/src/external/database/serde/serialize.rs +++ b/src/external/database/serde/serialize.rs @@ -52,13 +52,15 @@ impl<'a> Serialize for SerializeMbid<'a> { impl<'a> From<&'a Artist> for SerializeArtist<'a> { fn from(artist: &'a Artist) -> Self { SerializeArtist { - name: &artist.id.name, - sort: artist.sort.as_ref().map(|id| id.name.as_ref()), + name: &artist.meta.id.name, + sort: artist.meta.sort.as_ref().map(|id| id.name.as_ref()), musicbrainz: artist + .meta .musicbrainz .as_ref() .map(|mbref| SerializeMbid(mbref.mbid())), properties: artist + .meta .properties .iter() .map(|(k, v)| (k.as_ref(), v)) @@ -71,14 +73,16 @@ impl<'a> From<&'a Artist> for SerializeArtist<'a> { impl<'a> From<&'a Album> for SerializeAlbum<'a> { fn from(album: &'a Album) -> Self { SerializeAlbum { - title: &album.id.title, - seq: album.seq.0, + title: &album.meta.id.title, + seq: album.meta.seq.0, musicbrainz: album + .meta .musicbrainz .as_ref() .map(|mbref| SerializeMbid(mbref.mbid())), - primary_type: album.primary_type.map(Into::into), + primary_type: album.meta.primary_type.map(Into::into), secondary_types: album + .meta .secondary_types .iter() .copied() diff --git a/src/testmod/full.rs b/src/testmod/full.rs index b91f846..1a8c5ae 100644 --- a/src/testmod/full.rs +++ b/src/testmod/full.rs @@ -2,35 +2,39 @@ macro_rules! full_collection { () => { vec![ Artist { - id: ArtistId { - name: "Album_Artist ‘A’".to_string(), + meta: ArtistMeta { + id: ArtistId { + name: "Album_Artist ‘A’".to_string(), + }, + sort: None, + musicbrainz: Some(MbArtistRef::from_url_str( + "https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000" + ).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", + ) + ]), + ]), }, - sort: None, - musicbrainz: Some(MbArtistRef::from_url_str( - "https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000" - ).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![ Album { - id: AlbumId { - title: "album_title a.a".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title a.a".to_string(), + }, + date: 1998.into(), + seq: AlbumSeq(1), + musicbrainz: Some(MbAlbumRef::from_url_str( + "https://musicbrainz.org/release-group/00000000-0000-0000-0000-000000000000" + ).unwrap()), + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 1998.into(), - seq: AlbumSeq(1), - musicbrainz: Some(MbAlbumRef::from_url_str( - "https://musicbrainz.org/release-group/00000000-0000-0000-0000-000000000000" - ).unwrap()), - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -82,14 +86,16 @@ macro_rules! full_collection { ], }, Album { - id: AlbumId { - title: "album_title a.b".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title a.b".to_string(), + }, + date: (2015, 4).into(), + seq: AlbumSeq(1), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: (2015, 4).into(), - seq: AlbumSeq(1), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -118,37 +124,41 @@ macro_rules! full_collection { ], }, Artist { - id: ArtistId { - name: "Album_Artist ‘B’".to_string(), + meta: ArtistMeta { + id: ArtistId { + name: "Album_Artist ‘B’".to_string(), + }, + sort: None, + musicbrainz: Some(MbArtistRef::from_url_str( + "https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111" + ).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", + ) + ]), + ]), }, - sort: None, - musicbrainz: Some(MbArtistRef::from_url_str( - "https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111" - ).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![ Album { - id: AlbumId { - title: "album_title b.a".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title b.a".to_string(), + }, + date: (2003, 6, 6).into(), + seq: AlbumSeq(1), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: (2003, 6, 6).into(), - seq: AlbumSeq(1), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -178,16 +188,18 @@ macro_rules! full_collection { ], }, Album { - id: AlbumId { - title: "album_title b.b".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title b.b".to_string(), + }, + date: 2008.into(), + seq: AlbumSeq(3), + musicbrainz: Some(MbAlbumRef::from_url_str( + "https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111111" + ).unwrap()), + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 2008.into(), - seq: AlbumSeq(3), - musicbrainz: Some(MbAlbumRef::from_url_str( - "https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111111" - ).unwrap()), - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -217,16 +229,18 @@ macro_rules! full_collection { ], }, Album { - id: AlbumId { - title: "album_title b.c".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title b.c".to_string(), + }, + date: 2009.into(), + seq: AlbumSeq(2), + musicbrainz: Some(MbAlbumRef::from_url_str( + "https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111112" + ).unwrap()), + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 2009.into(), - seq: AlbumSeq(2), - musicbrainz: Some(MbAlbumRef::from_url_str( - "https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111112" - ).unwrap()), - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -256,14 +270,16 @@ macro_rules! full_collection { ], }, Album { - id: AlbumId { - title: "album_title b.d".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title b.d".to_string(), + }, + date: 2015.into(), + seq: AlbumSeq(4), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 2015.into(), - seq: AlbumSeq(4), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -295,26 +311,30 @@ macro_rules! full_collection { ], }, Artist { - id: ArtistId { - name: "The Album_Artist ‘C’".to_string(), + meta: ArtistMeta { + id: ArtistId { + name: "The Album_Artist ‘C’".to_string(), + }, + sort: Some(ArtistId { + name: "Album_Artist ‘C’, The".to_string(), + }), + musicbrainz: Some(MbArtistRef::from_url_str( + "https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111" + ).unwrap()), + properties: HashMap::new(), }, - sort: Some(ArtistId { - name: "Album_Artist ‘C’, The".to_string(), - }), - musicbrainz: Some(MbArtistRef::from_url_str( - "https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111" - ).unwrap()), - properties: HashMap::new(), albums: vec![ Album { - id: AlbumId { - title: "album_title c.a".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title c.a".to_string(), + }, + date: 1985.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 1985.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -344,14 +364,16 @@ macro_rules! full_collection { ], }, Album { - id: AlbumId { - title: "album_title c.b".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title c.b".to_string(), + }, + date: 2018.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 2018.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -383,22 +405,26 @@ macro_rules! full_collection { ], }, Artist { - id: ArtistId { - name: "Album_Artist ‘D’".to_string(), + meta: ArtistMeta { + id: ArtistId { + name: "Album_Artist ‘D’".to_string(), + }, + sort: None, + musicbrainz: None, + properties: HashMap::new(), }, - sort: None, - musicbrainz: None, - properties: HashMap::new(), albums: vec![ Album { - id: AlbumId { - title: "album_title d.a".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title d.a".to_string(), + }, + date: 1995.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 1995.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -428,14 +454,16 @@ macro_rules! full_collection { ], }, Album { - id: AlbumId { - title: "album_title d.b".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title d.b".to_string(), + }, + date: 2028.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 2028.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { diff --git a/src/testmod/library.rs b/src/testmod/library.rs index 7011083..1456f6c 100644 --- a/src/testmod/library.rs +++ b/src/testmod/library.rs @@ -3,22 +3,26 @@ macro_rules! library_collection { () => { vec![ Artist { - id: ArtistId { - name: "Album_Artist ‘A’".to_string(), + meta: ArtistMeta { + id: ArtistId { + name: "Album_Artist ‘A’".to_string(), + }, + sort: None, + musicbrainz: None, + properties: HashMap::new(), }, - sort: None, - musicbrainz: None, - properties: HashMap::new(), albums: vec![ Album { - id: AlbumId { - title: "album_title a.a".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title a.a".to_string(), + }, + date: 1998.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: 1998.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -70,14 +74,16 @@ macro_rules! library_collection { ], }, Album { - id: AlbumId { - title: "album_title a.b".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title a.b".to_string(), + }, + date: (2015, 4).into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: (2015, 4).into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -106,22 +112,26 @@ macro_rules! library_collection { ], }, Artist { - id: ArtistId { - name: "Album_Artist ‘B’".to_string(), + meta: ArtistMeta { + id: ArtistId { + name: "Album_Artist ‘B’".to_string(), + }, + sort: None, + musicbrainz: None, + properties: HashMap::new(), }, - sort: None, - musicbrainz: None, - properties: HashMap::new(), albums: vec![ Album { - id: AlbumId { - title: "album_title b.a".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title b.a".to_string(), + }, + date: (2003, 6, 6).into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: (2003, 6, 6).into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -151,14 +161,16 @@ macro_rules! library_collection { ], }, Album { - id: AlbumId { - title: "album_title b.b".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title b.b".to_string(), + }, + date: 2008.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: 2008.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -188,14 +200,16 @@ macro_rules! library_collection { ], }, Album { - id: AlbumId { - title: "album_title b.c".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title b.c".to_string(), + }, + date: 2009.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: 2009.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -225,14 +239,16 @@ macro_rules! library_collection { ], }, Album { - id: AlbumId { - title: "album_title b.d".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title b.d".to_string(), + }, + date: 2015.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: 2015.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -264,24 +280,28 @@ macro_rules! library_collection { ], }, Artist { - id: ArtistId { - name: "The Album_Artist ‘C’".to_string(), + meta: ArtistMeta { + id: ArtistId { + name: "The Album_Artist ‘C’".to_string(), + }, + sort: Some(ArtistId { + name: "Album_Artist ‘C’, The".to_string(), + }), + musicbrainz: None, + properties: HashMap::new(), }, - sort: Some(ArtistId { - name: "Album_Artist ‘C’, The".to_string(), - }), - musicbrainz: None, - properties: HashMap::new(), albums: vec![ Album { - id: AlbumId { - title: "album_title c.a".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title c.a".to_string(), + }, + date: 1985.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: 1985.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -311,14 +331,16 @@ macro_rules! library_collection { ], }, Album { - id: AlbumId { - title: "album_title c.b".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title c.b".to_string(), + }, + date: 2018.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: 2018.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -350,22 +372,26 @@ macro_rules! library_collection { ], }, Artist { - id: ArtistId { - name: "Album_Artist ‘D’".to_string(), + meta: ArtistMeta { + id: ArtistId { + name: "Album_Artist ‘D’".to_string(), + }, + sort: None, + musicbrainz: None, + properties: HashMap::new(), }, - sort: None, - musicbrainz: None, - properties: HashMap::new(), albums: vec![ Album { - id: AlbumId { - title: "album_title d.a".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title d.a".to_string(), + }, + date: 1995.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: 1995.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -395,14 +421,16 @@ macro_rules! library_collection { ], }, Album { - id: AlbumId { - title: "album_title d.b".to_string(), + meta: AlbumMeta { + id: AlbumId { + title: "album_title d.b".to_string(), + }, + date: 2028.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: 2028.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { diff --git a/src/tui/app/machine/browse.rs b/src/tui/app/machine/browse.rs index 951d430..cd07769 100644 --- a/src/tui/app/machine/browse.rs +++ b/src/tui/app/machine/browse.rs @@ -93,13 +93,13 @@ impl IAppInteractBrowse for AppMachine { let (matches_tx, matches_rx) = mpsc::channel::(); - match artist.musicbrainz { + match artist.meta.musicbrainz { Some(ref mbid) => { let arid = mbid.mbid(); let mut album_iter = artist.albums.iter().peekable(); while let Some(album) = album_iter.next() { - if album.musicbrainz.is_some() { + if album.meta.musicbrainz.is_some() { continue; } diff --git a/src/tui/app/machine/matches.rs b/src/tui/app/machine/matches.rs index adeaea8..e632ffc 100644 --- a/src/tui/app/machine/matches.rs +++ b/src/tui/app/machine/matches.rs @@ -194,8 +194,8 @@ mod tests { let album_match_1_1 = Match::new(100, album_1_1); let mut album_1_2 = album_1.clone(); - album_1_2.id.title.push_str(" extra title part"); - album_1_2.secondary_types.pop(); + album_1_2.meta.id.title.push_str(" extra title part"); + album_1_2.meta.secondary_types.pop(); let album_match_1_2 = Match::new(100, album_1_2); let list = vec![album_match_1_1.clone(), album_match_1_2.clone()]; diff --git a/src/tui/app/machine/search.rs b/src/tui/app/machine/search.rs index c13f6a5..17ed189 100644 --- a/src/tui/app/machine/search.rs +++ b/src/tui/app/machine/search.rs @@ -181,10 +181,10 @@ impl IAppInteractSearchPrivate for AppMachine { } fn predicate_artists(case_sens: bool, char_sens: bool, search: &str, probe: &Artist) -> bool { - let name = Self::normalize_search(&probe.id.name, !case_sens, !char_sens); + let name = Self::normalize_search(&probe.meta.id.name, !case_sens, !char_sens); let mut result = name.starts_with(search); - if let Some(ref probe_sort) = probe.sort { + if let Some(ref probe_sort) = probe.meta.sort { if !result { let name = Self::normalize_search(&probe_sort.name, !case_sens, !char_sens); result = name.starts_with(search); @@ -195,7 +195,7 @@ impl IAppInteractSearchPrivate for AppMachine { } fn predicate_albums(case_sens: bool, char_sens: bool, search: &str, probe: &Album) -> bool { - Self::predicate_title(case_sens, char_sens, search, &probe.id.title) + Self::predicate_title(case_sens, char_sens, search, &probe.meta.id.title) } fn predicate_tracks(case_sens: bool, char_sens: bool, search: &str, probe: &Track) -> bool { diff --git a/src/tui/lib/external/musicbrainz/mod.rs b/src/tui/lib/external/musicbrainz/mod.rs index 903c48c..3a7673f 100644 --- a/src/tui/lib/external/musicbrainz/mod.rs +++ b/src/tui/lib/external/musicbrainz/mod.rs @@ -33,7 +33,7 @@ impl MusicBrainz { impl IMusicBrainz for MusicBrainz { fn search_artist(&mut self, artist: &Artist) -> Result>, Error> { - let query = SearchArtistRequest::new().string(&artist.id.name); + let query = SearchArtistRequest::new().string(&artist.meta.id.name); let mb_response = self.client.search_artist(query)?; @@ -51,14 +51,14 @@ impl IMusicBrainz for MusicBrainz { ) -> Result>, Error> { // Some release groups may have a promotional early release messing up the search. Searching // with just the year should be enough anyway. - let date = AlbumDate::new(album.date.year, None, None); + let date = AlbumDate::new(album.meta.date.year, None, None); let query = SearchReleaseGroupRequest::new() .arid(arid) .and() .first_release_date(&date) .and() - .release_group(&album.id.title); + .release_group(&album.meta.id.title); let mb_response = self.client.search_release_group(query)?; diff --git a/src/tui/testmod.rs b/src/tui/testmod.rs index 5340087..a5a6710 100644 --- a/src/tui/testmod.rs +++ b/src/tui/testmod.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use musichoard::collection::{ - album::{Album, AlbumId, AlbumPrimaryType, AlbumSeq}, - artist::{Artist, ArtistId}, + album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSeq}, + artist::{Artist, ArtistId, ArtistMeta}, musicbrainz::{MbAlbumRef, MbArtistRef}, track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality}, }; diff --git a/src/tui/ui/browse.rs b/src/tui/ui/browse.rs index 616a5fe..22df107 100644 --- a/src/tui/ui/browse.rs +++ b/src/tui/ui/browse.rs @@ -120,7 +120,7 @@ impl<'a, 'b> ArtistState<'a, 'b> { let list = List::new( artists .iter() - .map(|a| ListItem::new(a.id.name.as_str())) + .map(|a| ListItem::new(a.meta.id.name.as_str())) .collect::>(), ); @@ -158,12 +158,12 @@ impl<'a, 'b> AlbumState<'a, 'b> { Date: {}\n\ Type: {}\n\ Status: {}", - album.map(|a| a.id.title.as_str()).unwrap_or(""), + album.map(|a| a.meta.id.title.as_str()).unwrap_or(""), album - .map(|a| UiDisplay::display_date(&a.date, &a.seq)) + .map(|a| UiDisplay::display_date(&a.meta.date, &a.meta.seq)) .unwrap_or_default(), album - .map(|a| UiDisplay::display_type(&a.primary_type, &a.secondary_types)) + .map(|a| UiDisplay::display_type(&a.meta.primary_type, &a.meta.secondary_types)) .unwrap_or_default(), album .map(|a| UiDisplay::display_album_status(&a.get_status())) @@ -180,10 +180,10 @@ impl<'a, 'b> AlbumState<'a, 'b> { fn to_list_item(album: &Album) -> ListItem { let line = match album.get_status() { - AlbumStatus::None => Line::raw(album.id.title.as_str()), + AlbumStatus::None => Line::raw(album.meta.id.title.as_str()), AlbumStatus::Owned(format) => match format { - TrackFormat::Mp3 => Line::styled(album.id.title.as_str(), UiColor::FG_WARN), - TrackFormat::Flac => Line::styled(album.id.title.as_str(), UiColor::FG_GOOD), + TrackFormat::Mp3 => Line::styled(album.meta.id.title.as_str(), UiColor::FG_WARN), + TrackFormat::Flac => Line::styled(album.meta.id.title.as_str(), UiColor::FG_GOOD), }, }; ListItem::new(line) diff --git a/src/tui/ui/display.rs b/src/tui/ui/display.rs index 78f0927..1d15413 100644 --- a/src/tui/ui/display.rs +++ b/src/tui/ui/display.rs @@ -99,14 +99,14 @@ impl UiDisplay { } pub fn display_artist_matching(artist: &Artist) -> String { - format!("Matching artist: {}", &artist.id.name) + format!("Matching artist: {}", &artist.meta.id.name) } pub fn display_album_matching(album: &Album) -> String { format!( "Matching album: {} | {}", - UiDisplay::display_album_date(&album.date), - &album.id.title + UiDisplay::display_album_date(&album.meta.date), + &album.meta.id.title ) } @@ -128,7 +128,7 @@ impl UiDisplay { match match_option { MatchOption::Match(match_artist) => format!( "{}{} ({}%)", - &match_artist.item.id.name, + &match_artist.item.meta.id.name, &match_artist .disambiguation .as_ref() @@ -144,11 +144,11 @@ impl UiDisplay { match match_option { MatchOption::Match(match_album) => format!( "{:010} | {} [{}] ({}%)", - UiDisplay::display_album_date(&match_album.item.date), - &match_album.item.id.title, + UiDisplay::display_album_date(&match_album.item.meta.date), + &match_album.item.meta.id.title, UiDisplay::display_type( - &match_album.item.primary_type, - &match_album.item.secondary_types + &match_album.item.meta.primary_type, + &match_album.item.meta.secondary_types ), match_album.score, ), diff --git a/src/tui/ui/info.rs b/src/tui/ui/info.rs index 41a2e23..a604e99 100644 --- a/src/tui/ui/info.rs +++ b/src/tui/ui/info.rs @@ -72,12 +72,12 @@ impl<'a> ArtistOverlay<'a> { "Artist: {}\n\n{item_indent}\ MusicBrainz: {}\n{item_indent}\ Properties: {}", - artist.map(|a| a.id.name.as_str()).unwrap_or(""), + artist.map(|a| a.meta.id.name.as_str()).unwrap_or(""), artist - .and_then(|a| a.musicbrainz.as_ref().map(|mb| mb.url().as_str())) + .and_then(|a| a.meta.musicbrainz.as_ref().map(|mb| mb.url().as_str())) .unwrap_or(""), Self::opt_hashmap_to_string( - artist.map(|a| &a.properties), + artist.map(|a| &a.meta.properties), &double_item_indent, &double_list_indent ), @@ -100,9 +100,9 @@ impl<'a> AlbumOverlay<'a> { let properties = Paragraph::new(format!( "Album: {}\n\n{item_indent}\ MusicBrainz: {}", - album.map(|a| a.id.title.as_str()).unwrap_or(""), + album.map(|a| a.meta.id.title.as_str()).unwrap_or(""), album - .and_then(|a| a.musicbrainz.as_ref().map(|mb| mb.url().as_str())) + .and_then(|a| a.meta.musicbrainz.as_ref().map(|mb| mb.url().as_str())) .unwrap_or(""), )); diff --git a/tests/database/json.rs b/tests/database/json.rs index e803557..2ee6755 100644 --- a/tests/database/json.rs +++ b/tests/database/json.rs @@ -18,7 +18,7 @@ fn expected() -> Collection { let mut expected = COLLECTION.to_owned(); for artist in expected.iter_mut() { for album in artist.albums.iter_mut() { - album.date = AlbumDate::default(); + album.meta.date = AlbumDate::default(); album.tracks.clear(); } } diff --git a/tests/testlib.rs b/tests/testlib.rs index 3261e0a..09f964d 100644 --- a/tests/testlib.rs +++ b/tests/testlib.rs @@ -2,8 +2,8 @@ use once_cell::sync::Lazy; use std::collections::HashMap; use musichoard::collection::{ - album::{Album, AlbumId, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq}, - artist::{Artist, ArtistId}, + album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq}, + artist::{Artist, ArtistId, ArtistMeta}, musicbrainz::MbArtistRef, track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality}, Collection, @@ -12,35 +12,39 @@ use musichoard::collection::{ pub static COLLECTION: Lazy> = Lazy::new(|| -> Collection { vec![ Artist { - id: ArtistId { - name: String::from("Аркона"), - }, - sort: Some(ArtistId{ - name: String::from("Arkona") - }), - musicbrainz: Some(MbArtistRef::from_url_str( - "https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212" - ).unwrap()), - properties: HashMap::from([ - (String::from("MusicButler"), vec![ - String::from("https://www.musicbutler.io/artist-page/283448581"), - ]), - (String::from("Bandcamp"), vec![ - String::from("https://arkonamoscow.bandcamp.com/"), - ]), - (String::from("Qobuz"), vec![String::from( - "https://www.qobuz.com/nl-nl/interpreter/arkona/download-streaming-albums", - )]), - ]), - albums: vec![Album { - id: AlbumId { - title: String::from("Slovo"), + meta: ArtistMeta { + id: ArtistId { + name: String::from("Аркона"), + }, + sort: Some(ArtistId{ + name: String::from("Arkona") + }), + musicbrainz: Some(MbArtistRef::from_url_str( + "https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212" + ).unwrap()), + properties: HashMap::from([ + (String::from("MusicButler"), vec![ + String::from("https://www.musicbutler.io/artist-page/283448581"), + ]), + (String::from("Bandcamp"), vec![ + String::from("https://arkonamoscow.bandcamp.com/"), + ]), + (String::from("Qobuz"), vec![String::from( + "https://www.qobuz.com/nl-nl/interpreter/arkona/download-streaming-albums", + )]), + ]), + }, + albums: vec![Album { + meta: AlbumMeta { + id: AlbumId { + title: String::from("Slovo"), + }, + date: 2011.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 2011.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -200,31 +204,35 @@ pub static COLLECTION: Lazy> = Lazy::new(|| -> Collection { }], }, Artist { - id: ArtistId { - name: String::from("Eluveitie"), - }, - sort: None, - musicbrainz: Some(MbArtistRef::from_url_str( - "https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38" - ).unwrap()), - properties: HashMap::from([ - (String::from("MusicButler"), vec![ - String::from("https://www.musicbutler.io/artist-page/269358403"), + meta: ArtistMeta { + id: ArtistId { + name: String::from("Eluveitie"), + }, + sort: None, + musicbrainz: Some(MbArtistRef::from_url_str( + "https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38" + ).unwrap()), + properties: HashMap::from([ + (String::from("MusicButler"), vec![ + String::from("https://www.musicbutler.io/artist-page/269358403"), + ]), + (String::from("Qobuz"), vec![String::from( + "https://www.qobuz.com/nl-nl/interpreter/eluveitie/download-streaming-albums", + )]), ]), - (String::from("Qobuz"), vec![String::from( - "https://www.qobuz.com/nl-nl/interpreter/eluveitie/download-streaming-albums", - )]), - ]), + }, albums: vec![ Album { - id: AlbumId { - title: String::from("Vên [re‐recorded]"), + meta: AlbumMeta { + id: AlbumId { + title: String::from("Vên [re‐recorded]"), + }, + date: 2004.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Ep), + secondary_types: vec![], }, - date: 2004.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Ep), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -295,14 +303,16 @@ pub static COLLECTION: Lazy> = Lazy::new(|| -> Collection { ], }, Album { - id: AlbumId { - title: String::from("Slania"), + meta: AlbumMeta { + id: AlbumId { + title: String::from("Slania"), + }, + date: 2008.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 2008.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -441,30 +451,34 @@ pub static COLLECTION: Lazy> = Lazy::new(|| -> Collection { ], }, Artist { - id: ArtistId { - name: String::from("Frontside"), - }, - sort: None, - musicbrainz: Some(MbArtistRef::from_url_str( - "https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490" - ).unwrap()), - properties: HashMap::from([ - (String::from("MusicButler"), vec![ - String::from("https://www.musicbutler.io/artist-page/826588800"), - ]), - (String::from("Qobuz"), vec![String::from( - "https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums", - )]), - ]), - albums: vec![Album { - id: AlbumId { - title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"), + meta: ArtistMeta { + id: ArtistId { + name: String::from("Frontside"), + }, + sort: None, + musicbrainz: Some(MbArtistRef::from_url_str( + "https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490" + ).unwrap()), + properties: HashMap::from([ + (String::from("MusicButler"), vec![ + String::from("https://www.musicbutler.io/artist-page/826588800"), + ]), + (String::from("Qobuz"), vec![String::from( + "https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums", + )]), + ]), + }, + albums: vec![Album { + meta: AlbumMeta { + id: AlbumId { + title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"), + }, + date: 2001.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 2001.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -591,32 +605,36 @@ pub static COLLECTION: Lazy> = Lazy::new(|| -> Collection { }], }, Artist { - id: ArtistId { - name: String::from("Heaven’s Basement"), - }, - sort: Some(ArtistId { - name: String::from("Heaven’s Basement"), - }), - musicbrainz: Some(MbArtistRef::from_url_str( - "https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc" - ).unwrap()), - properties: HashMap::from([ - (String::from("MusicButler"), vec![ - String::from("https://www.musicbutler.io/artist-page/291158685"), - ]), - (String::from("Qobuz"), vec![String::from( - "https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums", - )]), - ]), - albums: vec![Album { - id: AlbumId { - title: String::from("Paper Plague"), + meta: ArtistMeta { + id: ArtistId { + name: String::from("Heaven’s Basement"), + }, + sort: Some(ArtistId { + name: String::from("Heaven’s Basement"), + }), + musicbrainz: Some(MbArtistRef::from_url_str( + "https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc" + ).unwrap()), + properties: HashMap::from([ + (String::from("MusicButler"), vec![ + String::from("https://www.musicbutler.io/artist-page/291158685"), + ]), + (String::from("Qobuz"), vec![String::from( + "https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums", + )]), + ]), + }, + albums: vec![Album { + meta: AlbumMeta { + id: AlbumId { + title: String::from("Paper Plague"), + }, + date: 2011.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: None, + secondary_types: vec![], }, - date: 2011.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: None, - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -631,14 +649,16 @@ pub static COLLECTION: Lazy> = Lazy::new(|| -> Collection { }, ], }, Album { - id: AlbumId { - title: String::from("Unbreakable"), + meta: AlbumMeta { + id: AlbumId { + title: String::from("Unbreakable"), + }, + date: 2011.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 2011.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -721,31 +741,35 @@ pub static COLLECTION: Lazy> = Lazy::new(|| -> Collection { }], }, Artist { - id: ArtistId { - name: String::from("Metallica"), - }, - sort: None, - musicbrainz: Some(MbArtistRef::from_url_str( - "https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab" - ).unwrap()), - properties: HashMap::from([ - (String::from("MusicButler"), vec![ - String::from("https://www.musicbutler.io/artist-page/3996865"), + meta: ArtistMeta { + id: ArtistId { + name: String::from("Metallica"), + }, + sort: None, + musicbrainz: Some(MbArtistRef::from_url_str( + "https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab" + ).unwrap()), + properties: HashMap::from([ + (String::from("MusicButler"), vec![ + String::from("https://www.musicbutler.io/artist-page/3996865"), + ]), + (String::from("Qobuz"), vec![String::from( + "https://www.qobuz.com/nl-nl/interpreter/metallica/download-streaming-albums", + )]), ]), - (String::from("Qobuz"), vec![String::from( - "https://www.qobuz.com/nl-nl/interpreter/metallica/download-streaming-albums", - )]), - ]), + }, albums: vec![ Album { - id: AlbumId { - title: String::from("Ride the Lightning"), + meta: AlbumMeta { + id: AlbumId { + title: String::from("Ride the Lightning"), + }, + date: 1984.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![], }, - date: 1984.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![], tracks: vec![ Track { id: TrackId { @@ -838,14 +862,16 @@ pub static COLLECTION: Lazy> = Lazy::new(|| -> Collection { ], }, Album { - id: AlbumId { - title: String::from("S&M"), + meta: AlbumMeta { + id: AlbumId { + title: String::from("S&M"), + }, + date: 1999.into(), + seq: AlbumSeq(0), + musicbrainz: None, + primary_type: Some(AlbumPrimaryType::Album), + secondary_types: vec![AlbumSecondaryType::Live], }, - date: 1999.into(), - seq: AlbumSeq(0), - musicbrainz: None, - primary_type: Some(AlbumPrimaryType::Album), - secondary_types: vec![AlbumSecondaryType::Live], tracks: vec![ Track { id: TrackId {