use std::{num::ParseIntError, str::FromStr}; use musichoard::{ collection::{album::AlbumDate, musicbrainz::Mbid}, external::musicbrainz::{ api::{ search::{SearchArtistRequest, SearchReleaseGroupRequest}, MusicBrainzClient, }, http::MusicBrainzHttp, }, }; use structopt::StructOpt; use uuid::Uuid; const USER_AGENT: &str = concat!( "MusicHoard---examples---musicbrainz-api---search/", env!("CARGO_PKG_VERSION"), " ( musichoard@thenineworlds.net )" ); #[derive(StructOpt)] struct Opt { #[structopt(subcommand)] entity: OptEntity, } #[derive(StructOpt)] enum OptEntity { #[structopt(about = "Search artist")] Artist(OptArtist), #[structopt(about = "Search release group")] ReleaseGroup(OptReleaseGroup), } #[derive(StructOpt)] struct OptArtist { #[structopt(help = "Artist search string")] string: String, } #[derive(StructOpt)] enum OptReleaseGroup { #[structopt(about = "Search by artist MBID, title(, and date)")] Title(OptReleaseGroupTitle), #[structopt(about = "Search by release group MBID")] Rgid(OptReleaseGroupRgid), } #[derive(StructOpt)] struct OptReleaseGroupTitle { #[structopt(help = "Release group's artist MBID")] arid: Uuid, #[structopt(help = "Release group title")] title: String, #[structopt(help = "Release group release date")] date: Option, } #[derive(StructOpt)] struct OptReleaseGroupRgid { #[structopt(help = "Release group MBID")] rgid: Uuid, } struct Date(AlbumDate); impl FromStr for Date { type Err = ParseIntError; fn from_str(s: &str) -> Result { let mut elems = s.split('-'); let elem = elems.next(); let year = elem.map(|s| s.parse()).transpose()?; let elem = elems.next(); let month = elem.map(|s| s.parse()).transpose()?; let elem = elems.next(); let day = elem.map(|s| s.parse()).transpose()?; Ok(Date(AlbumDate::new(year, month, day))) } } impl From for AlbumDate { fn from(value: Date) -> Self { value.0 } } fn main() { let opt = Opt::from_args(); println!("USER_AGENT: {USER_AGENT}"); let http = MusicBrainzHttp::new(USER_AGENT).expect("failed to create API client"); let mut client = MusicBrainzClient::new(http); match opt.entity { OptEntity::Artist(opt_artist) => { let query = SearchArtistRequest::new().string(&opt_artist.string); println!("Query: {query}"); let matches = client .search_artist(query) .expect("failed to make API call"); println!("{matches:#?}"); } OptEntity::ReleaseGroup(opt_release_group) => { let arid: Mbid; let date: AlbumDate; let title: String; let rgid: Mbid; let query = match opt_release_group { OptReleaseGroup::Title(opt_title) => { arid = opt_title.arid.into(); date = opt_title.date.map(Into::into).unwrap_or_default(); title = opt_title.title; SearchReleaseGroupRequest::new() .arid(&arid) .and() .release_group(&title) .and() .first_release_date(&date) } OptReleaseGroup::Rgid(opt_rgid) => { rgid = opt_rgid.rgid.into(); SearchReleaseGroupRequest::new().rgid(&rgid) } }; println!("Query: {query}"); let matches = client .search_release_group(query) .expect("failed to make API call"); println!("{matches:#?}"); } } }