1use serde::Serialize;
2use serde_json::json;
3
4use crate::{
5 auth::{AuthFlow, Authorised},
6 client::Body,
7 error::Result,
8 model::{
9 artist::{Artist, PagedArtists},
10 track::Track,
11 user::{PrivateUser, TimeRange, User},
12 CursorPage, Page,
13 },
14 query_list, Nil,
15};
16
17use super::{Client, Endpoint, EndpointPrivate};
18
19pub async fn get_current_user_profile(
20 spotify: &Client<impl AuthFlow + Authorised>,
21) -> Result<PrivateUser> {
22 spotify.get::<(), _>("/me".to_owned(), None).await
23}
24
25pub fn current_user_top_artists() -> UserTopItemsEndpoint<ArtistsMarker> {
26 UserTopItemsEndpoint::default()
27}
28
29pub fn current_user_top_tracks() -> UserTopItemsEndpoint<TracksMarker> {
30 UserTopItemsEndpoint::default()
31}
32
33pub async fn get_user(id: impl Into<String>, spotify: &Client<impl AuthFlow>) -> Result<User> {
34 spotify
35 .get::<(), _>(format!("/users/{}", id.into()), None)
36 .await
37}
38
39pub fn follow_playlist(id: impl Into<String>) -> FollowPlaylistEndpoint {
40 FollowPlaylistEndpoint {
41 id: id.into(),
42 public: None,
43 }
44}
45
46pub async fn unfollow_playlist(
47 id: impl Into<String>,
48 spotify: &Client<impl AuthFlow + Authorised>,
49) -> Result<Nil> {
50 spotify
51 .delete::<(), _>(format!("/playlists/{}/followers", id.into()), None)
52 .await
53}
54
55pub fn followed_artists() -> FollowedArtistsEndpoint {
56 FollowedArtistsEndpoint {
58 r#type: "artist".to_owned(),
59 ..Default::default()
60 }
61}
62
63pub async fn follow_artists<T: AsRef<str>>(
64 ids: &[T],
65 spotify: &Client<impl AuthFlow + Authorised>,
66) -> Result<Nil> {
67 let ids: Vec<String> = ids.iter().map(|i| i.as_ref().to_owned()).collect();
68
69 spotify
70 .put(
71 "/me/following?type=artist".to_owned(),
72 Body::Json(json!({ "ids": ids })),
73 )
74 .await
75}
76
77pub async fn unfollow_artists<T: AsRef<str>>(
78 ids: &[T],
79 spotify: &Client<impl AuthFlow + Authorised>,
80) -> Result<Nil> {
81 let ids: Vec<String> = ids.iter().map(|i| i.as_ref().to_owned()).collect();
82
83 spotify
84 .delete(
85 "/me/following?type=artist".to_owned(),
86 Body::Json(json!({ "ids": ids })),
87 )
88 .await
89}
90
91pub async fn check_if_user_follows_artists<T: AsRef<str>>(
92 ids: &[T],
93 spotify: &Client<impl AuthFlow + Authorised>,
94) -> Result<Vec<bool>> {
95 spotify
96 .get::<(), _>(
97 format!("/me/following/contains?type=artist&ids={}", query_list(ids)),
98 None,
99 )
100 .await
101}
102
103pub async fn follow_users<T: AsRef<str>>(
104 ids: &[T],
105 spotify: &Client<impl AuthFlow + Authorised>,
106) -> Result<Nil> {
107 let ids: Vec<String> = ids.iter().map(|i| i.as_ref().to_owned()).collect();
108
109 spotify
110 .put(
111 "/me/following?type=user".to_owned(),
112 Body::Json(json!({ "ids": ids })),
113 )
114 .await
115}
116
117pub async fn unfollow_users<T: AsRef<str>>(
118 ids: &[T],
119 spotify: &Client<impl AuthFlow + Authorised>,
120) -> Result<Nil> {
121 let ids: Vec<String> = ids.iter().map(|i| i.as_ref().to_owned()).collect();
122
123 spotify
124 .delete(
125 "/me/following?type=user".to_owned(),
126 Body::Json(json!({ "ids": ids })),
127 )
128 .await
129}
130
131pub async fn check_if_user_follows_users<T: AsRef<str>>(
132 ids: &[T],
133 spotify: &Client<impl AuthFlow + Authorised>,
134) -> Result<Vec<bool>> {
135 spotify
136 .get::<(), _>(
137 format!("/me/following/contains?type=user&ids={}", query_list(ids)),
138 None,
139 )
140 .await
141}
142
143pub async fn check_if_current_user_follow_playlist(
144 playlist_id: impl Into<String>,
145 spotify: &Client<impl AuthFlow + Authorised>,
146) -> Result<Vec<bool>> {
147 spotify
148 .get::<(), _>(
149 format!("/playlists/{}/followers/contains", playlist_id.into()),
150 None,
151 )
152 .await
153}
154
155pub trait ItemType: private::Sealed {}
156impl ItemType for ArtistsMarker {}
157impl ItemType for TracksMarker {}
158
159#[derive(Clone, Copy, Debug, Default)]
160pub struct ArtistsMarker;
161#[derive(Clone, Copy, Debug, Default)]
162pub struct TracksMarker;
163
164mod private {
165 pub trait Sealed {}
166
167 impl Sealed for super::ArtistsMarker {}
168 impl Sealed for super::TracksMarker {}
169}
170
171impl<I: ItemType> Endpoint for UserTopItemsEndpoint<I> {}
172impl Endpoint for FollowPlaylistEndpoint {}
173impl Endpoint for FollowedArtistsEndpoint {}
174
175#[derive(Clone, Debug, Default, Serialize)]
176pub struct UserTopItemsEndpoint<ItemType> {
177 pub(crate) time_range: Option<TimeRange>,
178 pub(crate) limit: Option<u32>,
179 pub(crate) offset: Option<u32>,
180 #[serde(skip)]
181 marker: std::marker::PhantomData<ItemType>,
182}
183
184impl<I: ItemType> UserTopItemsEndpoint<I> {
185 pub fn time_range(mut self, time_range: TimeRange) -> Self {
187 self.time_range = Some(time_range);
188 self
189 }
190
191 #[doc = include_str!("../docs/limit.md")]
192 pub fn limit(mut self, limit: u32) -> Self {
193 self.limit = Some(limit);
194 self
195 }
196
197 #[doc = include_str!("../docs/offset.md")]
198 pub fn offset(mut self, offset: u32) -> Self {
199 self.offset = Some(offset);
200 self
201 }
202}
203
204impl UserTopItemsEndpoint<ArtistsMarker> {
205 #[doc = include_str!("../docs/send.md")]
206 pub async fn get(self, spotify: &Client<impl AuthFlow + Authorised>) -> Result<Page<Artist>> {
207 spotify.get("/me/top/artists".to_owned(), self).await
208 }
209}
210
211impl UserTopItemsEndpoint<TracksMarker> {
212 #[doc = include_str!("../docs/send.md")]
213 pub async fn get(self, spotify: &Client<impl AuthFlow + Authorised>) -> Result<Page<Track>> {
214 spotify.get("/me/top/tracks".to_owned(), self).await
215 }
216}
217
218#[derive(Clone, Debug, Default, Serialize)]
219pub struct FollowPlaylistEndpoint {
220 #[serde(skip)]
221 pub(crate) id: String,
222 #[serde(skip_serializing_if = "Option::is_none")]
223 pub(crate) public: Option<bool>,
224}
225
226impl FollowPlaylistEndpoint {
227 pub fn public(mut self, public: bool) -> Self {
230 self.public = Some(public);
231 self
232 }
233
234 #[doc = include_str!("../docs/send.md")]
235 pub async fn send(self, spotify: &Client<impl AuthFlow + Authorised>) -> Result<Nil> {
236 spotify
237 .put(format!("/playlists/{}/followers", self.id), self.json())
238 .await
239 }
240}
241
242#[derive(Clone, Debug, Default, Serialize)]
243pub struct FollowedArtistsEndpoint {
244 pub(crate) r#type: String,
245 pub(crate) after: Option<String>,
246 pub(crate) limit: Option<u32>,
247}
248
249impl FollowedArtistsEndpoint {
250 pub fn after(mut self, artist_id: impl Into<String>) -> Self {
252 self.after = Some(artist_id.into());
253 self
254 }
255
256 #[doc = include_str!("../docs/limit.md")]
257 pub fn limit(mut self, limit: u32) -> Self {
258 self.limit = Some(limit);
259 self
260 }
261
262 #[doc = include_str!("../docs/send.md")]
263 pub async fn get(
264 self,
265 spotify: &Client<impl AuthFlow + Authorised>,
266 ) -> Result<CursorPage<Artist, Self>> {
267 spotify
268 .get("/me/following".to_owned(), self)
269 .await
270 .map(|a: PagedArtists| a.artists)
271 }
272}