Enable fetch to apply modifications to the database #221

Merged
wojtek merged 14 commits from 189---enable-fetch-to-apply-modifications-to-the-database into main 2024-09-29 10:44:38 +02:00
17 changed files with 247 additions and 233 deletions
Showing only changes of commit 32b09b2f52 - Show all commits

View File

@ -171,10 +171,7 @@ impl SortCommand {
fn handle(self, music_hoard: &mut MH, artist_name: &str) { fn handle(self, music_hoard: &mut MH, artist_name: &str) {
match self { match self {
SortCommand::Set(artist_sort_value) => music_hoard SortCommand::Set(artist_sort_value) => music_hoard
.set_artist_sort( .set_artist_sort(ArtistId::new(artist_name), artist_sort_value.sort)
ArtistId::new(artist_name),
String::from(artist_sort_value.sort),
)
.expect("faild to set artist sort name"), .expect("faild to set artist sort name"),
SortCommand::Clear => music_hoard SortCommand::Clear => music_hoard
.clear_artist_sort(ArtistId::new(artist_name)) .clear_artist_sort(ArtistId::new(artist_name))

View File

@ -22,6 +22,12 @@ pub struct Artist {
pub struct ArtistMeta { pub struct ArtistMeta {
pub id: ArtistId, pub id: ArtistId,
pub sort: Option<String>, pub sort: Option<String>,
pub info: ArtistInfo,
}
/// Artist non-identifier metadata.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ArtistInfo {
pub musicbrainz: MbRefOption<MbArtistRef>, pub musicbrainz: MbRefOption<MbArtistRef>,
pub properties: HashMap<String, Vec<String>>, pub properties: HashMap<String, Vec<String>>,
} }
@ -75,8 +81,10 @@ impl ArtistMeta {
ArtistMeta { ArtistMeta {
id: id.into(), id: id.into(),
sort: None, sort: None,
musicbrainz: MbRefOption::None, info: ArtistInfo {
properties: HashMap::new(), musicbrainz: MbRefOption::None,
properties: HashMap::new(),
},
} }
} }
@ -91,7 +99,9 @@ impl ArtistMeta {
pub fn clear_sort_key(&mut self) { pub fn clear_sort_key(&mut self) {
self.sort.take(); self.sort.take();
} }
}
impl ArtistInfo {
pub fn set_musicbrainz_ref(&mut self, mbref: MbRefOption<MbArtistRef>) { pub fn set_musicbrainz_ref(&mut self, mbref: MbRefOption<MbArtistRef>) {
self.musicbrainz = mbref self.musicbrainz = mbref
} }
@ -162,6 +172,12 @@ impl Merge for ArtistMeta {
assert_eq!(self.id, other.id); assert_eq!(self.id, other.id);
self.sort = self.sort.take().or(other.sort); self.sort = self.sort.take().or(other.sort);
self.info.merge_in_place(other.info);
}
}
impl Merge for ArtistInfo {
fn merge_in_place(&mut self, other: Self) {
self.musicbrainz = self.musicbrainz.take().or(other.musicbrainz); self.musicbrainz = self.musicbrainz.take().or(other.musicbrainz);
self.properties.merge_in_place(other.properties); self.properties.merge_in_place(other.properties);
} }
@ -256,160 +272,132 @@ mod tests {
let mut artist = Artist::new(ArtistId::new("an artist")); let mut artist = Artist::new(ArtistId::new("an artist"));
let mut expected: MbRefOption<MbArtistRef> = MbRefOption::None; let mut expected: MbRefOption<MbArtistRef> = MbRefOption::None;
assert_eq!(artist.meta.musicbrainz, expected); assert_eq!(artist.meta.info.musicbrainz, expected);
// Setting a URL on an artist. // Setting a URL on an artist.
artist.meta.set_musicbrainz_ref(MbRefOption::Some( artist.meta.info.set_musicbrainz_ref(MbRefOption::Some(
MbArtistRef::from_url_str(MUSICBRAINZ).unwrap(), MbArtistRef::from_url_str(MUSICBRAINZ).unwrap(),
)); ));
expected.replace(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap()); expected.replace(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
assert_eq!(artist.meta.musicbrainz, expected); assert_eq!(artist.meta.info.musicbrainz, expected);
artist.meta.set_musicbrainz_ref(MbRefOption::Some( artist.meta.info.set_musicbrainz_ref(MbRefOption::Some(
MbArtistRef::from_url_str(MUSICBRAINZ).unwrap(), MbArtistRef::from_url_str(MUSICBRAINZ).unwrap(),
)); ));
assert_eq!(artist.meta.musicbrainz, expected); assert_eq!(artist.meta.info.musicbrainz, expected);
artist.meta.set_musicbrainz_ref(MbRefOption::Some( artist.meta.info.set_musicbrainz_ref(MbRefOption::Some(
MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap(), MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap(),
)); ));
expected.replace(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap()); expected.replace(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap());
assert_eq!(artist.meta.musicbrainz, expected); assert_eq!(artist.meta.info.musicbrainz, expected);
// Clearing URLs. // Clearing URLs.
artist.meta.clear_musicbrainz_ref(); artist.meta.info.clear_musicbrainz_ref();
expected.take(); expected.take();
assert_eq!(artist.meta.musicbrainz, expected); assert_eq!(artist.meta.info.musicbrainz, expected);
} }
#[test] #[test]
fn add_to_remove_from_property() { fn add_to_remove_from_property() {
let mut artist = Artist::new(ArtistId::new("an artist")); let mut artist = Artist::new(ArtistId::new("an artist"));
let info = &mut artist.meta.info;
let mut expected: Vec<String> = vec![]; let mut expected: Vec<String> = vec![];
assert!(artist.meta.properties.is_empty()); assert!(info.properties.is_empty());
// Adding a single URL. // Adding a single URL.
artist info.add_to_property("MusicButler", vec![MUSICBUTLER]);
.meta
.add_to_property("MusicButler", vec![MUSICBUTLER]);
expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER.to_owned());
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); assert_eq!(info.properties.get("MusicButler"), Some(&expected));
// Adding a URL that already exists is ok, but does not do anything. // Adding a URL that already exists is ok, but does not do anything.
artist info.add_to_property("MusicButler", vec![MUSICBUTLER]);
.meta assert_eq!(info.properties.get("MusicButler"), Some(&expected));
.add_to_property("MusicButler", vec![MUSICBUTLER]);
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
// Adding another single URL. // Adding another single URL.
artist info.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
.meta
.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
expected.push(MUSICBUTLER_2.to_owned()); expected.push(MUSICBUTLER_2.to_owned());
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); assert_eq!(info.properties.get("MusicButler"), Some(&expected));
artist info.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
.meta assert_eq!(info.properties.get("MusicButler"), Some(&expected));
.add_to_property("MusicButler", vec![MUSICBUTLER_2]);
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
// Removing a URL. // Removing a URL.
artist info.remove_from_property("MusicButler", vec![MUSICBUTLER]);
.meta
.remove_from_property("MusicButler", vec![MUSICBUTLER]);
expected.retain(|url| url != MUSICBUTLER); expected.retain(|url| url != MUSICBUTLER);
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); assert_eq!(info.properties.get("MusicButler"), Some(&expected));
// Removing URls that do not exist is okay, they will be ignored. // Removing URls that do not exist is okay, they will be ignored.
artist info.remove_from_property("MusicButler", vec![MUSICBUTLER]);
.meta assert_eq!(info.properties.get("MusicButler"), Some(&expected));
.remove_from_property("MusicButler", vec![MUSICBUTLER]);
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected));
// Removing a URL. // Removing a URL.
artist info.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
.meta
.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
expected.retain(|url| url.as_str() != MUSICBUTLER_2); expected.retain(|url| url.as_str() != MUSICBUTLER_2);
assert!(artist.meta.properties.is_empty()); assert!(info.properties.is_empty());
artist info.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
.meta assert!(info.properties.is_empty());
.remove_from_property("MusicButler", vec![MUSICBUTLER_2]);
assert!(artist.meta.properties.is_empty());
// Adding URLs if some exist is okay, they will be ignored. // Adding URLs if some exist is okay, they will be ignored.
artist info.add_to_property("MusicButler", vec![MUSICBUTLER]);
.meta
.add_to_property("MusicButler", vec![MUSICBUTLER]);
expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER.to_owned());
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); assert_eq!(info.properties.get("MusicButler"), Some(&expected));
artist info.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
.meta
.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
expected.push(MUSICBUTLER_2.to_owned()); expected.push(MUSICBUTLER_2.to_owned());
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); assert_eq!(info.properties.get("MusicButler"), Some(&expected));
// Removing URLs if some do not exist is okay, they will be ignored. // Removing URLs if some do not exist is okay, they will be ignored.
artist info.remove_from_property("MusicButler", vec![MUSICBUTLER]);
.meta
.remove_from_property("MusicButler", vec![MUSICBUTLER]);
expected.retain(|url| url.as_str() != MUSICBUTLER); expected.retain(|url| url.as_str() != MUSICBUTLER);
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); assert_eq!(info.properties.get("MusicButler"), Some(&expected));
artist info.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
.meta
.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
expected.retain(|url| url.as_str() != MUSICBUTLER_2); expected.retain(|url| url.as_str() != MUSICBUTLER_2);
assert!(artist.meta.properties.is_empty()); assert!(info.properties.is_empty());
// Adding mutliple URLs without clashes. // Adding mutliple URLs without clashes.
artist info.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
.meta
.add_to_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER.to_owned());
expected.push(MUSICBUTLER_2.to_owned()); expected.push(MUSICBUTLER_2.to_owned());
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); assert_eq!(info.properties.get("MusicButler"), Some(&expected));
// Removing multiple URLs without clashes. // Removing multiple URLs without clashes.
artist info.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
.meta
.remove_from_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
expected.clear(); expected.clear();
assert!(artist.meta.properties.is_empty()); assert!(info.properties.is_empty());
} }
#[test] #[test]
fn set_clear_musicbutler_urls() { fn set_clear_musicbutler_urls() {
let mut artist = Artist::new(ArtistId::new("an artist")); let mut artist = Artist::new(ArtistId::new("an artist"));
let info = &mut artist.meta.info;
let mut expected: Vec<String> = vec![]; let mut expected: Vec<String> = vec![];
assert!(artist.meta.properties.is_empty()); assert!(info.properties.is_empty());
// Set URLs. // Set URLs.
artist.meta.set_property("MusicButler", vec![MUSICBUTLER]); info.set_property("MusicButler", vec![MUSICBUTLER]);
expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER.to_owned());
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); assert_eq!(info.properties.get("MusicButler"), Some(&expected));
artist.meta.set_property("MusicButler", vec![MUSICBUTLER_2]); info.set_property("MusicButler", vec![MUSICBUTLER_2]);
expected.clear(); expected.clear();
expected.push(MUSICBUTLER_2.to_owned()); expected.push(MUSICBUTLER_2.to_owned());
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); assert_eq!(info.properties.get("MusicButler"), Some(&expected));
artist info.set_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
.meta
.set_property("MusicButler", vec![MUSICBUTLER, MUSICBUTLER_2]);
expected.clear(); expected.clear();
expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER.to_owned());
expected.push(MUSICBUTLER_2.to_owned()); expected.push(MUSICBUTLER_2.to_owned());
assert_eq!(artist.meta.properties.get("MusicButler"), Some(&expected)); assert_eq!(info.properties.get("MusicButler"), Some(&expected));
// Clear URLs. // Clear URLs.
artist.meta.clear_property("MusicButler"); info.clear_property("MusicButler");
expected.clear(); expected.clear();
assert!(artist.meta.properties.is_empty()); assert!(info.properties.is_empty());
} }
#[test] #[test]
@ -417,14 +405,15 @@ mod tests {
let left = FULL_COLLECTION[0].to_owned(); let left = FULL_COLLECTION[0].to_owned();
let mut right = FULL_COLLECTION[1].to_owned(); let mut right = FULL_COLLECTION[1].to_owned();
right.meta.id = left.meta.id.clone(); right.meta.id = left.meta.id.clone();
right.meta.musicbrainz = MbRefOption::None; right.meta.info.musicbrainz = MbRefOption::None;
right.meta.properties = HashMap::new(); right.meta.info.properties = HashMap::new();
let mut expected = left.clone(); let mut expected = left.clone();
expected.meta.properties = expected expected.meta.info.properties = expected
.meta .meta
.info
.properties .properties
.merge(right.clone().meta.properties); .merge(right.clone().meta.info.properties);
expected.albums.append(&mut right.albums.clone()); expected.albums.append(&mut right.albums.clone());
expected.albums.sort_unstable(); expected.albums.sort_unstable();
@ -445,10 +434,11 @@ mod tests {
left.albums.sort_unstable(); left.albums.sort_unstable();
let mut expected = left.clone(); let mut expected = left.clone();
expected.meta.properties = expected expected.meta.info.properties = expected
.meta .meta
.info
.properties .properties
.merge(right.clone().meta.properties); .merge(right.clone().meta.info.properties);
expected.albums.append(&mut right.albums.clone()); expected.albums.append(&mut right.albums.clone());
expected.albums.sort_unstable(); expected.albums.sort_unstable();
expected.albums.dedup(); expected.albums.dedup();

View File

@ -165,7 +165,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
mbid: MbRefOption<MbArtistRef>, mbid: MbRefOption<MbArtistRef>,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.update_artist(artist_id.as_ref(), |artist| { self.update_artist(artist_id.as_ref(), |artist| {
artist.meta.set_musicbrainz_ref(mbid) artist.meta.info.set_musicbrainz_ref(mbid)
}) })
} }
@ -174,7 +174,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
artist_id: Id, artist_id: Id,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.update_artist(artist_id.as_ref(), |artist| { self.update_artist(artist_id.as_ref(), |artist| {
artist.meta.clear_musicbrainz_ref() artist.meta.info.clear_musicbrainz_ref()
}) })
} }
@ -185,7 +185,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
values: Vec<S>, values: Vec<S>,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.update_artist(artist_id.as_ref(), |artist| { self.update_artist(artist_id.as_ref(), |artist| {
artist.meta.add_to_property(property, values) artist.meta.info.add_to_property(property, values)
}) })
} }
@ -196,7 +196,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
values: Vec<S>, values: Vec<S>,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.update_artist(artist_id.as_ref(), |artist| { self.update_artist(artist_id.as_ref(), |artist| {
artist.meta.remove_from_property(property, values) artist.meta.info.remove_from_property(property, values)
}) })
} }
@ -207,7 +207,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
values: Vec<S>, values: Vec<S>,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.update_artist(artist_id.as_ref(), |artist| { self.update_artist(artist_id.as_ref(), |artist| {
artist.meta.set_property(property, values) artist.meta.info.set_property(property, values)
}) })
} }
@ -217,7 +217,7 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
property: S, property: S,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.update_artist(artist_id.as_ref(), |artist| { self.update_artist(artist_id.as_ref(), |artist| {
artist.meta.clear_property(property) artist.meta.info.clear_property(property)
}) })
} }
@ -533,7 +533,7 @@ mod tests {
assert!(music_hoard.add_artist(artist_id.clone()).is_ok()); assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
let mut expected: MbRefOption<MbArtistRef> = MbRefOption::None; let mut expected: MbRefOption<MbArtistRef> = MbRefOption::None;
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
let mbref = MbRefOption::Some(MbArtistRef::from_uuid_str(MBID).unwrap()); let mbref = MbRefOption::Some(MbArtistRef::from_uuid_str(MBID).unwrap());
@ -541,23 +541,23 @@ mod tests {
assert!(music_hoard assert!(music_hoard
.set_artist_musicbrainz(&artist_id_2, mbref.clone()) .set_artist_musicbrainz(&artist_id_2, mbref.clone())
.is_err()); .is_err());
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
// Setting a URL on an artist. // Setting a URL on an artist.
assert!(music_hoard assert!(music_hoard
.set_artist_musicbrainz(&artist_id, mbref.clone()) .set_artist_musicbrainz(&artist_id, mbref.clone())
.is_ok()); .is_ok());
expected.replace(MbArtistRef::from_uuid_str(MBID).unwrap()); expected.replace(MbArtistRef::from_uuid_str(MBID).unwrap());
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
// Clearing URLs on an artist that does not exist is an error. // Clearing URLs on an artist that does not exist is an error.
assert!(music_hoard.clear_artist_musicbrainz(&artist_id_2).is_err()); assert!(music_hoard.clear_artist_musicbrainz(&artist_id_2).is_err());
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
// Clearing URLs. // Clearing URLs.
assert!(music_hoard.clear_artist_musicbrainz(&artist_id).is_ok()); assert!(music_hoard.clear_artist_musicbrainz(&artist_id).is_ok());
expected.take(); expected.take();
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); assert_eq!(music_hoard.collection[0].meta.info.musicbrainz, expected);
} }
#[test] #[test]
@ -573,13 +573,13 @@ mod tests {
assert!(music_hoard.add_artist(artist_id.clone()).is_ok()); assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
let mut expected: Vec<String> = vec![]; let mut expected: Vec<String> = vec![];
assert!(music_hoard.collection[0].meta.properties.is_empty()); assert!(music_hoard.collection[0].meta.info.properties.is_empty());
// Adding URLs to an artist not in the collection is an error. // Adding URLs to an artist not in the collection is an error.
assert!(music_hoard assert!(music_hoard
.add_to_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER]) .add_to_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
.is_err()); .is_err());
assert!(music_hoard.collection[0].meta.properties.is_empty()); assert!(music_hoard.collection[0].meta.info.properties.is_empty());
// Adding mutliple URLs without clashes. // Adding mutliple URLs without clashes.
assert!(music_hoard assert!(music_hoard
@ -587,19 +587,15 @@ mod tests {
.is_ok()); .is_ok());
expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER.to_owned());
expected.push(MUSICBUTLER_2.to_owned()); expected.push(MUSICBUTLER_2.to_owned());
assert_eq!( let info = &music_hoard.collection[0].meta.info;
music_hoard.collection[0].meta.properties.get("MusicButler"), assert_eq!(info.properties.get("MusicButler"), Some(&expected));
Some(&expected)
);
// Removing URLs from an artist not in the collection is an error. // Removing URLs from an artist not in the collection is an error.
assert!(music_hoard assert!(music_hoard
.remove_from_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER]) .remove_from_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
.is_err()); .is_err());
assert_eq!( let info = &music_hoard.collection[0].meta.info;
music_hoard.collection[0].meta.properties.get("MusicButler"), assert_eq!(info.properties.get("MusicButler"), Some(&expected));
Some(&expected)
);
// Removing multiple URLs without clashes. // Removing multiple URLs without clashes.
assert!(music_hoard assert!(music_hoard
@ -610,7 +606,7 @@ mod tests {
) )
.is_ok()); .is_ok());
expected.clear(); expected.clear();
assert!(music_hoard.collection[0].meta.properties.is_empty()); assert!(music_hoard.collection[0].meta.info.properties.is_empty());
} }
#[test] #[test]
@ -626,13 +622,13 @@ mod tests {
assert!(music_hoard.add_artist(artist_id.clone()).is_ok()); assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
let mut expected: Vec<String> = vec![]; let mut expected: Vec<String> = vec![];
assert!(music_hoard.collection[0].meta.properties.is_empty()); assert!(music_hoard.collection[0].meta.info.properties.is_empty());
// Seting URL on an artist not in the collection is an error. // Seting URL on an artist not in the collection is an error.
assert!(music_hoard assert!(music_hoard
.set_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER]) .set_artist_property(&artist_id_2, "MusicButler", vec![MUSICBUTLER])
.is_err()); .is_err());
assert!(music_hoard.collection[0].meta.properties.is_empty()); assert!(music_hoard.collection[0].meta.info.properties.is_empty());
// Set URLs. // Set URLs.
assert!(music_hoard assert!(music_hoard
@ -641,10 +637,8 @@ mod tests {
expected.clear(); expected.clear();
expected.push(MUSICBUTLER.to_owned()); expected.push(MUSICBUTLER.to_owned());
expected.push(MUSICBUTLER_2.to_owned()); expected.push(MUSICBUTLER_2.to_owned());
assert_eq!( let info = &music_hoard.collection[0].meta.info;
music_hoard.collection[0].meta.properties.get("MusicButler"), assert_eq!(info.properties.get("MusicButler"), Some(&expected));
Some(&expected)
);
// Clearing URLs on an artist that does not exist is an error. // Clearing URLs on an artist that does not exist is an error.
assert!(music_hoard assert!(music_hoard
@ -656,7 +650,7 @@ mod tests {
.clear_artist_property(&artist_id, "MusicButler") .clear_artist_property(&artist_id, "MusicButler")
.is_ok()); .is_ok());
expected.clear(); expected.clear();
assert!(music_hoard.collection[0].meta.properties.is_empty()); assert!(music_hoard.collection[0].meta.info.properties.is_empty());
} }
#[test] #[test]

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use crate::core::collection::{ use crate::core::collection::{
album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSeq}, album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSeq},
artist::{Artist, ArtistId, ArtistMeta}, artist::{Artist, ArtistId, ArtistInfo, ArtistMeta},
musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption}, musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption},
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality}, track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
}; };

