mod private {
use serde::Deserialize;
use crate::model::{
page::PageObject,
search::{AlbumObject, ArtistObject},
track::TrackObject,
};
#[derive(Debug, Deserialize)]
pub struct SearchResultsObject {
pub tracks: Option<PageObject<TrackObject>>,
pub artists: Option<PageObject<ArtistObject>>,
pub albums: Option<PageObject<AlbumObject>>,
}
}
use std::{convert::Infallible, marker::PhantomData};
use serde::Deserialize;
pub(crate) use self::private::SearchResultsObject;
use super::{
album::{AlbumObject, FullAlbum},
artist::{ArtistObject, FullArtist},
page::{Page, PageInformation, PageObject},
track::{FullTrack, TrackObject},
ItemType,
};
pub const DEFAULT_SEARCH_TYPES_STRING: &str = "album,artist,playlist,track,show,episode";
pub const DEFAULT_SEARCH_LIMIT: u32 = 20;
pub const DEFAULT_SEARCH_OFFSET: u32 = 0;
pub trait ToTypesString: crate::private::Sealed {
fn to_types_string(self) -> String;
}
#[derive(Debug)]
pub struct SearchResults {
pub(crate) inner: SearchResultsObject,
}
#[derive(Debug, Deserialize)]
#[doc(hidden)]
pub struct TrackSearchResults {
tracks: PageObject<TrackObject>,
}
#[derive(Debug, Deserialize)]
#[doc(hidden)]
pub struct ArtistSearchResults {
artists: PageObject<ArtistObject>,
}
#[derive(Debug, Deserialize)]
#[doc(hidden)]
pub struct AlbumSearchResults {
albums: PageObject<AlbumObject>,
}
impl TryFrom<SearchResultsObject> for SearchResults {
type Error = Infallible;
fn try_from(value: SearchResultsObject) -> Result<Self, Self::Error> {
Ok(Self { inner: value })
}
}
impl SearchResults {
pub fn tracks(self) -> Option<Page<TrackSearchResults, FullTrack>> {
self.inner.tracks.and_then(|page| {
if !<PageObject<TrackObject> as PageInformation<FullTrack>>::items(&page).is_empty() {
Some(Page {
inner: TrackSearchResults { tracks: page },
phantom: PhantomData,
})
} else {
None
}
})
}
pub fn artists(self) -> Option<Page<ArtistSearchResults, FullArtist>> {
self.inner.artists.and_then(|page| {
if !<PageObject<ArtistObject> as PageInformation<FullArtist>>::items(&page).is_empty() {
Some(Page {
inner: ArtistSearchResults { artists: page },
phantom: PhantomData,
})
} else {
None
}
})
}
pub fn albums(self) -> Option<Page<AlbumSearchResults, FullAlbum>> {
self.inner.albums.and_then(|page| {
if !<PageObject<AlbumObject> as PageInformation<FullAlbum>>::items(&page).is_empty() {
Some(Page {
inner: AlbumSearchResults { albums: page },
phantom: PhantomData,
})
} else {
None
}
})
}
}
impl crate::private::Sealed for TrackSearchResults {}
impl crate::private::Sealed for ArtistSearchResults {}
impl crate::private::Sealed for AlbumSearchResults {}
impl PageInformation<FullTrack> for TrackSearchResults {
type Items = Vec<FullTrack>;
fn items(&self) -> Self::Items {
self.tracks.items()
}
fn take_items(self) -> Self::Items {
self.tracks.take_items()
}
fn next(self) -> Option<String> {
<PageObject<TrackObject> as PageInformation<FullTrack>>::next(self.tracks)
}
}
impl PageInformation<FullArtist> for ArtistSearchResults {
type Items = Vec<FullArtist>;
fn items(&self) -> Self::Items {
self.artists.items()
}
fn take_items(self) -> Self::Items {
self.artists.take_items()
}
fn next(self) -> Option<String> {
<PageObject<ArtistObject> as PageInformation<FullArtist>>::next(self.artists)
}
}
impl PageInformation<FullAlbum> for AlbumSearchResults {
type Items = Vec<FullAlbum>;
fn items(&self) -> Self::Items {
self.albums.items()
}
fn take_items(self) -> Self::Items {
self.albums.take_items()
}
fn next(self) -> Option<String> {
<PageObject<AlbumObject> as PageInformation<FullAlbum>>::next(self.albums)
}
}
impl<I> crate::private::Sealed for I where I: IntoIterator<Item = ItemType> {}
impl<I> ToTypesString for I
where
I: IntoIterator<Item = ItemType> + crate::private::Sealed,
{
fn to_types_string(self) -> String {
self.into_iter()
.map(|ty| ty.to_string())
.collect::<Vec<String>>()
.join(",")
}
}