use super::*;
use crate::{common::SearchSuggestion, parse::SearchResults};
pub use filteredsearch::*;
use std::borrow::Cow;
pub mod filteredsearch;
const SPECIALIZED_PLAYLIST_EXACT_MATCH_PARAMS: &str = "BagwQDhAKEAMQBBAJEAU%3D";
const SPECIALIZED_PLAYLIST_WITH_SUGGESTIONS_PARAMS: &str = "BQgIIAWoMEA4QChADEAQQCRAF";
const SPECIALIZED_PLAYLIST_PREFIX_PARAMS: &str = "EgeKAQQoA";
const SEARCH_QUERY_PATH: &str = "search";
pub trait SearchType: Default {
fn specialised_params(&self, spelling_mode: &SpellingMode) -> Option<Cow<str>>;
}
pub trait UnfilteredSearchType: SearchType {}
#[derive(PartialEq, Debug, Clone)]
pub struct SearchQuery<'a, S: SearchType> {
query: Cow<'a, str>,
spelling_mode: SpellingMode,
search_type: S,
}
#[derive(PartialEq, Debug, Clone, Default)]
pub enum SpellingMode {
#[default]
ExactMatch,
WithSuggestions,
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct BasicSearch;
#[derive(Default, Debug, Clone, PartialEq)]
pub struct LibrarySearch;
#[derive(Default, Debug, Clone, PartialEq)]
pub struct UploadSearch;
impl SearchType for BasicSearch {
fn specialised_params(&self, spelling_mode: &SpellingMode) -> Option<Cow<str>> {
match spelling_mode {
SpellingMode::ExactMatch => Some("EhGKAQ4IARABGAEgASgAOAFAAUICCAE%3D".into()),
SpellingMode::WithSuggestions => None,
}
}
}
impl SearchType for UploadSearch {
fn specialised_params(&self, _: &SpellingMode) -> Option<Cow<str>> {
Some("agIYAw%3D%3D".into())
}
}
impl SearchType for LibrarySearch {
fn specialised_params(&self, _: &SpellingMode) -> Option<Cow<str>> {
Some("agIYBA%3D%3D".into())
}
}
impl UnfilteredSearchType for BasicSearch {}
impl UnfilteredSearchType for UploadSearch {}
impl UnfilteredSearchType for LibrarySearch {}
impl<'a, S: UnfilteredSearchType, A: AuthToken> Query<A> for SearchQuery<'a, S> {
type Output = SearchResults;
type Method = PostMethod;
}
impl<'a, S: UnfilteredSearchType> PostQuery for SearchQuery<'a, S> {
fn header(&self) -> serde_json::Map<String, serde_json::Value> {
search_query_header(self)
}
fn path(&self) -> &str {
SEARCH_QUERY_PATH
}
fn params(&self) -> Option<Cow<str>> {
search_query_params(self)
}
}
impl<'a, Q: Into<Cow<'a, str>>, S: SearchType> From<Q> for SearchQuery<'a, S> {
fn from(value: Q) -> SearchQuery<'a, S> {
SearchQuery {
query: value.into(),
spelling_mode: SpellingMode::default(),
search_type: S::default(),
}
}
}
impl<'a> SearchQuery<'a, BasicSearch> {
pub fn new<Q: Into<Cow<'a, str>>>(q: Q) -> SearchQuery<'a, BasicSearch> {
SearchQuery {
query: q.into(),
spelling_mode: SpellingMode::default(),
search_type: BasicSearch {},
}
}
}
impl<'a, S: SearchType> SearchQuery<'a, S> {
pub fn with_spelling_mode(mut self, spelling_mode: SpellingMode) -> Self {
self.spelling_mode = spelling_mode;
self
}
pub fn with_query<Q: Into<Cow<'a, str>>>(mut self, query: Q) -> Self {
self.query = query.into();
self
}
}
impl<'a> SearchQuery<'a, BasicSearch> {
pub fn with_filter<F: FilteredSearchType>(
self,
filter: F,
) -> SearchQuery<'a, FilteredSearch<F>> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
search_type: FilteredSearch { filter },
}
}
pub fn uploads(self) -> SearchQuery<'a, UploadSearch> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
search_type: UploadSearch,
}
}
pub fn library(self) -> SearchQuery<'a, LibrarySearch> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
search_type: LibrarySearch,
}
}
}
impl<'a, F: FilteredSearchType> SearchQuery<'a, FilteredSearch<F>> {
pub fn with_filter<F2: FilteredSearchType>(
self,
filter: F2,
) -> SearchQuery<'a, FilteredSearch<F2>> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
search_type: FilteredSearch { filter },
}
}
pub fn unfiltered(self) -> SearchQuery<'a, BasicSearch> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
search_type: BasicSearch,
}
}
}
impl<'a> SearchQuery<'a, UploadSearch> {
pub fn with_scope_public(self) -> SearchQuery<'a, BasicSearch> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
search_type: BasicSearch,
}
}
}
impl<'a> SearchQuery<'a, LibrarySearch> {
pub fn with_scope_public(self) -> SearchQuery<'a, BasicSearch> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
search_type: BasicSearch,
}
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct GetSearchSuggestionsQuery<'a> {
query: Cow<'a, str>,
}
impl<'a> GetSearchSuggestionsQuery<'a> {
pub fn new<S: Into<Cow<'a, str>>>(value: S) -> GetSearchSuggestionsQuery<'a> {
GetSearchSuggestionsQuery {
query: value.into(),
}
}
}
impl<'a, S: Into<Cow<'a, str>>> From<S> for GetSearchSuggestionsQuery<'a> {
fn from(value: S) -> GetSearchSuggestionsQuery<'a> {
GetSearchSuggestionsQuery::new(value)
}
}
impl<'a, A: AuthToken> Query<A> for GetSearchSuggestionsQuery<'a> {
type Output = Vec<SearchSuggestion>;
type Method = PostMethod;
}
impl<'a> PostQuery for GetSearchSuggestionsQuery<'a> {
fn header(&self) -> serde_json::Map<String, serde_json::Value> {
let value = self.query.as_ref().into();
serde_json::Map::from_iter([("input".into(), value)])
}
fn path(&self) -> &str {
"music/get_search_suggestions"
}
fn params(&self) -> Option<Cow<str>> {
None
}
}
fn search_query_header<S: SearchType>(
query: &SearchQuery<S>,
) -> serde_json::Map<String, serde_json::Value> {
let value = query.query.as_ref().into();
serde_json::Map::from_iter([("query".to_string(), value)])
}
fn search_query_params<'a, S: SearchType>(query: &'a SearchQuery<'a, S>) -> Option<Cow<str>> {
query.search_type.specialised_params(&query.spelling_mode)
}