ytmapi_rs/query/search/
filteredsearch.rs

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