View File

@ -5,7 +5,7 @@ use serde::{de::Visitor, Deserialize, Deserializer};
use crate::{ use crate::{
collection::{ collection::{
album::AlbumMeta, album::AlbumMeta,
artist::ArtistMeta, artist::{ArtistInfo, ArtistMeta},
musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption, Mbid}, musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption, Mbid},
}, },
core::collection::{ core::collection::{
@ -115,8 +115,10 @@ impl From<DeserializeArtist> for Artist {
meta: ArtistMeta { meta: ArtistMeta {
id: ArtistId::new(artist.name), id: ArtistId::new(artist.name),
sort: artist.sort, sort: artist.sort,
musicbrainz: artist.musicbrainz.into(), info: ArtistInfo {
properties: artist.properties, musicbrainz: artist.musicbrainz.into(),
properties: artist.properties,
},
}, },
albums: artist.albums.into_iter().map(Into::into).collect(), albums: artist.albums.into_iter().map(Into::into).collect(),
} }

View File

@ -73,9 +73,10 @@ impl<'a> From<&'a Artist> for SerializeArtist<'a> {
SerializeArtist { SerializeArtist {
name: &artist.meta.id.name, name: &artist.meta.id.name,
sort: artist.meta.sort.as_deref(), sort: artist.meta.sort.as_deref(),
musicbrainz: (&artist.meta.musicbrainz).into(), musicbrainz: (&artist.meta.info.musicbrainz).into(),
properties: artist properties: artist
.meta .meta
.info
.properties .properties
.iter() .iter()
.map(|(k, v)| (k.as_ref(), v)) .map(|(k, v)| (k.as_ref(), v))

View File

@ -79,8 +79,8 @@ impl From<SerdeMbArtistMeta> for MbArtistMeta {
fn from(value: SerdeMbArtistMeta) -> Self { fn from(value: SerdeMbArtistMeta) -> Self {
MbArtistMeta { MbArtistMeta {
id: value.id.into(), id: value.id.into(),
name: value.name.into(), name: value.name,
sort_name: value.sort_name.into(), sort_name: value.sort_name,
disambiguation: value.disambiguation, disambiguation: value.disambiguation,
} }
} }
@ -109,7 +109,7 @@ impl From<SerdeMbReleaseGroupMeta> for MbReleaseGroupMeta {
fn from(value: SerdeMbReleaseGroupMeta) -> Self { fn from(value: SerdeMbReleaseGroupMeta) -> Self {
MbReleaseGroupMeta { MbReleaseGroupMeta {
id: value.id.into(), id: value.id.into(),
title: value.title.into(), title: value.title,
first_release_date: value.first_release_date.into(), first_release_date: value.first_release_date.into(),
primary_type: value.primary_type.into(), primary_type: value.primary_type.into(),
secondary_types: value secondary_types: value

View File

@ -7,19 +7,21 @@ macro_rules! full_collection {
name: "Album_Artist A".to_string(), name: "Album_Artist A".to_string(),
}, },
sort: None, sort: None,
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str( info: ArtistInfo {
"https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000" musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
).unwrap()), "https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000"
properties: HashMap::from([ ).unwrap()),
(String::from("MusicButler"), vec![ properties: HashMap::from([
String::from("https://www.musicbutler.io/artist-page/000000000"), (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",
)
]),
]), ]),
(String::from("Qobuz"), vec![ },
String::from(
"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums",
)
]),
]),
}, },
albums: vec![ albums: vec![
Album { Album {
@ -129,23 +131,25 @@ macro_rules! full_collection {
name: "Album_Artist B".to_string(), name: "Album_Artist B".to_string(),
}, },
sort: None, sort: None,
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str( info: ArtistInfo {
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111" musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
).unwrap()), "https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111"
properties: HashMap::from([ ).unwrap()),
(String::from("MusicButler"), vec![ properties: HashMap::from([
String::from("https://www.musicbutler.io/artist-page/111111111"), (String::from("MusicButler"), vec![
String::from("https://www.musicbutler.io/artist-page/111111112"), 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",
)
]),
]), ]),
(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![ albums: vec![
Album { Album {
@ -316,8 +320,10 @@ macro_rules! full_collection {
name: "The Album_Artist C".to_string(), name: "The Album_Artist C".to_string(),
}, },
sort: Some("Album_Artist C, The".to_string()), sort: Some("Album_Artist C, The".to_string()),
musicbrainz: MbRefOption::CannotHaveMbid, info: ArtistInfo {
properties: HashMap::new(), musicbrainz: MbRefOption::CannotHaveMbid,
properties: HashMap::new(),
},
}, },
albums: vec![ albums: vec![
Album { Album {
@ -406,8 +412,10 @@ macro_rules! full_collection {
name: "Album_Artist D".to_string(), name: "Album_Artist D".to_string(),
}, },
sort: None, sort: None,
musicbrainz: MbRefOption::None, info: ArtistInfo {
properties: HashMap::new(), musicbrainz: MbRefOption::None,
properties: HashMap::new(),
},
}, },
albums: vec![ albums: vec![
Album { Album {

View File

@ -8,8 +8,10 @@ macro_rules! library_collection {
name: "Album_Artist A".to_string(), name: "Album_Artist A".to_string(),
}, },
sort: None, sort: None,
musicbrainz: MbRefOption::None, info: ArtistInfo {
properties: HashMap::new(), musicbrainz: MbRefOption::None,
properties: HashMap::new(),
},
}, },
albums: vec![ albums: vec![
Album { Album {
@ -117,8 +119,10 @@ macro_rules! library_collection {
name: "Album_Artist B".to_string(), name: "Album_Artist B".to_string(),
}, },
sort: None, sort: None,
musicbrainz: MbRefOption::None, info: ArtistInfo {
properties: HashMap::new(), musicbrainz: MbRefOption::None,
properties: HashMap::new(),
},
}, },
albums: vec![ albums: vec![
Album { Album {
@ -285,8 +289,10 @@ macro_rules! library_collection {
name: "The Album_Artist C".to_string(), name: "The Album_Artist C".to_string(),
}, },
sort: Some("Album_Artist C, The".to_string()), sort: Some("Album_Artist C, The".to_string()),
musicbrainz: MbRefOption::None, info: ArtistInfo {
properties: HashMap::new(), musicbrainz: MbRefOption::None,
properties: HashMap::new(),
},
}, },
albums: vec![ albums: vec![
Album { Album {
@ -375,8 +381,10 @@ macro_rules! library_collection {
name: "Album_Artist D".to_string(), name: "Album_Artist D".to_string(),
}, },
sort: None, sort: None,
musicbrainz: MbRefOption::None, info: ArtistInfo {
properties: HashMap::new(), musicbrainz: MbRefOption::None,
properties: HashMap::new(),
},
}, },
albums: vec![ albums: vec![
Album { Album {

View File

@ -156,7 +156,7 @@ impl AppMachine<FetchState> {
result_sender: ResultSender, result_sender: ResultSender,
artist: &Artist, artist: &Artist,
) -> Result<(), FetchError> { ) -> Result<(), FetchError> {
let requests = match artist.meta.musicbrainz { let requests = match artist.meta.info.musicbrainz {
MbRefOption::Some(ref arid) => { MbRefOption::Some(ref arid) => {
let arid = arid.mbid(); let arid = arid.mbid();
let albums = artist.albums.iter(); let albums = artist.albums.iter();

View File

@ -27,7 +27,7 @@ macro_rules! item_option_artist_set {
meta: &ArtistMeta, meta: &ArtistMeta,
) -> Result<(), musichoard::Error> { ) -> Result<(), musichoard::Error> {
let mbref = match self { let mbref = match self {
MatchOption::Some(m) => m.item.musicbrainz, MatchOption::Some(m) => m.item.info.musicbrainz,
MatchOption::CannotHaveMbid => MbRefOption::CannotHaveMbid, MatchOption::CannotHaveMbid => MbRefOption::CannotHaveMbid,
MatchOption::ManualInputMbid => panic!(), MatchOption::ManualInputMbid => panic!(),
}; };

View File

@ -182,7 +182,7 @@ impl IAppInteractSearchPrivate for AppMachine<SearchState> {
if let Some(ref probe_sort) = probe.meta.sort { if let Some(ref probe_sort) = probe.meta.sort {
if !result { if !result {
let name = Self::normalize_search(&probe_sort, !case_sens, !char_sens); let name = Self::normalize_search(probe_sort, !case_sens, !char_sens);
result = name.starts_with(search); result = name.starts_with(search);
} }
} }

View File

@ -5,7 +5,7 @@ use std::collections::HashMap;
use musichoard::{ use musichoard::{
collection::{ collection::{
album::{AlbumDate, AlbumMeta, AlbumSeq}, album::{AlbumDate, AlbumMeta, AlbumSeq},
artist::ArtistMeta, artist::{ArtistInfo, ArtistMeta},
musicbrainz::{MbRefOption, Mbid}, musicbrainz::{MbRefOption, Mbid},
}, },
external::musicbrainz::{ external::musicbrainz::{
@ -98,8 +98,10 @@ fn from_lookup_artist_response(entity: LookupArtistResponse) -> Lookup<ArtistMet
item: ArtistMeta { item: ArtistMeta {
id: entity.meta.name.into(), id: entity.meta.name.into(),
sort, sort,
musicbrainz: MbRefOption::Some(entity.meta.id.into()), info: ArtistInfo {
properties: HashMap::new(), musicbrainz: MbRefOption::Some(entity.meta.id.into()),
properties: HashMap::new(),
},
}, },
disambiguation: entity.meta.disambiguation, disambiguation: entity.meta.disambiguation,
} }
@ -126,8 +128,10 @@ fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Mat
item: ArtistMeta { item: ArtistMeta {
id: entity.meta.name.into(), id: entity.meta.name.into(),
sort, sort,
musicbrainz: MbRefOption::Some(entity.meta.id.into()), info: ArtistInfo {
properties: HashMap::new(), musicbrainz: MbRefOption::Some(entity.meta.id.into()),
properties: HashMap::new(),
},
}, },
disambiguation: entity.meta.disambiguation, disambiguation: entity.meta.disambiguation,
} }

View File

@ -419,7 +419,7 @@ mod tests {
} }
fn search_albums_requests() -> VecDeque<MbParams> { fn search_albums_requests() -> VecDeque<MbParams> {
let mbref = mb_ref_opt_as_ref(&COLLECTION[1].meta.musicbrainz); let mbref = mb_ref_opt_as_ref(&COLLECTION[1].meta.info.musicbrainz);
let arid = mb_ref_opt_unwrap(mbref).mbid().clone(); let arid = mb_ref_opt_unwrap(mbref).mbid().clone();
let artist_id = COLLECTION[1].meta.id.clone(); let artist_id = COLLECTION[1].meta.id.clone();
@ -437,7 +437,7 @@ mod tests {
} }
fn album_arid_expectation() -> Mbid { fn album_arid_expectation() -> Mbid {
let mbref = mb_ref_opt_as_ref(&COLLECTION[1].meta.musicbrainz); let mbref = mb_ref_opt_as_ref(&COLLECTION[1].meta.info.musicbrainz);
mb_ref_opt_unwrap(mbref).mbid().clone() mb_ref_opt_unwrap(mbref).mbid().clone()
} }

View File

@ -2,7 +2,7 @@ use std::collections::HashMap;
use musichoard::collection::{ use musichoard::collection::{
album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSeq}, album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSeq},
artist::{Artist, ArtistId, ArtistMeta}, artist::{Artist, ArtistId, ArtistInfo, ArtistMeta},
musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption}, musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption},
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality}, track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
}; };

View File

@ -76,10 +76,10 @@ impl<'a> ArtistOverlay<'a> {
Properties: {}", Properties: {}",
artist.map(|a| a.meta.id.name.as_str()).unwrap_or(""), artist.map(|a| a.meta.id.name.as_str()).unwrap_or(""),
artist artist
.map(|a| UiDisplay::display_mb_ref_option_as_url(&a.meta.musicbrainz)) .map(|a| UiDisplay::display_mb_ref_option_as_url(&a.meta.info.musicbrainz))
.unwrap_or_default(), .unwrap_or_default(),
Self::opt_hashmap_to_string( Self::opt_hashmap_to_string(
artist.map(|a| &a.meta.properties), artist.map(|a| &a.meta.info.properties),
&double_item_indent, &double_item_indent,
&double_list_indent &double_list_indent
), ),

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use musichoard::collection::{ use musichoard::collection::{
album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq}, album::{Album, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq},
artist::{Artist, ArtistId, ArtistMeta}, artist::{Artist, ArtistId, ArtistInfo, ArtistMeta},
musicbrainz::{MbArtistRef, MbRefOption}, musicbrainz::{MbArtistRef, MbRefOption},
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality}, track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
Collection, Collection,
@ -17,20 +17,22 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
name: String::from("Аркона"), name: String::from("Аркона"),
}, },
sort: Some(String::from("Arkona")), sort: Some(String::from("Arkona")),
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str( info: ArtistInfo {
"https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212" musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
).unwrap()), "https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212"
properties: HashMap::from([ ).unwrap()),
(String::from("MusicButler"), vec![ properties: HashMap::from([
String::from("https://www.musicbutler.io/artist-page/283448581"), (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",
)]),
]), ]),
(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 { albums: vec![Album {
meta: AlbumMeta { meta: AlbumMeta {
@ -207,17 +209,19 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
name: String::from("Eluveitie"), name: String::from("Eluveitie"),
}, },
sort: None, sort: None,
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str( info: ArtistInfo {
"https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38" musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
).unwrap()), "https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38"
properties: HashMap::from([ ).unwrap()),
(String::from("MusicButler"), vec![ properties: HashMap::from([
String::from("https://www.musicbutler.io/artist-page/269358403"), (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![ albums: vec![
Album { Album {
@ -454,17 +458,19 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
name: String::from("Frontside"), name: String::from("Frontside"),
}, },
sort: None, sort: None,
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str( info: ArtistInfo {
"https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490" musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
).unwrap()), "https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490"
properties: HashMap::from([ ).unwrap()),
(String::from("MusicButler"), vec![ properties: HashMap::from([
String::from("https://www.musicbutler.io/artist-page/826588800"), (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",
)]),
]), ]),
(String::from("Qobuz"), vec![String::from( },
"https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums",
)]),
]),
}, },
albums: vec![Album { albums: vec![Album {
meta: AlbumMeta { meta: AlbumMeta {
@ -608,17 +614,19 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
name: String::from("Heavens Basement"), name: String::from("Heavens Basement"),
}, },
sort: Some(String::from("Heavens Basement")), sort: Some(String::from("Heavens Basement")),
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str( info: ArtistInfo {
"https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc" musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
).unwrap()), "https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc"
properties: HashMap::from([ ).unwrap()),
(String::from("MusicButler"), vec![ properties: HashMap::from([
String::from("https://www.musicbutler.io/artist-page/291158685"), (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",
)]),
]), ]),
(String::from("Qobuz"), vec![String::from( },
"https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums",
)]),
]),
}, },
albums: vec![Album { albums: vec![Album {
meta: AlbumMeta { meta: AlbumMeta {
@ -742,17 +750,19 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
name: String::from("Metallica"), name: String::from("Metallica"),
}, },
sort: None, sort: None,
musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str( info: ArtistInfo {
"https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab" musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
).unwrap()), "https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab"
properties: HashMap::from([ ).unwrap()),
(String::from("MusicButler"), vec![ properties: HashMap::from([
String::from("https://www.musicbutler.io/artist-page/3996865"), (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![ albums: vec![
Album { Album {