use std::{fmt::Display, str::FromStr};
use serde::Deserialize;
use super::{
album::SimplifiedAlbum,
artist::Artist,
audiobook::SimplifiedAudiobook,
playlist::SimplifiedPlaylist,
show::{SimplifiedEpisode, SimplifiedShow},
track::Track,
Page,
};
#[derive(Clone, Debug, Default)]
pub struct SearchQuery {
query: String,
album: Option<String>,
artist: Option<String>,
track: Option<String>,
year: Option<String>,
irsc: Option<String>,
genre: Option<String>,
upc: Option<String>,
hipster: bool,
new: bool,
}
impl SearchQuery {
pub fn from_query(query: impl Into<String>) -> Self {
Self {
query: query.into(),
..Default::default()
}
}
pub fn album(mut self, album: impl Into<String>) -> Self {
self.album = Some(album.into());
self
}
pub fn artist(mut self, artist: impl Into<String>) -> Self {
self.artist = Some(artist.into());
self
}
pub fn track(mut self, track: impl Into<String>) -> Self {
self.track = Some(track.into());
self
}
pub fn year(mut self, year: u32) -> Self {
self.year = Some(year.to_string());
self
}
pub fn years(mut self, start_year: u32, end_year: u32) -> Self {
self.year = Some(format!("{start_year}-{end_year}"));
self
}
pub fn irsc(mut self, irsc: impl Into<String>) -> Self {
self.irsc = Some(irsc.into());
self
}
pub fn genre(mut self, genre: impl Into<String>) -> Self {
self.genre = Some(genre.into());
self
}
pub fn upc(mut self, upc: impl Into<String>) -> Self {
self.upc = Some(upc.into());
self
}
pub fn hipster(mut self, hipster: bool) -> Self {
self.hipster = hipster;
self
}
pub fn new(mut self, new: bool) -> Self {
self.new = new;
self
}
}
impl<T> From<T> for SearchQuery
where
T: Into<String>,
{
fn from(value: T) -> Self {
Self::from_query(value)
}
}
impl Display for SearchQuery {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.query)?;
if let Some(album) = &self.album {
write!(f, " album:{album}")?;
}
if let Some(artist) = &self.artist {
write!(f, " artist:{artist}")?;
}
if let Some(track) = &self.track {
write!(f, " track:{track}")?;
}
if let Some(year) = &self.year {
write!(f, " year:{year}")?;
}
if let Some(irsc) = &self.irsc {
write!(f, " irsc:{irsc}")?;
}
if let Some(genre) = &self.genre {
write!(f, " genre:{genre}")?;
}
if let Some(upc) = &self.upc {
write!(f, " upc:{upc}")?;
}
if self.hipster {
write!(f, " tag:hipster")?;
}
if self.new {
write!(f, " tag:new")?;
}
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct SearchResults {
pub tracks: Option<Page<Track>>,
pub artists: Option<Page<Artist>>,
pub albums: Option<Page<SimplifiedAlbum>>,
pub playlists: Option<Page<SimplifiedPlaylist>>,
pub shows: Option<Page<SimplifiedShow>>,
pub episodes: Option<Page<SimplifiedEpisode>>,
pub audiobooks: Option<Page<SimplifiedAudiobook>>,
}
#[derive(Clone, Debug)]
pub enum Item {
Album,
Artist,
Playlist,
Track,
Show,
Episode,
Audiobook,
}
impl Item {
pub fn all() -> &'static [Self; 7] {
&[
Self::Album,
Self::Artist,
Self::Playlist,
Self::Track,
Self::Show,
Self::Episode,
Self::Audiobook,
]
}
}
impl FromStr for Item {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_ref() {
"album" => Ok(Self::Album),
"artist" => Ok(Self::Artist),
"playlist" => Ok(Self::Playlist),
"track" => Ok(Self::Track),
"show" => Ok(Self::Show),
"episode" => Ok(Self::Episode),
"audiobook" => Ok(Self::Audiobook),
_ => Err(crate::Error::Parse { description: format!("Failed to parse search item: {s} is not a valid item. Expected one of: *album, artist, playlist, track, show, episode, audiobook*.") }),
}
}
}
impl AsRef<str> for Item {
fn as_ref(&self) -> &str {
match self {
Item::Album => "album",
Item::Artist => "artist",
Item::Playlist => "playlist",
Item::Track => "track",
Item::Show => "show",
Item::Episode => "episode",
Item::Audiobook => "audiobook",
}
}
}