1use std::{fmt::Display, str::FromStr};
2
3use serde::Deserialize;
4
5use super::{
6 album::SimplifiedAlbum,
7 artist::Artist,
8 audiobook::SimplifiedAudiobook,
9 playlist::SimplifiedPlaylist,
10 show::{SimplifiedEpisode, SimplifiedShow},
11 track::Track,
12 Page,
13};
14
15#[derive(Clone, Debug, Default)]
17pub struct SearchQuery {
18 query: String,
19 album: Option<String>,
20 artist: Option<String>,
21 track: Option<String>,
22 year: Option<String>,
23 irsc: Option<String>,
24 genre: Option<String>,
25 upc: Option<String>,
26 hipster: bool,
27 new: bool,
28}
29
30impl SearchQuery {
31 pub fn from_query(query: impl Into<String>) -> Self {
32 Self {
33 query: query.into(),
34 ..Default::default()
35 }
36 }
37
38 pub fn album(mut self, album: impl Into<String>) -> Self {
39 self.album = Some(album.into());
40 self
41 }
42
43 pub fn artist(mut self, artist: impl Into<String>) -> Self {
44 self.artist = Some(artist.into());
45 self
46 }
47
48 pub fn track(mut self, track: impl Into<String>) -> Self {
49 self.track = Some(track.into());
50 self
51 }
52
53 pub fn year(mut self, year: u32) -> Self {
54 self.year = Some(year.to_string());
55 self
56 }
57
58 pub fn years(mut self, start_year: u32, end_year: u32) -> Self {
59 self.year = Some(format!("{start_year}-{end_year}"));
60 self
61 }
62
63 pub fn irsc(mut self, irsc: impl Into<String>) -> Self {
64 self.irsc = Some(irsc.into());
65 self
66 }
67
68 pub fn genre(mut self, genre: impl Into<String>) -> Self {
69 self.genre = Some(genre.into());
70 self
71 }
72
73 pub fn upc(mut self, upc: impl Into<String>) -> Self {
74 self.upc = Some(upc.into());
75 self
76 }
77
78 pub fn hipster(mut self, hipster: bool) -> Self {
79 self.hipster = hipster;
80 self
81 }
82
83 pub fn new(mut self, new: bool) -> Self {
84 self.new = new;
85 self
86 }
87}
88
89impl<T> From<T> for SearchQuery
90where
91 T: Into<String>,
92{
93 fn from(value: T) -> Self {
94 Self::from_query(value)
95 }
96}
97
98impl Display for SearchQuery {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 write!(f, "{}", self.query)?;
102
103 if let Some(album) = &self.album {
104 write!(f, " album:{album}")?;
105 }
106
107 if let Some(artist) = &self.artist {
108 write!(f, " artist:{artist}")?;
109 }
110
111 if let Some(track) = &self.track {
112 write!(f, " track:{track}")?;
113 }
114
115 if let Some(year) = &self.year {
116 write!(f, " year:{year}")?;
117 }
118
119 if let Some(irsc) = &self.irsc {
120 write!(f, " irsc:{irsc}")?;
121 }
122
123 if let Some(genre) = &self.genre {
124 write!(f, " genre:{genre}")?;
125 }
126
127 if let Some(upc) = &self.upc {
128 write!(f, " upc:{upc}")?;
129 }
130
131 if self.hipster {
132 write!(f, " tag:hipster")?;
133 }
134
135 if self.new {
136 write!(f, " tag:new")?;
137 }
138
139 Ok(())
140 }
141}
142
143#[derive(Clone, Debug, Deserialize, PartialEq)]
148pub struct SearchResults {
149 pub tracks: Option<Page<Track>>,
151 pub artists: Option<Page<Artist>>,
153 pub albums: Option<Page<SimplifiedAlbum>>,
155 pub playlists: Option<Page<SimplifiedPlaylist>>,
157 pub shows: Option<Page<SimplifiedShow>>,
159 pub episodes: Option<Page<SimplifiedEpisode>>,
161 pub audiobooks: Option<Page<SimplifiedAudiobook>>,
163}
164
165#[derive(Clone, Debug)]
170pub enum Item {
171 Album,
173 Artist,
175 Playlist,
177 Track,
179 Show,
181 Episode,
183 Audiobook,
185}
186
187impl Item {
188 pub fn all() -> &'static [Self; 7] {
190 &[
191 Self::Album,
192 Self::Artist,
193 Self::Playlist,
194 Self::Track,
195 Self::Show,
196 Self::Episode,
197 Self::Audiobook,
198 ]
199 }
200}
201
202impl FromStr for Item {
203 type Err = crate::Error;
204
205 fn from_str(s: &str) -> Result<Self, Self::Err> {
206 match s.to_lowercase().as_ref() {
207 "album" => Ok(Self::Album),
208 "artist" => Ok(Self::Artist),
209 "playlist" => Ok(Self::Playlist),
210 "track" => Ok(Self::Track),
211 "show" => Ok(Self::Show),
212 "episode" => Ok(Self::Episode),
213 "audiobook" => Ok(Self::Audiobook),
214 _ => 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*.") }),
215 }
216 }
217}
218
219impl AsRef<str> for Item {
220 fn as_ref(&self) -> &str {
221 match self {
222 Item::Album => "album",
223 Item::Artist => "artist",
224 Item::Playlist => "playlist",
225 Item::Track => "track",
226 Item::Show => "show",
227 Item::Episode => "episode",
228 Item::Audiobook => "audiobook",
229 }
230 }
231}