lastfm_client/api/user/top/
tracks.rs1use crate::api::builder_ext::{FetchAndSave, LimitBuilder};
2use crate::api::constants::METHOD_TOP_TRACKS;
3use crate::api::fetch_utils::{Period, ProgressCallback, ResourceContainer, fetch};
4use crate::client::HttpClient;
5use crate::config::Config;
6use crate::error::Result;
7use crate::types::{TopTrack, TrackLimit, TrackList, UserTopTracks};
8use crate::url_builder::QueryParams;
9
10use serde::de::DeserializeOwned;
11use std::fmt;
12use std::sync::Arc;
13
14pub struct TopTracksRequestBuilder {
16 http: Arc<dyn HttpClient>,
17 config: Arc<Config>,
18 username: String,
19 limit: Option<u32>,
20 period: Option<Period>,
21 progress_callback: Option<ProgressCallback>,
22}
23
24impl fmt::Debug for TopTracksRequestBuilder {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 f.debug_struct("TopTracksRequestBuilder")
27 .field("username", &self.username)
28 .field("limit", &self.limit)
29 .field("period", &self.period)
30 .finish_non_exhaustive()
31 }
32}
33
34impl TopTracksRequestBuilder {
35 pub(crate) fn new(http: Arc<dyn HttpClient>, config: Arc<Config>, username: String) -> Self {
36 Self {
37 http,
38 config,
39 username,
40 limit: None,
41 period: None,
42 progress_callback: None,
43 }
44 }
45
46 #[must_use]
48 pub fn on_progress(mut self, callback: impl Fn(u32, u32) + Send + Sync + 'static) -> Self {
49 self.progress_callback = Some(Arc::new(callback));
50 self
51 }
52
53 #[cfg(feature = "progress")]
55 #[must_use]
56 pub fn with_progress(self) -> Self {
57 self.on_progress(crate::api::progress::make_progress_callback())
58 }
59
60 #[must_use]
78 pub const fn period(mut self, period: Period) -> Self {
79 self.period = Some(period);
80 self
81 }
82
83 pub async fn fetch(self) -> Result<TrackList<TopTrack>> {
88 let mut params = QueryParams::new();
89
90 if let Some(period) = self.period {
91 params.insert("period".to_string(), period.as_api_str().to_string());
92 }
93
94 let limit = self
95 .limit
96 .map_or(TrackLimit::Unlimited, TrackLimit::Limited);
97
98 self.fetch_tracks::<UserTopTracks>(limit, params)
99 .await
100 .map(TrackList::from)
101 }
102
103 async fn fetch_tracks<T>(
104 &self,
105 limit: TrackLimit,
106 additional_params: QueryParams,
107 ) -> Result<Vec<TopTrack>>
108 where
109 T: DeserializeOwned + ResourceContainer<ItemType = TopTrack>,
110 {
111 fetch::<TopTrack, T>(
112 self.http.clone(),
113 self.config.clone(),
114 self.username.clone(),
115 METHOD_TOP_TRACKS,
116 limit,
117 additional_params,
118 self.progress_callback.as_ref(),
119 )
120 .await
121 }
122}
123
124impl LimitBuilder for TopTracksRequestBuilder {
125 fn limit_mut(&mut self) -> &mut Option<u32> {
126 &mut self.limit
127 }
128}
129
130impl FetchAndSave for TopTracksRequestBuilder {
131 type Item = TopTrack;
132
133 fn resource_label() -> &'static str {
134 "top tracks"
135 }
136
137 async fn do_fetch(self) -> crate::error::Result<Vec<Self::Item>> {
138 Ok(Vec::from(self.fetch().await?))
139 }
140}
141
142impl ResourceContainer for UserTopTracks {
143 type ItemType = TopTrack;
144
145 fn total(&self) -> u32 {
146 self.toptracks.attr.total
147 }
148
149 fn items(self) -> Vec<Self::ItemType> {
150 self.toptracks.track
151 }
152}