Add method to manually add artist metadata #85
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -273,6 +273,15 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@ -366,6 +375,7 @@ name = "musichoard"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"itertools 0.12.0",
|
||||||
"mockall",
|
"mockall",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"openssh",
|
"openssh",
|
||||||
@ -476,7 +486,7 @@ checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"difflib",
|
"difflib",
|
||||||
"float-cmp",
|
"float-cmp",
|
||||||
"itertools",
|
"itertools 0.10.5",
|
||||||
"normalize-line-endings",
|
"normalize-line-endings",
|
||||||
"predicates-core",
|
"predicates-core",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossterm = { version = "0.26.1", optional = true}
|
crossterm = { version = "0.26.1", optional = true}
|
||||||
|
itertools = { version = "0.12.0" }
|
||||||
openssh = { version = "0.9.9", features = ["native-mux"], default-features = false, optional = true}
|
openssh = { version = "0.9.9", features = ["native-mux"], default-features = false, optional = true}
|
||||||
ratatui = { version = "0.20.1", optional = true}
|
ratatui = { version = "0.20.1", optional = true}
|
||||||
serde = { version = "1.0.159", features = ["derive"] }
|
serde = { version = "1.0.159", features = ["derive"] }
|
||||||
|
@ -126,38 +126,24 @@ impl ArtistCommand {
|
|||||||
clear_musicbutler_urls,
|
clear_musicbutler_urls,
|
||||||
urls
|
urls
|
||||||
),
|
),
|
||||||
ArtistCommand::Bandcamp(url_command) => {
|
ArtistCommand::Bandcamp(url_command) => url_command_dispatch!(
|
||||||
match url_command {
|
url_command,
|
||||||
UrlCommand::Add(_) => {
|
music_hoard,
|
||||||
// Add URL.
|
add_bandcamp_urls,
|
||||||
}
|
remove_bandcamp_urls,
|
||||||
UrlCommand::Remove(_) => {
|
set_bandcamp_urls,
|
||||||
// Remove URL if it exists.
|
clear_bandcamp_urls,
|
||||||
}
|
urls
|
||||||
UrlCommand::Set(_) => {
|
),
|
||||||
// Set the URLs regardless of previous (if any) value.
|
ArtistCommand::Qobuz(url_command) => url_command_dispatch!(
|
||||||
}
|
url_command,
|
||||||
UrlCommand::Clear(_) => {
|
music_hoard,
|
||||||
// Remove the URLs.
|
add_qobuz_url,
|
||||||
}
|
remove_qobuz_url,
|
||||||
}
|
set_qobuz_url,
|
||||||
}
|
clear_qobuz_url,
|
||||||
ArtistCommand::Qobuz(url_command) => {
|
url
|
||||||
match url_command {
|
),
|
||||||
UrlCommand::Add(_) => {
|
|
||||||
// Add URL or return error if one already existss.
|
|
||||||
}
|
|
||||||
UrlCommand::Remove(_) => {
|
|
||||||
// Remove URL if it exists.
|
|
||||||
}
|
|
||||||
UrlCommand::Set(_) => {
|
|
||||||
// Set the URL regardless of previous (if any) value.
|
|
||||||
}
|
|
||||||
UrlCommand::Clear(_) => {
|
|
||||||
// Remove the URL.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
434
src/lib.rs
434
src/lib.rs
@ -6,12 +6,13 @@ pub mod library;
|
|||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fmt,
|
fmt::{self, Debug},
|
||||||
iter::Peekable,
|
iter::Peekable,
|
||||||
mem,
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
use database::IDatabase;
|
use database::IDatabase;
|
||||||
|
use itertools::Itertools;
|
||||||
use library::{ILibrary, Item, Query};
|
use library::{ILibrary, Item, Query};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@ -79,6 +80,20 @@ impl MusicBrainz {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for MusicBrainz {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
MusicBrainz::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for MusicBrainz {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl IUrl for MusicBrainz {
|
impl IUrl for MusicBrainz {
|
||||||
fn url(&self) -> &str {
|
fn url(&self) -> &str {
|
||||||
self.0.as_str()
|
self.0.as_str()
|
||||||
@ -119,6 +134,20 @@ impl MusicButler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for MusicButler {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
MusicButler::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for MusicButler {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl IUrl for MusicButler {
|
impl IUrl for MusicButler {
|
||||||
fn url(&self) -> &str {
|
fn url(&self) -> &str {
|
||||||
self.0.as_str()
|
self.0.as_str()
|
||||||
@ -152,6 +181,20 @@ impl Bandcamp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Bandcamp {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
Bandcamp::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for Bandcamp {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl IUrl for Bandcamp {
|
impl IUrl for Bandcamp {
|
||||||
fn url(&self) -> &str {
|
fn url(&self) -> &str {
|
||||||
self.0.as_str()
|
self.0.as_str()
|
||||||
@ -185,6 +228,20 @@ impl Qobuz {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Qobuz {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
Qobuz::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for Qobuz {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl IUrl for Qobuz {
|
impl IUrl for Qobuz {
|
||||||
fn url(&self) -> &str {
|
fn url(&self) -> &str {
|
||||||
self.0.as_str()
|
self.0.as_str()
|
||||||
@ -325,6 +382,46 @@ pub struct Artist {
|
|||||||
pub albums: Vec<Album>,
|
pub albums: Vec<Album>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! artist_unique_url_dispatch {
|
||||||
|
($add:ident, $remove:ident, $set:ident, $clear:ident, $label:literal, $field:ident) => {
|
||||||
|
fn $add<S: AsRef<str>>(&mut self, url: S) -> Result<(), Error> {
|
||||||
|
Self::add_unique_url(&self.id, $label, &mut self.properties.$field, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn $remove<S: AsRef<str>>(&mut self, url: S) -> Result<(), Error> {
|
||||||
|
Self::remove_unique_url(&self.id, $label, &mut self.properties.$field, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn $set<S: AsRef<str>>(&mut self, url: S) -> Result<(), Error> {
|
||||||
|
Self::set_unique_url(&mut self.properties.$field, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn $clear(&mut self) {
|
||||||
|
Self::clear_unique_url(&mut self.properties.$field);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! artist_multi_url_dispatch {
|
||||||
|
($add:ident, $remove:ident, $set:ident, $clear:ident, $label:literal, $field:ident) => {
|
||||||
|
fn $add<S: AsRef<str>>(&mut self, urls: Vec<S>) -> Result<(), Error> {
|
||||||
|
Self::add_multi_urls(&self.id, $label, &mut self.properties.$field, urls)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn $remove<S: AsRef<str>>(&mut self, urls: Vec<S>) -> Result<(), Error> {
|
||||||
|
Self::remove_multi_urls(&self.id, $label, &mut self.properties.$field, urls)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn $set<S: AsRef<str>>(&mut self, urls: Vec<S>) -> Result<(), Error> {
|
||||||
|
Self::set_multi_urls(&mut self.properties.$field, urls)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn $clear(&mut self) {
|
||||||
|
Self::clear_multi_urls(&mut self.properties.$field);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl Artist {
|
impl Artist {
|
||||||
pub fn new(id: ArtistId) -> Self {
|
pub fn new(id: ArtistId) -> Self {
|
||||||
Artist {
|
Artist {
|
||||||
@ -334,106 +431,189 @@ impl Artist {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_musicbrainz_url<S: AsRef<str>>(&mut self, url: S) -> Result<(), Error> {
|
fn add_unique_url<
|
||||||
if self.properties.musicbrainz.is_some() {
|
S: AsRef<str>,
|
||||||
return Err(Error::CollectionError(format!(
|
T: for<'a> TryFrom<&'a str, Error = Error> + Eq + AsRef<str>,
|
||||||
"artist '{}' already has a MusicBrainz URL",
|
>(
|
||||||
self.id
|
artist_id: &ArtistId,
|
||||||
)));
|
label: &'static str,
|
||||||
|
container: &mut Option<T>,
|
||||||
|
url: S,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let url: T = url.as_ref().try_into()?;
|
||||||
|
|
||||||
|
if let Some(current) = container {
|
||||||
|
if current == &url {
|
||||||
|
return Err(Error::CollectionError(format!(
|
||||||
|
"artist '{}' already has this {} URL: {}",
|
||||||
|
artist_id,
|
||||||
|
label,
|
||||||
|
current.as_ref()
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
return Err(Error::CollectionError(format!(
|
||||||
|
"artist '{}' already has a different {} URL: {}",
|
||||||
|
artist_id,
|
||||||
|
label,
|
||||||
|
current.as_ref()
|
||||||
|
)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.properties.musicbrainz = Some(MusicBrainz::new(url)?);
|
_ = container.insert(url);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_musicbrainz_url<S: AsRef<str>>(&mut self, url: S) -> Result<(), Error> {
|
fn remove_unique_url<
|
||||||
if self.properties.musicbrainz.is_none() {
|
S: AsRef<str>,
|
||||||
|
T: for<'a> TryFrom<&'a str, Error = Error> + Eq + AsRef<str>,
|
||||||
|
>(
|
||||||
|
artist_id: &ArtistId,
|
||||||
|
label: &'static str,
|
||||||
|
container: &mut Option<T>,
|
||||||
|
url: S,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if container.is_none() {
|
||||||
return Err(Error::CollectionError(format!(
|
return Err(Error::CollectionError(format!(
|
||||||
"artist '{}' does not have a MusicBrainz URL",
|
"artist '{}' does not have a {} URL",
|
||||||
self.id
|
artist_id, label
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.properties.musicbrainz.as_ref().unwrap().0.as_str() == url.as_ref() {
|
let url: T = url.as_ref().try_into()?;
|
||||||
self.properties.musicbrainz = None;
|
if container.as_ref().unwrap() == &url {
|
||||||
|
_ = container.take();
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::CollectionError(format!(
|
Err(Error::CollectionError(format!(
|
||||||
"artist '{}' does not have this MusicBrainz URL {}",
|
"artist '{}' does not have this {} URL: {}",
|
||||||
self.id,
|
artist_id,
|
||||||
|
label,
|
||||||
url.as_ref(),
|
url.as_ref(),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_musicbrainz_url<S: AsRef<str>>(&mut self, url: S) -> Result<(), Error> {
|
fn set_unique_url<S: AsRef<str>, T: for<'a> TryFrom<&'a str, Error = Error>>(
|
||||||
self.properties.musicbrainz = Some(MusicBrainz::new(url)?);
|
container: &mut Option<T>,
|
||||||
|
url: S,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
_ = container.insert(url.as_ref().try_into()?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_musicbrainz_url(&mut self) -> Result<(), Error> {
|
fn clear_unique_url<T>(container: &mut Option<T>) {
|
||||||
self.properties.musicbrainz = None;
|
_ = container.take();
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_musicbutler_urls<S: AsRef<str>>(&mut self, urls: Vec<S>) -> Result<(), Error> {
|
fn add_multi_urls<
|
||||||
|
S: AsRef<str>,
|
||||||
|
T: for<'a> TryFrom<&'a str, Error = Error> + Eq + AsRef<str>,
|
||||||
|
>(
|
||||||
|
artist_id: &ArtistId,
|
||||||
|
label: &'static str,
|
||||||
|
container: &mut Vec<T>,
|
||||||
|
urls: Vec<S>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
// Convert into URLs first to facilitate later comparison.
|
// Convert into URLs first to facilitate later comparison.
|
||||||
let urls: Result<Vec<MusicButler>, Error> = urls.iter().map(MusicButler::new).collect();
|
let urls: Result<Vec<T>, Error> = urls.iter().map(|url| url.as_ref().try_into()).collect();
|
||||||
let mut urls = urls?;
|
let mut urls = urls?;
|
||||||
|
|
||||||
// Do not check and insert. First check if any of the provided URLs already exist so that
|
// Do not check and insert. First check if any of the provided URLs already exist so that
|
||||||
// the vector remains unchanged in case of failure.
|
// the vector remains unchanged in case of failure.
|
||||||
let overlap: Vec<&MusicButler> = urls
|
let overlap: Vec<&T> = urls.iter().filter(|url| container.contains(url)).collect();
|
||||||
.iter()
|
|
||||||
.filter(|url| self.properties.musicbutler.contains(url))
|
|
||||||
.collect();
|
|
||||||
if !overlap.is_empty() {
|
if !overlap.is_empty() {
|
||||||
return Err(Error::CollectionError(format!(
|
return Err(Error::CollectionError(format!(
|
||||||
"artist '{}' already has these MusicButler URL(s): {:?}",
|
"artist '{}' already has these {} URL(s): {}",
|
||||||
self.id, overlap
|
artist_id,
|
||||||
|
label,
|
||||||
|
overlap.iter().map(|url| url.as_ref()).format(", ")
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.properties.musicbutler.append(&mut urls);
|
container.append(&mut urls);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_musicbutler_urls<S: AsRef<str>>(&mut self, urls: Vec<S>) -> Result<(), Error> {
|
fn remove_multi_urls<
|
||||||
|
S: AsRef<str>,
|
||||||
|
T: for<'a> TryFrom<&'a str, Error = Error> + Eq + AsRef<str>,
|
||||||
|
>(
|
||||||
|
artist_id: &ArtistId,
|
||||||
|
label: &'static str,
|
||||||
|
container: &mut Vec<T>,
|
||||||
|
urls: Vec<S>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
// Convert into URLs first to facilitate later comparison.
|
// Convert into URLs first to facilitate later comparison.
|
||||||
let urls: Result<Vec<MusicButler>, Error> = urls.iter().map(MusicButler::new).collect();
|
let urls: Result<Vec<T>, Error> = urls.iter().map(|url| url.as_ref().try_into()).collect();
|
||||||
let urls = urls?;
|
let urls = urls?;
|
||||||
|
|
||||||
// Do not check and insert. First check if any of the provided URLs already exist so that
|
// Do not check and insert. First check if any of the provided URLs already exist so that
|
||||||
// the vector remains unchanged in case of failure.
|
// the vector remains unchanged in case of failure.
|
||||||
let difference: Vec<&MusicButler> = urls
|
let difference: Vec<&T> = urls.iter().filter(|url| !container.contains(url)).collect();
|
||||||
.iter()
|
|
||||||
.filter(|url| !self.properties.musicbutler.contains(url))
|
|
||||||
.collect();
|
|
||||||
if !difference.is_empty() {
|
if !difference.is_empty() {
|
||||||
return Err(Error::CollectionError(format!(
|
return Err(Error::CollectionError(format!(
|
||||||
"artist '{}' does not have these MusicButler URL(s): {:?}",
|
"artist '{}' does not have these {} URL(s): {}",
|
||||||
self.id, difference
|
artist_id,
|
||||||
|
label,
|
||||||
|
difference.iter().map(|url| url.as_ref()).format(", ")
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let musicbutler = mem::take(&mut self.properties.musicbutler);
|
container.retain(|url| !urls.contains(url));
|
||||||
self.properties.musicbutler = musicbutler
|
|
||||||
.into_iter()
|
|
||||||
.filter(|url| !urls.contains(url))
|
|
||||||
.collect();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_musicbutler_urls<S: AsRef<str>>(&mut self, urls: Vec<S>) -> Result<(), Error> {
|
fn set_multi_urls<S: AsRef<str>, T: for<'a> TryFrom<&'a str, Error = Error>>(
|
||||||
let urls: Result<Vec<MusicButler>, Error> = urls.iter().map(MusicButler::new).collect();
|
container: &mut Vec<T>,
|
||||||
self.properties.musicbutler = urls?;
|
urls: Vec<S>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let urls: Result<Vec<T>, Error> = urls.iter().map(|url| url.as_ref().try_into()).collect();
|
||||||
|
let mut urls = urls?;
|
||||||
|
container.clear();
|
||||||
|
container.append(&mut urls);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_musicbutler_urls(&mut self) -> Result<(), Error> {
|
fn clear_multi_urls<T>(container: &mut Vec<T>) {
|
||||||
self.properties.musicbutler.clear();
|
container.clear();
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
artist_unique_url_dispatch!(
|
||||||
|
add_musicbrainz_url,
|
||||||
|
remove_musicbrainz_url,
|
||||||
|
set_musicbrainz_url,
|
||||||
|
clear_musicbrainz_url,
|
||||||
|
"MusicBrainz",
|
||||||
|
musicbrainz
|
||||||
|
);
|
||||||
|
|
||||||
|
artist_multi_url_dispatch!(
|
||||||
|
add_musicbutler_urls,
|
||||||
|
remove_musicbutler_urls,
|
||||||
|
set_musicbutler_urls,
|
||||||
|
clear_musicbutler_urls,
|
||||||
|
"MusicButler",
|
||||||
|
musicbutler
|
||||||
|
);
|
||||||
|
|
||||||
|
artist_multi_url_dispatch!(
|
||||||
|
add_bandcamp_urls,
|
||||||
|
remove_bandcamp_urls,
|
||||||
|
set_bandcamp_urls,
|
||||||
|
clear_bandcamp_urls,
|
||||||
|
"Bandcamp",
|
||||||
|
bandcamp
|
||||||
|
);
|
||||||
|
|
||||||
|
artist_unique_url_dispatch!(
|
||||||
|
add_qobuz_url,
|
||||||
|
remove_qobuz_url,
|
||||||
|
set_qobuz_url,
|
||||||
|
clear_qobuz_url,
|
||||||
|
"Qobuz",
|
||||||
|
qobuz
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Artist {
|
impl PartialOrd for Artist {
|
||||||
@ -597,6 +777,72 @@ pub struct MusicHoard<LIB, DB> {
|
|||||||
collection: Collection,
|
collection: Collection,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! music_hoard_unique_url_dispatch {
|
||||||
|
($add:ident, $remove:ident, $set:ident, $clear:ident) => {
|
||||||
|
pub fn $add<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
||||||
|
&mut self,
|
||||||
|
artist_id: ID,
|
||||||
|
url: S,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.get_artist_or_err(artist_id.as_ref())?.$add(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn $remove<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
||||||
|
&mut self,
|
||||||
|
artist_id: ID,
|
||||||
|
url: S,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.get_artist_or_err(artist_id.as_ref())?.$remove(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn $set<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
||||||
|
&mut self,
|
||||||
|
artist_id: ID,
|
||||||
|
url: S,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.get_artist_or_err(artist_id.as_ref())?.$set(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn $clear<ID: AsRef<ArtistId>>(&mut self, artist_id: ID) -> Result<(), Error> {
|
||||||
|
self.get_artist_or_err(artist_id.as_ref())?.$clear();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! music_hoard_multi_url_dispatch {
|
||||||
|
($add:ident, $remove:ident, $set:ident, $clear:ident) => {
|
||||||
|
pub fn $add<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
||||||
|
&mut self,
|
||||||
|
artist_id: ID,
|
||||||
|
urls: Vec<S>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.get_artist_or_err(artist_id.as_ref())?.$add(urls)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn $remove<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
||||||
|
&mut self,
|
||||||
|
artist_id: ID,
|
||||||
|
urls: Vec<S>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.get_artist_or_err(artist_id.as_ref())?.$remove(urls)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn $set<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
||||||
|
&mut self,
|
||||||
|
artist_id: ID,
|
||||||
|
urls: Vec<S>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.get_artist_or_err(artist_id.as_ref())?.$set(urls)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn $clear<ID: AsRef<ArtistId>>(&mut self, artist_id: ID) -> Result<(), Error> {
|
||||||
|
self.get_artist_or_err(artist_id.as_ref())?.$clear();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl<LIB: ILibrary, DB: IDatabase> MusicHoard<LIB, DB> {
|
impl<LIB: ILibrary, DB: IDatabase> MusicHoard<LIB, DB> {
|
||||||
/// Create a new [`MusicHoard`] with the provided [`ILibrary`] and [`IDatabase`].
|
/// Create a new [`MusicHoard`] with the provided [`ILibrary`] and [`IDatabase`].
|
||||||
pub fn new(library: Option<LIB>, database: Option<DB>) -> Self {
|
pub fn new(library: Option<LIB>, database: Option<DB>) -> Self {
|
||||||
@ -687,75 +933,33 @@ impl<LIB: ILibrary, DB: IDatabase> MusicHoard<LIB, DB> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_musicbrainz_url<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
music_hoard_unique_url_dispatch!(
|
||||||
&mut self,
|
add_musicbrainz_url,
|
||||||
artist_id: ID,
|
remove_musicbrainz_url,
|
||||||
url: S,
|
set_musicbrainz_url,
|
||||||
) -> Result<(), Error> {
|
clear_musicbrainz_url
|
||||||
self.get_artist_or_err(artist_id.as_ref())?
|
);
|
||||||
.add_musicbrainz_url(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_musicbrainz_url<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
music_hoard_multi_url_dispatch!(
|
||||||
&mut self,
|
add_musicbutler_urls,
|
||||||
artist_id: ID,
|
remove_musicbutler_urls,
|
||||||
url: S,
|
set_musicbutler_urls,
|
||||||
) -> Result<(), Error> {
|
clear_musicbutler_urls
|
||||||
self.get_artist_or_err(artist_id.as_ref())?
|
);
|
||||||
.remove_musicbrainz_url(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_musicbrainz_url<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
music_hoard_multi_url_dispatch!(
|
||||||
&mut self,
|
add_bandcamp_urls,
|
||||||
artist_id: ID,
|
remove_bandcamp_urls,
|
||||||
url: S,
|
set_bandcamp_urls,
|
||||||
) -> Result<(), Error> {
|
clear_bandcamp_urls
|
||||||
self.get_artist_or_err(artist_id.as_ref())?
|
);
|
||||||
.set_musicbrainz_url(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_musicbrainz_url<ID: AsRef<ArtistId>>(
|
music_hoard_unique_url_dispatch!(
|
||||||
&mut self,
|
add_qobuz_url,
|
||||||
artist_id: ID,
|
remove_qobuz_url,
|
||||||
) -> Result<(), Error> {
|
set_qobuz_url,
|
||||||
self.get_artist_or_err(artist_id.as_ref())?
|
clear_qobuz_url
|
||||||
.clear_musicbrainz_url()
|
);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_musicbutler_urls<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
|
||||||
&mut self,
|
|
||||||
artist_id: ID,
|
|
||||||
urls: Vec<S>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.get_artist_or_err(artist_id.as_ref())?
|
|
||||||
.add_musicbutler_urls(urls)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_musicbutler_urls<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
|
||||||
&mut self,
|
|
||||||
artist_id: ID,
|
|
||||||
urls: Vec<S>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.get_artist_or_err(artist_id.as_ref())?
|
|
||||||
.remove_musicbutler_urls(urls)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_musicbutler_urls<ID: AsRef<ArtistId>, S: AsRef<str>>(
|
|
||||||
&mut self,
|
|
||||||
artist_id: ID,
|
|
||||||
urls: Vec<S>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.get_artist_or_err(artist_id.as_ref())?
|
|
||||||
.set_musicbutler_urls(urls)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_musicbutler_urls<ID: AsRef<ArtistId>>(
|
|
||||||
&mut self,
|
|
||||||
artist_id: ID,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.get_artist_or_err(artist_id.as_ref())?
|
|
||||||
.clear_musicbutler_urls()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sort(collection: &mut [Artist]) {
|
fn sort(collection: &mut [Artist]) {
|
||||||
collection.sort_unstable();
|
collection.sort_unstable();
|
||||||
|
Loading…
Reference in New Issue
Block a user