Implement cannot have MBID in core #220

Merged
wojtek merged 2 commits from 189---enable-fetch-to-apply-modifications-to-the-database into main 2024-09-24 22:38:40 +02:00
20 changed files with 244 additions and 123 deletions
Showing only changes of commit f674b4c3b7 - Show all commits

View File

@ -5,7 +5,7 @@ use std::{
use crate::core::collection::{ use crate::core::collection::{
merge::{Merge, MergeSorted, WithId}, merge::{Merge, MergeSorted, WithId},
musicbrainz::MbAlbumRef, musicbrainz::{MbAlbumRef, MbRefOption},
track::{Track, TrackFormat}, track::{Track, TrackFormat},
}; };
@ -22,7 +22,7 @@ pub struct AlbumMeta {
pub id: AlbumId, pub id: AlbumId,
pub date: AlbumDate, pub date: AlbumDate,
pub seq: AlbumSeq, pub seq: AlbumSeq,
pub musicbrainz: Option<MbAlbumRef>, pub musicbrainz: MbRefOption<MbAlbumRef>,
pub primary_type: Option<AlbumPrimaryType>, pub primary_type: Option<AlbumPrimaryType>,
pub secondary_types: Vec<AlbumSecondaryType>, pub secondary_types: Vec<AlbumSecondaryType>,
} }
@ -186,7 +186,7 @@ impl AlbumMeta {
id: id.into(), id: id.into(),
date: date.into(), date: date.into(),
seq: AlbumSeq::default(), seq: AlbumSeq::default(),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type, primary_type,
secondary_types, secondary_types,
} }
@ -364,7 +364,7 @@ mod tests {
let mut album = Album::new(AlbumId::new("an album"), AlbumDate::default(), None, vec![]); let mut album = Album::new(AlbumId::new("an album"), AlbumDate::default(), None, vec![]);
let mut expected: Option<MbAlbumRef> = None; let mut expected: MbRefOption<MbAlbumRef> = MbRefOption::None;
assert_eq!(album.meta.musicbrainz, expected); assert_eq!(album.meta.musicbrainz, expected);
// Setting a URL on an album. // Setting a URL on an album.

View File

@ -7,7 +7,7 @@ use std::{
use crate::core::collection::{ use crate::core::collection::{
album::Album, album::Album,
merge::{Merge, MergeCollections, WithId}, merge::{Merge, MergeCollections, WithId},
musicbrainz::MbArtistRef, musicbrainz::{MbArtistRef, MbRefOption},
}; };
/// An artist. /// An artist.
@ -22,7 +22,7 @@ pub struct Artist {
pub struct ArtistMeta { pub struct ArtistMeta {
pub id: ArtistId, pub id: ArtistId,
pub sort: Option<ArtistId>, pub sort: Option<ArtistId>,
pub musicbrainz: Option<MbArtistRef>, pub musicbrainz: MbRefOption<MbArtistRef>,
pub properties: HashMap<String, Vec<String>>, pub properties: HashMap<String, Vec<String>>,
} }
@ -75,7 +75,7 @@ impl ArtistMeta {
ArtistMeta { ArtistMeta {
id: id.into(), id: id.into(),
sort: None, sort: None,
musicbrainz: None, musicbrainz: MbRefOption::None,
properties: HashMap::new(), properties: HashMap::new(),
} }
} }
@ -255,7 +255,7 @@ mod tests {
fn set_clear_musicbrainz_url() { fn set_clear_musicbrainz_url() {
let mut artist = Artist::new(ArtistId::new("an artist")); let mut artist = Artist::new(ArtistId::new("an artist"));
let mut expected: Option<MbArtistRef> = None; let mut expected: MbRefOption<MbArtistRef> = MbRefOption::None;
assert_eq!(artist.meta.musicbrainz, expected); assert_eq!(artist.meta.musicbrainz, expected);
// Setting a URL on an artist. // Setting a URL on an artist.
@ -417,7 +417,7 @@ 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 = None; right.meta.musicbrainz = MbRefOption::None;
right.meta.properties = HashMap::new(); right.meta.properties = HashMap::new();
let mut expected = left.clone(); let mut expected = left.clone();

View File

@ -1,4 +1,4 @@
use std::fmt::{Debug, Display}; use std::{fmt, mem};
use url::Url; use url::Url;
use uuid::Uuid; use uuid::Uuid;
@ -38,6 +38,30 @@ try_from_impl_for_mbid!(&str);
try_from_impl_for_mbid!(&String); try_from_impl_for_mbid!(&String);
try_from_impl_for_mbid!(String); try_from_impl_for_mbid!(String);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MbRefOption<T> {
Some(T),
CannotHaveMbid,
None,
}
impl<T> MbRefOption<T> {
pub fn or(self, optb: MbRefOption<T>) -> MbRefOption<T> {
match self {
x @ MbRefOption::Some(_) => x,
MbRefOption::CannotHaveMbid | MbRefOption::None => optb,
}
}
pub fn replace(&mut self, value: T) -> MbRefOption<T> {
mem::replace(self, MbRefOption::Some(value))
}
pub fn take(&mut self) -> MbRefOption<T> {
mem::replace(self, MbRefOption::None)
}
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
struct MusicBrainzRef { struct MusicBrainzRef {
mbid: Mbid, mbid: Mbid,
@ -142,7 +166,7 @@ impl MusicBrainzRef {
MusicBrainzRef { mbid, url } MusicBrainzRef { mbid, url }
} }
fn invalid_url_error<U: Display>(url: U, entity: &'static str) -> Error { fn invalid_url_error<U: fmt::Display>(url: U, entity: &'static str) -> Error {
Error::UrlError(format!("invalid {entity} MusicBrainz URL: {url}")) Error::UrlError(format!("invalid {entity} MusicBrainz URL: {url}"))
} }
} }

View File

@ -297,11 +297,14 @@ impl<Database: IDatabase, Library> MusicHoard<Database, Library> {
mod tests { mod tests {
use mockall::{predicate, Sequence}; use mockall::{predicate, Sequence};
use crate::core::{ use crate::{
collection::{album::AlbumDate, artist::ArtistId}, collection::musicbrainz::MbRefOption,
interface::database::{self, MockIDatabase}, core::{
musichoard::{base::IMusicHoardBase, NoLibrary}, collection::{album::AlbumDate, artist::ArtistId},
testmod::FULL_COLLECTION, interface::database::{self, MockIDatabase},
musichoard::{base::IMusicHoardBase, NoLibrary},
testmod::FULL_COLLECTION,
},
}; };
use super::*; use super::*;
@ -441,7 +444,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: Option<MbArtistRef> = None; let mut expected: MbRefOption<MbArtistRef> = MbRefOption::None;
assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected); assert_eq!(music_hoard.collection[0].meta.musicbrainz, expected);
// Setting a URL on an artist not in the collection is an error. // Setting a URL on an artist not in the collection is an error.

View File

@ -4,7 +4,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, ArtistMeta},
musicbrainz::{MbAlbumRef, MbArtistRef}, musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption},
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality}, track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
}; };
use crate::testmod::*; use crate::testmod::*;

View File

@ -1,10 +1,10 @@
pub static DATABASE_JSON: &str = "{\ pub static DATABASE_JSON: &str = "{\
\"V20240828\":\ \"V20240924\":\
[\ [\
{\ {\
\"name\":\"Album_Artist A\",\ \"name\":\"Album_Artist A\",\
\"sort\":null,\ \"sort\":null,\
\"musicbrainz\":\"00000000-0000-0000-0000-000000000000\",\ \"musicbrainz\":{\"Some\":\"00000000-0000-0000-0000-000000000000\"},\
\"properties\":{\ \"properties\":{\
\"MusicButler\":[\"https://www.musicbutler.io/artist-page/000000000\"],\ \"MusicButler\":[\"https://www.musicbutler.io/artist-page/000000000\"],\
\"Qobuz\":[\"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums\"]\ \"Qobuz\":[\"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums\"]\
@ -12,11 +12,11 @@ pub static DATABASE_JSON: &str = "{\
\"albums\":[\ \"albums\":[\
{\ {\
\"title\":\"album_title a.a\",\"seq\":1,\ \"title\":\"album_title a.a\",\"seq\":1,\
\"musicbrainz\":\"00000000-0000-0000-0000-000000000000\",\ \"musicbrainz\":{\"Some\":\"00000000-0000-0000-0000-000000000000\"},\
\"primary_type\":\"Album\",\"secondary_types\":[]\ \"primary_type\":\"Album\",\"secondary_types\":[]\
},\ },\
{\ {\
\"title\":\"album_title a.b\",\"seq\":1,\"musicbrainz\":null,\ \"title\":\"album_title a.b\",\"seq\":1,\"musicbrainz\":\"None\",\
\"primary_type\":\"Album\",\"secondary_types\":[]\ \"primary_type\":\"Album\",\"secondary_types\":[]\
}\ }\
]\ ]\
@ -24,7 +24,7 @@ pub static DATABASE_JSON: &str = "{\
{\ {\
\"name\":\"Album_Artist B\",\ \"name\":\"Album_Artist B\",\
\"sort\":null,\ \"sort\":null,\
\"musicbrainz\":\"11111111-1111-1111-1111-111111111111\",\ \"musicbrainz\":{\"Some\":\"11111111-1111-1111-1111-111111111111\"},\
\"properties\":{\ \"properties\":{\
\"Bandcamp\":[\"https://artist-b.bandcamp.com/\"],\ \"Bandcamp\":[\"https://artist-b.bandcamp.com/\"],\
\"MusicButler\":[\ \"MusicButler\":[\
@ -35,21 +35,21 @@ pub static DATABASE_JSON: &str = "{\
},\ },\
\"albums\":[\ \"albums\":[\
{\ {\
\"title\":\"album_title b.a\",\"seq\":1,\"musicbrainz\":null,\ \"title\":\"album_title b.a\",\"seq\":1,\"musicbrainz\":\"None\",\
\"primary_type\":\"Album\",\"secondary_types\":[]\ \"primary_type\":\"Album\",\"secondary_types\":[]\
},\ },\
{\ {\
\"title\":\"album_title b.b\",\"seq\":3,\ \"title\":\"album_title b.b\",\"seq\":3,\
\"musicbrainz\":\"11111111-1111-1111-1111-111111111111\",\ \"musicbrainz\":{\"Some\":\"11111111-1111-1111-1111-111111111111\"},\
\"primary_type\":\"Album\",\"secondary_types\":[]\ \"primary_type\":\"Album\",\"secondary_types\":[]\
},\ },\
{\ {\
\"title\":\"album_title b.c\",\"seq\":2,\ \"title\":\"album_title b.c\",\"seq\":2,\
\"musicbrainz\":\"11111111-1111-1111-1111-111111111112\",\ \"musicbrainz\":{\"Some\":\"11111111-1111-1111-1111-111111111112\"},\
\"primary_type\":\"Album\",\"secondary_types\":[]\ \"primary_type\":\"Album\",\"secondary_types\":[]\
},\ },\
{\ {\
\"title\":\"album_title b.d\",\"seq\":4,\"musicbrainz\":null,\ \"title\":\"album_title b.d\",\"seq\":4,\"musicbrainz\":\"None\",\
\"primary_type\":\"Album\",\"secondary_types\":[]\ \"primary_type\":\"Album\",\"secondary_types\":[]\
}\ }\
]\ ]\
@ -57,15 +57,15 @@ pub static DATABASE_JSON: &str = "{\
{\ {\
\"name\":\"The Album_Artist C\",\ \"name\":\"The Album_Artist C\",\
\"sort\":\"Album_Artist C, The\",\ \"sort\":\"Album_Artist C, The\",\
\"musicbrainz\":\"11111111-1111-1111-1111-111111111111\",\ \"musicbrainz\":\"CannotHaveMbid\",\
\"properties\":{},\ \"properties\":{},\
\"albums\":[\ \"albums\":[\
{\ {\
\"title\":\"album_title c.a\",\"seq\":0,\"musicbrainz\":null,\ \"title\":\"album_title c.a\",\"seq\":0,\"musicbrainz\":\"None\",\
\"primary_type\":\"Album\",\"secondary_types\":[]\ \"primary_type\":\"Album\",\"secondary_types\":[]\
},\ },\
{\ {\
\"title\":\"album_title c.b\",\"seq\":0,\"musicbrainz\":null,\ \"title\":\"album_title c.b\",\"seq\":0,\"musicbrainz\":\"None\",\
\"primary_type\":\"Album\",\"secondary_types\":[]\ \"primary_type\":\"Album\",\"secondary_types\":[]\
}\ }\
]\ ]\
@ -73,15 +73,15 @@ pub static DATABASE_JSON: &str = "{\
{\ {\
\"name\":\"Album_Artist D\",\ \"name\":\"Album_Artist D\",\
\"sort\":null,\ \"sort\":null,\
\"musicbrainz\":null,\ \"musicbrainz\":\"None\",\
\"properties\":{},\ \"properties\":{},\
\"albums\":[\ \"albums\":[\
{\ {\
\"title\":\"album_title d.a\",\"seq\":0,\"musicbrainz\":null,\ \"title\":\"album_title d.a\",\"seq\":0,\"musicbrainz\":\"None\",\
\"primary_type\":\"Album\",\"secondary_types\":[]\ \"primary_type\":\"Album\",\"secondary_types\":[]\
},\ },\
{\ {\
\"title\":\"album_title d.b\",\"seq\":0,\"musicbrainz\":null,\ \"title\":\"album_title d.b\",\"seq\":0,\"musicbrainz\":\"None\",\
\"primary_type\":\"Album\",\"secondary_types\":[]\ \"primary_type\":\"Album\",\"secondary_types\":[]\
}\ }\
]\ ]\

View File

@ -1,6 +1,17 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::core::collection::album::{AlbumPrimaryType, AlbumSecondaryType}; use crate::{
collection::musicbrainz::MbRefOption,
core::collection::album::{AlbumPrimaryType, AlbumSecondaryType},
};
#[derive(Debug, Deserialize, Serialize)]
#[serde(remote = "MbRefOption")]
pub enum MbRefOptionDef<T> {
Some(T),
CannotHaveMbid,
None,
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
#[serde(remote = "AlbumPrimaryType")] #[serde(remote = "AlbumPrimaryType")]

View File

@ -3,7 +3,11 @@ use std::{collections::HashMap, fmt};
use serde::{de::Visitor, Deserialize, Deserializer}; use serde::{de::Visitor, Deserialize, Deserializer};
use crate::{ use crate::{
collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid}, collection::{
album::AlbumMeta,
artist::ArtistMeta,
musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption, Mbid},
},
core::collection::{ core::collection::{
album::{Album, AlbumDate, AlbumId, AlbumSeq}, album::{Album, AlbumDate, AlbumId, AlbumSeq},
artist::{Artist, ArtistId}, artist::{Artist, ArtistId},
@ -12,15 +16,17 @@ use crate::{
external::database::serde::common::{SerdeAlbumPrimaryType, SerdeAlbumSecondaryType}, external::database::serde::common::{SerdeAlbumPrimaryType, SerdeAlbumSecondaryType},
}; };
use super::common::MbRefOptionDef;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub enum DeserializeDatabase { pub enum DeserializeDatabase {
V20240828(Vec<DeserializeArtist>), V20240924(Vec<DeserializeArtist>),
} }
impl From<DeserializeDatabase> for Collection { impl From<DeserializeDatabase> for Collection {
fn from(database: DeserializeDatabase) -> Self { fn from(database: DeserializeDatabase) -> Self {
match database { match database {
DeserializeDatabase::V20240828(collection) => { DeserializeDatabase::V20240924(collection) => {
collection.into_iter().map(Into::into).collect() collection.into_iter().map(Into::into).collect()
} }
} }
@ -31,7 +37,7 @@ impl From<DeserializeDatabase> for Collection {
pub struct DeserializeArtist { pub struct DeserializeArtist {
name: String, name: String,
sort: Option<String>, sort: Option<String>,
musicbrainz: Option<DeserializeMbid>, musicbrainz: DeserializeMbRefOption,
properties: HashMap<String, Vec<String>>, properties: HashMap<String, Vec<String>>,
albums: Vec<DeserializeAlbum>, albums: Vec<DeserializeAlbum>,
} }
@ -40,14 +46,34 @@ pub struct DeserializeArtist {
pub struct DeserializeAlbum { pub struct DeserializeAlbum {
title: String, title: String,
seq: u8, seq: u8,
musicbrainz: Option<DeserializeMbid>, musicbrainz: DeserializeMbRefOption,
primary_type: Option<SerdeAlbumPrimaryType>, primary_type: Option<SerdeAlbumPrimaryType>,
secondary_types: Vec<SerdeAlbumSecondaryType>, secondary_types: Vec<SerdeAlbumSecondaryType>,
} }
#[derive(Debug, Deserialize)]
pub struct DeserializeMbRefOption(#[serde(with = "MbRefOptionDef")] MbRefOption<DeserializeMbid>);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DeserializeMbid(Mbid); pub struct DeserializeMbid(Mbid);
macro_rules! impl_from_for_mb_ref_option {
($ref:ty) => {
impl From<DeserializeMbRefOption> for MbRefOption<$ref> {
fn from(value: DeserializeMbRefOption) -> Self {
match value.0 {
MbRefOption::Some(val) => MbRefOption::Some(val.0.into()),
MbRefOption::CannotHaveMbid => MbRefOption::CannotHaveMbid,
MbRefOption::None => MbRefOption::None,
}
}
}
};
}
impl_from_for_mb_ref_option!(MbArtistRef);
impl_from_for_mb_ref_option!(MbAlbumRef);
impl From<DeserializeMbid> for Mbid { impl From<DeserializeMbid> for Mbid {
fn from(value: DeserializeMbid) -> Self { fn from(value: DeserializeMbid) -> Self {
value.0 value.0
@ -89,7 +115,7 @@ impl From<DeserializeArtist> for Artist {
meta: ArtistMeta { meta: ArtistMeta {
id: ArtistId::new(artist.name), id: ArtistId::new(artist.name),
sort: artist.sort.map(ArtistId::new), sort: artist.sort.map(ArtistId::new),
musicbrainz: artist.musicbrainz.map(Into::<Mbid>::into).map(Into::into), musicbrainz: artist.musicbrainz.into(),
properties: artist.properties, properties: artist.properties,
}, },
albums: artist.albums.into_iter().map(Into::into).collect(), albums: artist.albums.into_iter().map(Into::into).collect(),
@ -104,7 +130,7 @@ impl From<DeserializeAlbum> for Album {
id: AlbumId { title: album.title }, id: AlbumId { title: album.title },
date: AlbumDate::default(), date: AlbumDate::default(),
seq: AlbumSeq(album.seq), seq: AlbumSeq(album.seq),
musicbrainz: album.musicbrainz.map(Into::<Mbid>::into).map(Into::into), musicbrainz: album.musicbrainz.into(),
primary_type: album.primary_type.map(Into::into), primary_type: album.primary_type.map(Into::into),
secondary_types: album.secondary_types.into_iter().map(Into::into).collect(), secondary_types: album.secondary_types.into_iter().map(Into::into).collect(),
}, },

View File

@ -3,19 +3,21 @@ use std::collections::BTreeMap;
use serde::Serialize; use serde::Serialize;
use crate::{ use crate::{
collection::musicbrainz::Mbid, collection::musicbrainz::{MbRefOption, Mbid},
core::collection::{album::Album, artist::Artist, musicbrainz::IMusicBrainzRef, Collection}, core::collection::{album::Album, artist::Artist, musicbrainz::IMusicBrainzRef, Collection},
external::database::serde::common::{SerdeAlbumPrimaryType, SerdeAlbumSecondaryType}, external::database::serde::common::{
MbRefOptionDef, SerdeAlbumPrimaryType, SerdeAlbumSecondaryType,
},
}; };
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub enum SerializeDatabase<'a> { pub enum SerializeDatabase<'a> {
V20240828(Vec<SerializeArtist<'a>>), V20240924(Vec<SerializeArtist<'a>>),
} }
impl<'a> From<&'a Collection> for SerializeDatabase<'a> { impl<'a> From<&'a Collection> for SerializeDatabase<'a> {
fn from(collection: &'a Collection) -> Self { fn from(collection: &'a Collection) -> Self {
SerializeDatabase::V20240828(collection.iter().map(Into::into).collect()) SerializeDatabase::V20240924(collection.iter().map(Into::into).collect())
} }
} }
@ -23,7 +25,7 @@ impl<'a> From<&'a Collection> for SerializeDatabase<'a> {
pub struct SerializeArtist<'a> { pub struct SerializeArtist<'a> {
name: &'a str, name: &'a str,
sort: Option<&'a str>, sort: Option<&'a str>,
musicbrainz: Option<SerializeMbid<'a>>, musicbrainz: SerializeMbRefOption<'a>,
properties: BTreeMap<&'a str, &'a Vec<String>>, properties: BTreeMap<&'a str, &'a Vec<String>>,
albums: Vec<SerializeAlbum<'a>>, albums: Vec<SerializeAlbum<'a>>,
} }
@ -32,14 +34,31 @@ pub struct SerializeArtist<'a> {
pub struct SerializeAlbum<'a> { pub struct SerializeAlbum<'a> {
title: &'a str, title: &'a str,
seq: u8, seq: u8,
musicbrainz: Option<SerializeMbid<'a>>, musicbrainz: SerializeMbRefOption<'a>,
primary_type: Option<SerdeAlbumPrimaryType>, primary_type: Option<SerdeAlbumPrimaryType>,
secondary_types: Vec<SerdeAlbumSecondaryType>, secondary_types: Vec<SerdeAlbumSecondaryType>,
} }
#[derive(Debug, Serialize)]
pub struct SerializeMbRefOption<'a>(
#[serde(with = "MbRefOptionDef")] MbRefOption<SerializeMbid<'a>>,
);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SerializeMbid<'a>(&'a Mbid); pub struct SerializeMbid<'a>(&'a Mbid);
impl<'a, T: IMusicBrainzRef> From<&'a MbRefOption<T>> for SerializeMbRefOption<'a> {
fn from(value: &'a MbRefOption<T>) -> Self {
match value {
MbRefOption::Some(val) => {
SerializeMbRefOption(MbRefOption::Some(SerializeMbid(val.mbid())))
}
MbRefOption::CannotHaveMbid => SerializeMbRefOption(MbRefOption::CannotHaveMbid),
MbRefOption::None => SerializeMbRefOption(MbRefOption::None),
}
}
}
impl<'a> Serialize for SerializeMbid<'a> { impl<'a> Serialize for SerializeMbid<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
@ -54,11 +73,7 @@ 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_ref().map(|id| id.name.as_ref()), sort: artist.meta.sort.as_ref().map(|id| id.name.as_ref()),
musicbrainz: artist musicbrainz: (&artist.meta.musicbrainz).into(),
.meta
.musicbrainz
.as_ref()
.map(|mbref| SerializeMbid(mbref.mbid())),
properties: artist properties: artist
.meta .meta
.properties .properties
@ -75,11 +90,7 @@ impl<'a> From<&'a Album> for SerializeAlbum<'a> {
SerializeAlbum { SerializeAlbum {
title: &album.meta.id.title, title: &album.meta.id.title,
seq: album.meta.seq.0, seq: album.meta.seq.0,
musicbrainz: album musicbrainz: (&album.meta.musicbrainz).into(),
.meta
.musicbrainz
.as_ref()
.map(|mbref| SerializeMbid(mbref.mbid())),
primary_type: album.meta.primary_type.map(Into::into), primary_type: album.meta.primary_type.map(Into::into),
secondary_types: album secondary_types: album
.meta .meta

View File

@ -7,7 +7,7 @@ macro_rules! full_collection {
name: "Album_Artist A".to_string(), name: "Album_Artist A".to_string(),
}, },
sort: None, sort: None,
musicbrainz: Some(MbArtistRef::from_url_str( musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
"https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000" "https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000"
).unwrap()), ).unwrap()),
properties: HashMap::from([ properties: HashMap::from([
@ -29,7 +29,7 @@ macro_rules! full_collection {
}, },
date: 1998.into(), date: 1998.into(),
seq: AlbumSeq(1), seq: AlbumSeq(1),
musicbrainz: Some(MbAlbumRef::from_url_str( musicbrainz: MbRefOption::Some(MbAlbumRef::from_url_str(
"https://musicbrainz.org/release-group/00000000-0000-0000-0000-000000000000" "https://musicbrainz.org/release-group/00000000-0000-0000-0000-000000000000"
).unwrap()), ).unwrap()),
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
@ -92,7 +92,7 @@ macro_rules! full_collection {
}, },
date: (2015, 4).into(), date: (2015, 4).into(),
seq: AlbumSeq(1), seq: AlbumSeq(1),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -129,7 +129,7 @@ macro_rules! full_collection {
name: "Album_Artist B".to_string(), name: "Album_Artist B".to_string(),
}, },
sort: None, sort: None,
musicbrainz: Some(MbArtistRef::from_url_str( musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111" "https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111"
).unwrap()), ).unwrap()),
properties: HashMap::from([ properties: HashMap::from([
@ -155,7 +155,7 @@ macro_rules! full_collection {
}, },
date: (2003, 6, 6).into(), date: (2003, 6, 6).into(),
seq: AlbumSeq(1), seq: AlbumSeq(1),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -194,7 +194,7 @@ macro_rules! full_collection {
}, },
date: 2008.into(), date: 2008.into(),
seq: AlbumSeq(3), seq: AlbumSeq(3),
musicbrainz: Some(MbAlbumRef::from_url_str( musicbrainz: MbRefOption::Some(MbAlbumRef::from_url_str(
"https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111111" "https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111111"
).unwrap()), ).unwrap()),
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
@ -235,7 +235,7 @@ macro_rules! full_collection {
}, },
date: 2009.into(), date: 2009.into(),
seq: AlbumSeq(2), seq: AlbumSeq(2),
musicbrainz: Some(MbAlbumRef::from_url_str( musicbrainz: MbRefOption::Some(MbAlbumRef::from_url_str(
"https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111112" "https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111112"
).unwrap()), ).unwrap()),
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
@ -276,7 +276,7 @@ macro_rules! full_collection {
}, },
date: 2015.into(), date: 2015.into(),
seq: AlbumSeq(4), seq: AlbumSeq(4),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -318,9 +318,7 @@ macro_rules! full_collection {
sort: Some(ArtistId { sort: Some(ArtistId {
name: "Album_Artist C, The".to_string(), name: "Album_Artist C, The".to_string(),
}), }),
musicbrainz: Some(MbArtistRef::from_url_str( musicbrainz: MbRefOption::CannotHaveMbid,
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111"
).unwrap()),
properties: HashMap::new(), properties: HashMap::new(),
}, },
albums: vec![ albums: vec![
@ -331,7 +329,7 @@ macro_rules! full_collection {
}, },
date: 1985.into(), date: 1985.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -370,7 +368,7 @@ macro_rules! full_collection {
}, },
date: 2018.into(), date: 2018.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -410,7 +408,7 @@ macro_rules! full_collection {
name: "Album_Artist D".to_string(), name: "Album_Artist D".to_string(),
}, },
sort: None, sort: None,
musicbrainz: None, musicbrainz: MbRefOption::None,
properties: HashMap::new(), properties: HashMap::new(),
}, },
albums: vec![ albums: vec![
@ -421,7 +419,7 @@ macro_rules! full_collection {
}, },
date: 1995.into(), date: 1995.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -460,7 +458,7 @@ macro_rules! full_collection {
}, },
date: 2028.into(), date: 2028.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },

View File

@ -8,7 +8,7 @@ macro_rules! library_collection {
name: "Album_Artist A".to_string(), name: "Album_Artist A".to_string(),
}, },
sort: None, sort: None,
musicbrainz: None, musicbrainz: MbRefOption::None,
properties: HashMap::new(), properties: HashMap::new(),
}, },
albums: vec![ albums: vec![
@ -19,7 +19,7 @@ macro_rules! library_collection {
}, },
date: 1998.into(), date: 1998.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },
@ -80,7 +80,7 @@ macro_rules! library_collection {
}, },
date: (2015, 4).into(), date: (2015, 4).into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },
@ -117,7 +117,7 @@ macro_rules! library_collection {
name: "Album_Artist B".to_string(), name: "Album_Artist B".to_string(),
}, },
sort: None, sort: None,
musicbrainz: None, musicbrainz: MbRefOption::None,
properties: HashMap::new(), properties: HashMap::new(),
}, },
albums: vec![ albums: vec![
@ -128,7 +128,7 @@ macro_rules! library_collection {
}, },
date: (2003, 6, 6).into(), date: (2003, 6, 6).into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },
@ -167,7 +167,7 @@ macro_rules! library_collection {
}, },
date: 2008.into(), date: 2008.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },
@ -206,7 +206,7 @@ macro_rules! library_collection {
}, },
date: 2009.into(), date: 2009.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },
@ -245,7 +245,7 @@ macro_rules! library_collection {
}, },
date: 2015.into(), date: 2015.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },
@ -287,7 +287,7 @@ macro_rules! library_collection {
sort: Some(ArtistId { sort: Some(ArtistId {
name: "Album_Artist C, The".to_string(), name: "Album_Artist C, The".to_string(),
}), }),
musicbrainz: None, musicbrainz: MbRefOption::None,
properties: HashMap::new(), properties: HashMap::new(),
}, },
albums: vec![ albums: vec![
@ -298,7 +298,7 @@ macro_rules! library_collection {
}, },
date: 1985.into(), date: 1985.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },
@ -337,7 +337,7 @@ macro_rules! library_collection {
}, },
date: 2018.into(), date: 2018.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },
@ -377,7 +377,7 @@ macro_rules! library_collection {
name: "Album_Artist D".to_string(), name: "Album_Artist D".to_string(),
}, },
sort: None, sort: None,
musicbrainz: None, musicbrainz: MbRefOption::None,
properties: HashMap::new(), properties: HashMap::new(),
}, },
albums: vec![ albums: vec![
@ -388,7 +388,7 @@ macro_rules! library_collection {
}, },
date: 1995.into(), date: 1995.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },
@ -427,7 +427,7 @@ macro_rules! library_collection {
}, },
date: 2028.into(), date: 2028.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },

