use crate::analytics::AnalysisHandler;
use crate::client::HttpClient;
use crate::config::Config;
use crate::error::Result;
use crate::file_handler::{FileFormat, FileHandler};
use crate::types::{LovedTrack, TrackLimit, UserLovedTracks};
use serde::de::DeserializeOwned;
use std::sync::Arc;
use super::fetch_utils::{TrackContainer, fetch_tracks};
pub struct LovedTracksClient {
http: Arc<dyn HttpClient>,
config: Arc<Config>,
}
impl LovedTracksClient {
pub fn new(http: Arc<dyn HttpClient>, config: Arc<Config>) -> Self {
Self { http, config }
}
pub fn builder(&self, username: impl Into<String>) -> LovedTracksRequestBuilder {
LovedTracksRequestBuilder::new(self.http.clone(), self.config.clone(), username.into())
}
}
pub struct LovedTracksRequestBuilder {
http: Arc<dyn HttpClient>,
config: Arc<Config>,
username: String,
limit: Option<u32>,
}
impl LovedTracksRequestBuilder {
fn new(http: Arc<dyn HttpClient>, config: Arc<Config>, username: String) -> Self {
Self {
http,
config,
username,
limit: None,
}
}
#[must_use]
pub fn limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
#[must_use]
pub fn unlimited(mut self) -> Self {
self.limit = None;
self
}
pub async fn fetch(self) -> Result<Vec<LovedTrack>> {
let limit = self
.limit
.map_or(TrackLimit::Unlimited, TrackLimit::Limited);
self.fetch_tracks::<UserLovedTracks>(limit).await
}
pub async fn fetch_and_save(self, format: FileFormat, filename_prefix: &str) -> Result<String> {
let tracks = self.fetch().await?;
tracing::info!("Saving {} loved tracks to file", tracks.len());
let filename = FileHandler::save(&tracks, &format, filename_prefix)
.map_err(crate::error::LastFmError::Io)?;
Ok(filename)
}
pub async fn analyze(self, threshold: usize) -> Result<crate::analytics::TrackStats> {
let tracks = self.fetch().await?;
Ok(AnalysisHandler::analyze_tracks(&tracks, threshold))
}
pub async fn analyze_and_print(self, threshold: usize) -> Result<()> {
let stats = self.analyze(threshold).await?;
AnalysisHandler::print_analysis(&stats);
Ok(())
}
async fn fetch_tracks<T>(&self, limit: TrackLimit) -> Result<Vec<LovedTrack>>
where
T: DeserializeOwned + TrackContainer<TrackType = LovedTrack>,
{
use crate::url_builder::QueryParams;
fetch_tracks::<LovedTrack, T>(
self.http.clone(),
self.config.clone(),
self.username.clone(),
"user.getlovedtracks",
limit,
QueryParams::new(),
)
.await
}
}
impl TrackContainer for UserLovedTracks {
type TrackType = LovedTrack;
fn total_tracks(&self) -> u32 {
self.lovedtracks.attr.total
}
fn tracks(self) -> Vec<Self::TrackType> {
self.lovedtracks.track
}
}