ytmapi_rs/query/search/
filteredsearch.rs

1use super::{
2    AuthToken, PostMethod, PostQuery, Query, SEARCH_QUERY_PATH,
3    SPECIALIZED_PLAYLIST_EXACT_MATCH_PARAMS, SPECIALIZED_PLAYLIST_PREFIX_PARAMS,
4    SPECIALIZED_PLAYLIST_WITH_SUGGESTIONS_PARAMS, SearchQuery, SearchType, SpellingMode,
5    search_query_header,
6};
7use crate::parse::{
8    SearchResultAlbum, SearchResultArtist, SearchResultEpisode, SearchResultFeaturedPlaylist,
9    SearchResultPlaylist, SearchResultPodcast, SearchResultProfile, SearchResultSong,
10    SearchResultVideo,
11};
12use std::borrow::Cow;
13
14// TODO Seal
15// TODO: Add param bits
16// Implements Default to allow simple implementation of
17// Into<SearchQuery<FilteredSearch<F>>>
18pub trait FilteredSearchType: Default {
19    fn filtered_param_bits(&self) -> Cow<'_, str>;
20    // By implementing a default method, we can specialize for cases were these
21    // params are incorrect.
22    fn filtered_spelling_param(&self, spelling_mode: &SpellingMode) -> Cow<'_, str> {
23        match spelling_mode {
24            SpellingMode::ExactMatch => "AWoMEA4QChADEAQQCRAF".into(),
25            SpellingMode::WithSuggestions => "AUICCAFqDBAOEAoQAxAEEAkQBQ%3D%3D".into(),
26        }
27    }
28    // By implementing a default method, we can specialize for cases were these
29    // params are incorrect.
30    fn filtered_prefix_param(&self) -> Cow<'_, str> {
31        "EgWKAQ".into()
32    }
33}
34/// Helper struct for SearchQuery
35#[derive(Default, Debug, Clone, PartialEq)]
36pub struct FilteredSearch<F: FilteredSearchType> {
37    pub(crate) filter: F,
38}
39/// Helper struct for FilteredSearch type state pattern.
40#[derive(Default, PartialEq, Debug, Clone)]
41pub struct SongsFilter;
42/// Helper struct for FilteredSearch type state pattern.
43#[derive(Default, PartialEq, Debug, Clone)]
44pub struct VideosFilter;
45/// Helper struct for FilteredSearch type state pattern.
46#[derive(Default, PartialEq, Debug, Clone)]
47pub struct AlbumsFilter;
48/// Helper struct for FilteredSearch type state pattern.
49#[derive(Default, PartialEq, Debug, Clone)]
50pub struct ArtistsFilter;
51/// Helper struct for FilteredSearch type state pattern.
52#[derive(Default, PartialEq, Debug, Clone)]
53pub struct PlaylistsFilter;
54/// Helper struct for FilteredSearch type state pattern.
55#[derive(Default, PartialEq, Debug, Clone)]
56pub struct CommunityPlaylistsFilter;
57/// Helper struct for FilteredSearch type state pattern.
58#[derive(Default, PartialEq, Debug, Clone)]
59pub struct FeaturedPlaylistsFilter;
60/// Helper struct for FilteredSearch type state pattern.
61#[derive(Default, PartialEq, Debug, Clone)]
62pub struct EpisodesFilter;
63/// Helper struct for FilteredSearch type state pattern.
64#[derive(Default, PartialEq, Debug, Clone)]
65pub struct PodcastsFilter;
66/// Helper struct for FilteredSearch type state pattern.
67#[derive(Default, PartialEq, Debug, Clone)]
68pub struct ProfilesFilter;
69
70impl<F: FilteredSearchType> SearchType for FilteredSearch<F> {
71    fn specialised_params(&self, spelling_mode: &SpellingMode) -> Option<Cow<'_, str>> {
72        Some(
73            format!(
74                "{}{}{}",
75                self.filter.filtered_prefix_param(),
76                self.filter.filtered_param_bits(),
77                self.filter.filtered_spelling_param(spelling_mode),
78            )
79            .into(),
80        )
81    }
82}
83
84// Implementations of FilteredSearchType
85impl FilteredSearchType for SongsFilter {
86    fn filtered_param_bits(&self) -> Cow<'_, str> {
87        "II".into()
88    }
89}
90impl FilteredSearchType for VideosFilter {
91    fn filtered_param_bits(&self) -> Cow<'_, str> {
92        "IQ".into()
93    }
94}
95impl FilteredSearchType for AlbumsFilter {
96    fn filtered_param_bits(&self) -> Cow<'_, str> {
97        "IY".into()
98    }
99}
100impl FilteredSearchType for ArtistsFilter {
101    fn filtered_param_bits(&self) -> Cow<'_, str> {
102        "Ig".into()
103    }
104}
105impl FilteredSearchType for PlaylistsFilter {
106    fn filtered_param_bits(&self) -> Cow<'_, str> {
107        // When filtering for Library params should be "Io"...
108        "".into()
109    }
110    fn filtered_spelling_param(&self, spelling_mode: &SpellingMode) -> Cow<'_, str> {
111        match spelling_mode {
112            SpellingMode::ExactMatch => "MABCAggBagoQBBADEAkQBRAK",
113            SpellingMode::WithSuggestions => "MABqChAEEAMQCRAFEAo%3D",
114        }
115        .into()
116    }
117    fn filtered_prefix_param(&self) -> Cow<'_, str> {
118        "Eg-KAQwIABAAGAAgACgB".into()
119    }
120}
121impl FilteredSearchType for CommunityPlaylistsFilter {
122    fn filtered_param_bits(&self) -> Cow<'_, str> {
123        "EA".into()
124    }
125    fn filtered_spelling_param(&self, spelling_mode: &SpellingMode) -> Cow<'_, str> {
126        match spelling_mode {
127            SpellingMode::ExactMatch => SPECIALIZED_PLAYLIST_EXACT_MATCH_PARAMS,
128            SpellingMode::WithSuggestions => SPECIALIZED_PLAYLIST_WITH_SUGGESTIONS_PARAMS,
129        }
130        .into()
131    }
132    fn filtered_prefix_param(&self) -> Cow<'_, str> {
133        SPECIALIZED_PLAYLIST_PREFIX_PARAMS.into()
134    }
135}
136impl FilteredSearchType for FeaturedPlaylistsFilter {
137    fn filtered_param_bits(&self) -> Cow<'_, str> {
138        "Dg".into()
139    }
140    fn filtered_spelling_param(&self, spelling_mode: &SpellingMode) -> Cow<'_, str> {
141        match spelling_mode {
142            SpellingMode::ExactMatch => SPECIALIZED_PLAYLIST_EXACT_MATCH_PARAMS,
143            SpellingMode::WithSuggestions => SPECIALIZED_PLAYLIST_WITH_SUGGESTIONS_PARAMS,
144        }
145        .into()
146    }
147    fn filtered_prefix_param(&self) -> Cow<'_, str> {
148        SPECIALIZED_PLAYLIST_PREFIX_PARAMS.into()
149    }
150}
151impl FilteredSearchType for EpisodesFilter {
152    fn filtered_param_bits(&self) -> Cow<'_, str> {
153        "JI".into()
154    }
155}
156impl FilteredSearchType for PodcastsFilter {
157    fn filtered_param_bits(&self) -> Cow<'_, str> {
158        "JQ".into()
159    }
160}
161impl FilteredSearchType for ProfilesFilter {
162    fn filtered_param_bits(&self) -> Cow<'_, str> {
163        "JY".into()
164    }
165}
166// Implementations of Query
167impl<A: AuthToken> Query<A> for SearchQuery<'_, FilteredSearch<SongsFilter>> {
168    type Output = Vec<SearchResultSong>;
169    type Method = PostMethod;
170}
171impl PostQuery for SearchQuery<'_, FilteredSearch<SongsFilter>> {
172    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
173        search_query_header(self)
174    }
175    fn path(&self) -> &str {
176        SEARCH_QUERY_PATH
177    }
178    fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
179        vec![]
180    }
181}
182impl<A: AuthToken> Query<A> for SearchQuery<'_, FilteredSearch<PlaylistsFilter>> {
183    type Output = Vec<SearchResultPlaylist>;
184    type Method = PostMethod;
185}
186impl PostQuery for SearchQuery<'_, FilteredSearch<PlaylistsFilter>> {
187    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
188        search_query_header(self)
189    }
190    fn path(&self) -> &str {
191        SEARCH_QUERY_PATH
192    }
193    fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
194        vec![]
195    }
196}
197impl<A: AuthToken> Query<A> for SearchQuery<'_, FilteredSearch<CommunityPlaylistsFilter>> {
198    type Output = Vec<SearchResultPlaylist>;
199    type Method = PostMethod;
200}
201impl PostQuery for SearchQuery<'_, FilteredSearch<CommunityPlaylistsFilter>> {
202    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
203        search_query_header(self)
204    }
205    fn path(&self) -> &str {
206        SEARCH_QUERY_PATH
207    }
208    fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
209        vec![]
210    }
211}
212impl<A: AuthToken> Query<A> for SearchQuery<'_, FilteredSearch<AlbumsFilter>> {
213    type Output = Vec<SearchResultAlbum>;
214    type Method = PostMethod;
215}
216impl PostQuery for SearchQuery<'_, FilteredSearch<AlbumsFilter>> {
217    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
218        search_query_header(self)
219    }
220    fn path(&self) -> &str {
221        SEARCH_QUERY_PATH
222    }
223    fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
224        vec![]
225    }
226}
227impl<A: AuthToken> Query<A> for SearchQuery<'_, FilteredSearch<ArtistsFilter>> {
228    type Output = Vec<SearchResultArtist>;
229    type Method = PostMethod;
230}
231impl PostQuery for SearchQuery<'_, FilteredSearch<ArtistsFilter>> {
232    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
233        search_query_header(self)
234    }
235    fn path(&self) -> &str {
236        SEARCH_QUERY_PATH
237    }
238    fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
239        vec![]
240    }
241}
242impl<A: AuthToken> Query<A> for SearchQuery<'_, FilteredSearch<FeaturedPlaylistsFilter>> {
243    type Output = Vec<SearchResultFeaturedPlaylist>;
244    type Method = PostMethod;
245}
246impl PostQuery for SearchQuery<'_, FilteredSearch<FeaturedPlaylistsFilter>> {
247    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
248        search_query_header(self)
249    }
250    fn path(&self) -> &str {
251        SEARCH_QUERY_PATH
252    }
253    fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
254        vec![]
255    }
256}
257impl<A: AuthToken> Query<A> for SearchQuery<'_, FilteredSearch<EpisodesFilter>> {
258    type Output = Vec<SearchResultEpisode>;
259    type Method = PostMethod;
260}
261impl PostQuery for SearchQuery<'_, FilteredSearch<EpisodesFilter>> {
262    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
263        search_query_header(self)
264    }
265    fn path(&self) -> &str {
266        SEARCH_QUERY_PATH
267    }
268    fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
269        vec![]
270    }
271}
272impl<A: AuthToken> Query<A> for SearchQuery<'_, FilteredSearch<PodcastsFilter>> {
273    type Output = Vec<SearchResultPodcast>;
274    type Method = PostMethod;
275}
276impl PostQuery for SearchQuery<'_, FilteredSearch<PodcastsFilter>> {
277    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
278        search_query_header(self)
279    }
280    fn path(&self) -> &str {
281        SEARCH_QUERY_PATH
282    }
283    fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
284        vec![]
285    }
286}
287impl<A: AuthToken> Query<A> for SearchQuery<'_, FilteredSearch<VideosFilter>> {
288    type Output = Vec<SearchResultVideo>;
289    type Method = PostMethod;
290}
291impl PostQuery for SearchQuery<'_, FilteredSearch<VideosFilter>> {
292    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
293        search_query_header(self)
294    }
295    fn path(&self) -> &str {
296        SEARCH_QUERY_PATH
297    }
298    fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
299        vec![]
300    }
301}
302impl<A: AuthToken> Query<A> for SearchQuery<'_, FilteredSearch<ProfilesFilter>> {
303    type Output = Vec<SearchResultProfile>;
304    type Method = PostMethod;
305}
306impl PostQuery for SearchQuery<'_, FilteredSearch<ProfilesFilter>> {
307    fn header(&self) -> serde_json::Map<String, serde_json::Value> {
308        search_query_header(self)
309    }
310    fn path(&self) -> &str {
311        SEARCH_QUERY_PATH
312    }
313    fn params(&self) -> Vec<(&str, Cow<'_, str>)> {
314        vec![]
315    }
316}