View File

@ -6,7 +6,7 @@ use std::{
use musichoard::collection::{ use musichoard::collection::{
album::AlbumMeta, album::AlbumMeta,
artist::{Artist, ArtistMeta}, artist::{Artist, ArtistMeta},
musicbrainz::{IMusicBrainzRef, Mbid}, musicbrainz::{IMusicBrainzRef, MbRefOption, Mbid},
}; };
use crate::tui::{ use crate::tui::{
@ -35,9 +35,8 @@ impl FetchState {
fn try_recv(&mut self) -> Result<MbApiResult, TryRecvError> { fn try_recv(&mut self) -> Result<MbApiResult, TryRecvError> {
if let Some(lookup_rx) = &self.lookup_rx { if let Some(lookup_rx) = &self.lookup_rx {
let result = lookup_rx.try_recv(); match lookup_rx.try_recv() {
match result { x @ Ok(_) | x @ Err(TryRecvError::Empty) => return x,
Ok(_) | Err(TryRecvError::Empty) => return result,
Err(TryRecvError::Disconnected) => { Err(TryRecvError::Disconnected) => {
self.lookup_rx.take(); self.lookup_rx.take();
} }
@ -142,15 +141,16 @@ impl AppMachine<FetchState> {
artist: &Artist, artist: &Artist,
) -> Result<(), DaemonError> { ) -> Result<(), DaemonError> {
let requests = match artist.meta.musicbrainz { let requests = match artist.meta.musicbrainz {
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();
albums albums
.filter(|album| album.meta.musicbrainz.is_none()) .filter(|album| matches!(album.meta.musicbrainz, MbRefOption::None))
.map(|album| MbParams::search_release_group(arid.clone(), album.meta.clone())) .map(|album| MbParams::search_release_group(arid.clone(), album.meta.clone()))
.collect() .collect()
} }
None => VecDeque::from([MbParams::search_artist(artist.meta.clone())]), MbRefOption::CannotHaveMbid => return Ok(()),
MbRefOption::None => VecDeque::from([MbParams::search_artist(artist.meta.clone())]),
}; };
musicbrainz.submit_background_job(result_sender, requests) musicbrainz.submit_background_job(result_sender, requests)
} }
@ -379,6 +379,20 @@ mod tests {
AppMachine::app_lookup_artist(inner, fetch, &artist, mbid()); AppMachine::app_lookup_artist(inner, fetch, &artist, mbid());
} }
#[test]
fn fetch_artist_cannot_have_mbid() {
let music_hoard = music_hoard(COLLECTION.to_owned());
let inner = inner(music_hoard);
// Use third artist to match the expectation.
let browse = AppMachine::browse_state(inner);
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let app = browse.increment_selection(Delta::Line);
let app = app.unwrap_browse().fetch_musicbrainz();
assert!(matches!(app, AppState::Match(_)));
}
#[test] #[test]
fn fetch_artist_job_sender_err() { fn fetch_artist_job_sender_err() {
let mut mb_job_sender = MockIMbJobSender::new(); let mut mb_job_sender = MockIMbJobSender::new();

View File

@ -6,7 +6,7 @@ use musichoard::{
collection::{ collection::{
album::{AlbumDate, AlbumMeta, AlbumSeq}, album::{AlbumDate, AlbumMeta, AlbumSeq},
artist::{ArtistId, ArtistMeta}, artist::{ArtistId, ArtistMeta},
musicbrainz::Mbid, musicbrainz::{MbRefOption, Mbid},
}, },
external::musicbrainz::{ external::musicbrainz::{
api::{ api::{
@ -100,7 +100,7 @@ fn from_lookup_artist_response(entity: LookupArtistResponse) -> Lookup<ArtistMet
item: ArtistMeta { item: ArtistMeta {
id: entity.meta.name, id: entity.meta.name,
sort, sort,
musicbrainz: Some(entity.meta.id.into()), musicbrainz: MbRefOption::Some(entity.meta.id.into()),
properties: HashMap::new(), properties: HashMap::new(),
}, },
disambiguation: entity.meta.disambiguation, disambiguation: entity.meta.disambiguation,
@ -113,7 +113,7 @@ fn from_lookup_release_group_response(entity: LookupReleaseGroupResponse) -> Loo
id: entity.meta.title, id: entity.meta.title,
date: entity.meta.first_release_date, date: entity.meta.first_release_date,
seq: AlbumSeq::default(), seq: AlbumSeq::default(),
musicbrainz: Some(entity.meta.id.into()), musicbrainz: MbRefOption::Some(entity.meta.id.into()),
primary_type: Some(entity.meta.primary_type), primary_type: Some(entity.meta.primary_type),
secondary_types: entity.meta.secondary_types.unwrap_or_default(), secondary_types: entity.meta.secondary_types.unwrap_or_default(),
}, },
@ -130,7 +130,7 @@ fn from_search_artist_response_artist(entity: SearchArtistResponseArtist) -> Mat
item: ArtistMeta { item: ArtistMeta {
id: entity.meta.name, id: entity.meta.name,
sort, sort,
musicbrainz: Some(entity.meta.id.into()), musicbrainz: MbRefOption::Some(entity.meta.id.into()),
properties: HashMap::new(), properties: HashMap::new(),
}, },
disambiguation: entity.meta.disambiguation, disambiguation: entity.meta.disambiguation,
@ -146,7 +146,7 @@ fn from_search_release_group_response_release_group(
id: entity.meta.title, id: entity.meta.title,
date: entity.meta.first_release_date, date: entity.meta.first_release_date,
seq: AlbumSeq::default(), seq: AlbumSeq::default(),
musicbrainz: Some(entity.meta.id.into()), musicbrainz: MbRefOption::Some(entity.meta.id.into()),
primary_type: Some(entity.meta.primary_type), primary_type: Some(entity.meta.primary_type),
secondary_types: entity.meta.secondary_types.unwrap_or_default(), secondary_types: entity.meta.secondary_types.unwrap_or_default(),
}, },

View File

@ -316,7 +316,7 @@ mod tests {
use musichoard::collection::{ use musichoard::collection::{
album::AlbumMeta, album::AlbumMeta,
artist::ArtistMeta, artist::ArtistMeta,
musicbrainz::{IMusicBrainzRef, Mbid}, musicbrainz::{IMusicBrainzRef, MbRefOption, Mbid},
}; };
use crate::tui::{ use crate::tui::{
@ -327,6 +327,21 @@ mod tests {
use super::*; use super::*;
fn mb_ref_opt_unwrap<T>(opt: MbRefOption<T>) -> T {
match opt {
MbRefOption::Some(val) => val,
_ => panic!(),
}
}
fn mb_ref_opt_as_ref<T>(opt: &MbRefOption<T>) -> MbRefOption<&T> {
match *opt {
MbRefOption::Some(ref x) => MbRefOption::Some(x),
MbRefOption::CannotHaveMbid => MbRefOption::CannotHaveMbid,
MbRefOption::None => MbRefOption::None,
}
}
fn musicbrainz() -> MockIMusicBrainz { fn musicbrainz() -> MockIMusicBrainz {
MockIMusicBrainz::new() MockIMusicBrainz::new()
} }
@ -403,8 +418,8 @@ mod tests {
} }
fn search_albums_requests() -> VecDeque<MbParams> { fn search_albums_requests() -> VecDeque<MbParams> {
let mbref = COLLECTION[1].meta.musicbrainz.as_ref(); let mbref = mb_ref_opt_as_ref(&COLLECTION[1].meta.musicbrainz);
let arid = mbref.unwrap().mbid().clone(); let arid = mb_ref_opt_unwrap(mbref).mbid().clone();
let album_1 = COLLECTION[1].albums[0].meta.clone(); let album_1 = COLLECTION[1].albums[0].meta.clone();
let album_4 = COLLECTION[1].albums[3].meta.clone(); let album_4 = COLLECTION[1].albums[3].meta.clone();
@ -416,8 +431,8 @@ mod tests {
} }
fn album_arid_expectation() -> Mbid { fn album_arid_expectation() -> Mbid {
let mbref = COLLECTION[1].meta.musicbrainz.as_ref(); let mbref = mb_ref_opt_as_ref(&COLLECTION[1].meta.musicbrainz);
mbref.unwrap().mbid().clone() mb_ref_opt_unwrap(mbref).mbid().clone()
} }
fn search_album_expectations_1() -> (AlbumMeta, Vec<Match<AlbumMeta>>) { fn search_album_expectations_1() -> (AlbumMeta, Vec<Match<AlbumMeta>>) {

View File

@ -3,7 +3,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, ArtistMeta},
musicbrainz::{MbAlbumRef, MbArtistRef}, musicbrainz::{MbAlbumRef, MbArtistRef, MbRefOption},
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality}, track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
}; };
use once_cell::sync::Lazy; use once_cell::sync::Lazy;

View File

@ -1,6 +1,7 @@
use musichoard::collection::{ use musichoard::collection::{
album::{AlbumDate, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq, AlbumStatus}, album::{AlbumDate, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq, AlbumStatus},
artist::ArtistMeta, artist::ArtistMeta,
musicbrainz::{IMusicBrainzRef, MbRefOption},
track::{TrackFormat, TrackQuality}, track::{TrackFormat, TrackQuality},
}; };
@ -30,6 +31,14 @@ impl UiDisplay {
} }
} }
pub fn display_mb_ref_option_as_url<T: IMusicBrainzRef>(option: &MbRefOption<T>) -> &str {
match option {
MbRefOption::Some(val) => val.url().as_str(),
MbRefOption::CannotHaveMbid => "cannot have a MusicBrainz identifier",
MbRefOption::None => "",
}
}
pub fn display_type( pub fn display_type(
primary: &Option<AlbumPrimaryType>, primary: &Option<AlbumPrimaryType>,
secondary: &Vec<AlbumSecondaryType>, secondary: &Vec<AlbumSecondaryType>,

View File

@ -1,8 +1,10 @@
use std::collections::HashMap; use std::collections::HashMap;
use musichoard::collection::{album::Album, artist::Artist, musicbrainz::IMusicBrainzRef}; use musichoard::collection::{album::Album, artist::Artist};
use ratatui::widgets::{ListState, Paragraph}; use ratatui::widgets::{ListState, Paragraph};
use super::display::UiDisplay;
struct InfoOverlay; struct InfoOverlay;
impl InfoOverlay { impl InfoOverlay {
@ -74,7 +76,7 @@ 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
.and_then(|a| a.meta.musicbrainz.as_ref().map(|mb| mb.url().as_str())) .and_then(|a| Some(UiDisplay::display_mb_ref_option_as_url(&a.meta.musicbrainz)))
.unwrap_or(""), .unwrap_or(""),
Self::opt_hashmap_to_string( Self::opt_hashmap_to_string(
artist.map(|a| &a.meta.properties), artist.map(|a| &a.meta.properties),
@ -102,7 +104,7 @@ impl<'a> AlbumOverlay<'a> {
MusicBrainz: {}", MusicBrainz: {}",
album.map(|a| a.meta.id.title.as_str()).unwrap_or(""), album.map(|a| a.meta.id.title.as_str()).unwrap_or(""),
album album
.and_then(|a| a.meta.musicbrainz.as_ref().map(|mb| mb.url().as_str())) .and_then(|a| Some(UiDisplay::display_mb_ref_option_as_url(&a.meta.musicbrainz)))
.unwrap_or(""), .unwrap_or(""),
)); ));

View File

@ -305,7 +305,15 @@ mod tests {
draw_test_suite(artists, &mut selection); draw_test_suite(artists, &mut selection);
// Change the artist (which cannot have a MBID).
selection.increment_selection(artists, Delta::Line);
selection.increment_selection(artists, Delta::Line);
draw_test_suite(artists, &mut selection);
// Change the track (which has a different track format). // Change the track (which has a different track format).
selection.decrement_selection(artists, Delta::Line);
selection.decrement_selection(artists, Delta::Line);
selection.increment_category(); selection.increment_category();
selection.increment_category(); selection.increment_category();
selection.increment_selection(artists, Delta::Line); selection.increment_selection(artists, Delta::Line);

View File

@ -1 +1 @@
{"V20240828":[{"name":"Аркона","sort":"Arkona","musicbrainz":"baad262d-55ef-427a-83c7-f7530964f212","properties":{"Bandcamp":["https://arkonamoscow.bandcamp.com/"],"MusicButler":["https://www.musicbutler.io/artist-page/283448581"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/arkona/download-streaming-albums"]},"albums":[{"title":"Slovo","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":[]}]},{"name":"Eluveitie","sort":null,"musicbrainz":"8000598a-5edb-401c-8e6d-36b167feaf38","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/269358403"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/eluveitie/download-streaming-albums"]},"albums":[{"title":"Vên [rerecorded]","seq":0,"musicbrainz":null,"primary_type":"Ep","secondary_types":[]},{"title":"Slania","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":[]}]},{"name":"Frontside","sort":null,"musicbrainz":"3a901353-fccd-4afd-ad01-9c03f451b490","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/826588800"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums"]},"albums":[{"title":"…nasze jest królestwo, potęga i chwała na wieki…","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":[]}]},{"name":"Heavens Basement","sort":"Heavens Basement","musicbrainz":"c2c4d56a-d599-4a18-bd2f-ae644e2198cc","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/291158685"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums"]},"albums":[{"title":"Paper Plague","seq":0,"musicbrainz":null,"primary_type":null,"secondary_types":[]},{"title":"Unbreakable","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":[]}]},{"name":"Metallica","sort":null,"musicbrainz":"65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/3996865"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/metallica/download-streaming-albums"]},"albums":[{"title":"Ride the Lightning","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":[]},{"title":"S&M","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":["Live"]}]}]} {"V20240924":[{"name":"Аркона","sort":"Arkona","musicbrainz":{"Some":"baad262d-55ef-427a-83c7-f7530964f212"},"properties":{"Bandcamp":["https://arkonamoscow.bandcamp.com/"],"MusicButler":["https://www.musicbutler.io/artist-page/283448581"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/arkona/download-streaming-albums"]},"albums":[{"title":"Slovo","seq":0,"musicbrainz":"None","primary_type":"Album","secondary_types":[]}]},{"name":"Eluveitie","sort":null,"musicbrainz":{"Some":"8000598a-5edb-401c-8e6d-36b167feaf38"},"properties":{"MusicButler":["https://www.musicbutler.io/artist-page/269358403"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/eluveitie/download-streaming-albums"]},"albums":[{"title":"Vên [rerecorded]","seq":0,"musicbrainz":"None","primary_type":"Ep","secondary_types":[]},{"title":"Slania","seq":0,"musicbrainz":"None","primary_type":"Album","secondary_types":[]}]},{"name":"Frontside","sort":null,"musicbrainz":{"Some":"3a901353-fccd-4afd-ad01-9c03f451b490"},"properties":{"MusicButler":["https://www.musicbutler.io/artist-page/826588800"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums"]},"albums":[{"title":"…nasze jest królestwo, potęga i chwała na wieki…","seq":0,"musicbrainz":"None","primary_type":"Album","secondary_types":[]}]},{"name":"Heavens Basement","sort":"Heavens Basement","musicbrainz":{"Some":"c2c4d56a-d599-4a18-bd2f-ae644e2198cc"},"properties":{"MusicButler":["https://www.musicbutler.io/artist-page/291158685"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums"]},"albums":[{"title":"Paper Plague","seq":0,"musicbrainz":"None","primary_type":null,"secondary_types":[]},{"title":"Unbreakable","seq":0,"musicbrainz":"None","primary_type":"Album","secondary_types":[]}]},{"name":"Metallica","sort":null,"musicbrainz":{"Some":"65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab"},"properties":{"MusicButler":["https://www.musicbutler.io/artist-page/3996865"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/metallica/download-streaming-albums"]},"albums":[{"title":"Ride the Lightning","seq":0,"musicbrainz":"None","primary_type":"Album","secondary_types":[]},{"title":"S&M","seq":0,"musicbrainz":"None","primary_type":"Album","secondary_types":["Live"]}]}]}

View File

@ -4,7 +4,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, ArtistMeta},
musicbrainz::MbArtistRef, musicbrainz::{MbArtistRef, MbRefOption},
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality}, track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
Collection, Collection,
}; };
@ -19,7 +19,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
sort: Some(ArtistId{ sort: Some(ArtistId{
name: String::from("Arkona") name: String::from("Arkona")
}), }),
musicbrainz: Some(MbArtistRef::from_url_str( musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
"https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212" "https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212"
).unwrap()), ).unwrap()),
properties: HashMap::from([ properties: HashMap::from([
@ -41,7 +41,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
}, },
date: 2011.into(), date: 2011.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -209,7 +209,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
name: String::from("Eluveitie"), name: String::from("Eluveitie"),
}, },
sort: None, sort: None,
musicbrainz: Some(MbArtistRef::from_url_str( musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
"https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38" "https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38"
).unwrap()), ).unwrap()),
properties: HashMap::from([ properties: HashMap::from([
@ -229,7 +229,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
}, },
date: 2004.into(), date: 2004.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Ep), primary_type: Some(AlbumPrimaryType::Ep),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -309,7 +309,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
}, },
date: 2008.into(), date: 2008.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -456,7 +456,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
name: String::from("Frontside"), name: String::from("Frontside"),
}, },
sort: None, sort: None,
musicbrainz: Some(MbArtistRef::from_url_str( musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
"https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490" "https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490"
).unwrap()), ).unwrap()),
properties: HashMap::from([ properties: HashMap::from([
@ -475,7 +475,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
}, },
date: 2001.into(), date: 2001.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -612,7 +612,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
sort: Some(ArtistId { sort: Some(ArtistId {
name: String::from("Heavens Basement"), name: String::from("Heavens Basement"),
}), }),
musicbrainz: Some(MbArtistRef::from_url_str( musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
"https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc" "https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc"
).unwrap()), ).unwrap()),
properties: HashMap::from([ properties: HashMap::from([
@ -631,7 +631,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
}, },
date: 2011.into(), date: 2011.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: None, primary_type: None,
secondary_types: vec![], secondary_types: vec![],
}, },
@ -655,7 +655,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
}, },
date: 2011.into(), date: 2011.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -746,7 +746,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
name: String::from("Metallica"), name: String::from("Metallica"),
}, },
sort: None, sort: None,
musicbrainz: Some(MbArtistRef::from_url_str( musicbrainz: MbRefOption::Some(MbArtistRef::from_url_str(
"https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab" "https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab"
).unwrap()), ).unwrap()),
properties: HashMap::from([ properties: HashMap::from([
@ -766,7 +766,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
}, },
date: 1984.into(), date: 1984.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![], secondary_types: vec![],
}, },
@ -868,7 +868,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
}, },
date: 1999.into(), date: 1999.into(),
seq: AlbumSeq(0), seq: AlbumSeq(0),
musicbrainz: None, musicbrainz: MbRefOption::None,
primary_type: Some(AlbumPrimaryType::Album), primary_type: Some(AlbumPrimaryType::Album),
secondary_types: vec![AlbumSecondaryType::Live], secondary_types: vec![AlbumSecondaryType::Live],
}, },