lastfm_client/api/user/
loved_tracks.rs1use crate::client::HttpClient;
2use crate::config::Config;
3use crate::error::Result;
4use crate::types::{LovedTrack, Timestamped, TrackLimit, TrackList, UserLovedTracks};
5
6use serde::de::DeserializeOwned;
7use std::fmt;
8use std::sync::Arc;
9
10use crate::api::builder_ext::{FetchAndSave, FetchAndUpdate, LimitBuilder};
11use crate::api::constants::METHOD_LOVED_TRACKS;
12use crate::api::fetch_utils::{ProgressCallback, ResourceContainer, fetch};
13
14pub struct LovedTracksRequestBuilder {
16 http: Arc<dyn HttpClient>,
17 config: Arc<Config>,
18 username: String,
19 limit: Option<u32>,
20 progress_callback: Option<ProgressCallback>,
21}
22
23impl fmt::Debug for LovedTracksRequestBuilder {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 f.debug_struct("LovedTracksRequestBuilder")
26 .field("username", &self.username)
27 .field("limit", &self.limit)
28 .finish_non_exhaustive()
29 }
30}
31
32impl LovedTracksRequestBuilder {
33 pub(crate) fn new(http: Arc<dyn HttpClient>, config: Arc<Config>, username: String) -> Self {
34 Self {
35 http,
36 config,
37 username,
38 limit: None,
39 progress_callback: None,
40 }
41 }
42
43 #[must_use]
45 pub fn on_progress(mut self, callback: impl Fn(u32, u32) + Send + Sync + 'static) -> Self {
46 self.progress_callback = Some(Arc::new(callback));
47 self
48 }
49
50 #[cfg(feature = "progress")]
52 #[must_use]
53 pub fn with_progress(self) -> Self {
54 self.on_progress(crate::api::progress::make_progress_callback())
55 }
56
57 pub async fn fetch(self) -> Result<TrackList<LovedTrack>> {
62 let limit = self
63 .limit
64 .map_or(TrackLimit::Unlimited, TrackLimit::Limited);
65
66 self.fetch_tracks::<UserLovedTracks>(limit)
67 .await
68 .map(TrackList::from)
69 }
70
71 async fn fetch_tracks<T>(&self, limit: TrackLimit) -> Result<Vec<LovedTrack>>
72 where
73 T: DeserializeOwned + ResourceContainer<ItemType = LovedTrack>,
74 {
75 use crate::url_builder::QueryParams;
76
77 fetch::<LovedTrack, T>(
78 self.http.clone(),
79 self.config.clone(),
80 self.username.clone(),
81 METHOD_LOVED_TRACKS,
82 limit,
83 QueryParams::new(),
84 self.progress_callback.as_ref(),
85 )
86 .await
87 }
88}
89
90impl LimitBuilder for LovedTracksRequestBuilder {
91 fn limit_mut(&mut self) -> &mut Option<u32> {
92 &mut self.limit
93 }
94}
95
96impl FetchAndSave for LovedTracksRequestBuilder {
97 type Item = LovedTrack;
98
99 fn resource_label() -> &'static str {
100 "loved tracks"
101 }
102
103 fn latest_timestamp(items: &[Self::Item]) -> Option<u32> {
104 items.first().and_then(Timestamped::get_timestamp)
105 }
106
107 async fn do_fetch(self) -> crate::error::Result<Vec<Self::Item>> {
108 Ok(Vec::from(self.fetch().await?))
109 }
110}
111
112impl FetchAndUpdate for LovedTracksRequestBuilder {
113 type Item = LovedTrack;
114
115 async fn fetch_since(self, max_ts: Option<u32>) -> crate::error::Result<Vec<Self::Item>> {
120 let all_tracks: Vec<LovedTrack> = Vec::from(self.fetch().await?);
121 Ok(match max_ts {
122 Some(cutoff) => all_tracks
123 .into_iter()
124 .filter(|t| t.get_timestamp().is_some_and(|ts| ts > cutoff))
125 .collect(),
126 None => all_tracks,
127 })
128 }
129}
130
131impl ResourceContainer for UserLovedTracks {
132 type ItemType = LovedTrack;
133
134 fn total(&self) -> u32 {
135 self.lovedtracks.attr.total
136 }
137
138 fn items(self) -> Vec<Self::ItemType> {
139 self.lovedtracks.track
140 }
141}