use crate::api::{LovedTracksClient, RecentTracksClient, TopTracksClient};
use crate::client::{
HttpClient, RateLimitedClient, RateLimiter, ReqwestClient, RetryClient, RetryPolicy,
};
use crate::config::{Config, ConfigBuilder};
use crate::error::Result;
use std::sync::Arc;
pub struct LastFmClient {
config: Arc<Config>,
recent_tracks_client: RecentTracksClient,
loved_tracks_client: LovedTracksClient,
top_tracks_client: TopTracksClient,
}
impl LastFmClient {
#[must_use]
pub fn builder() -> ConfigBuilder {
ConfigBuilder::new()
}
pub fn new() -> Result<Self> {
let config = ConfigBuilder::build_with_defaults()?;
Ok(Self::from_config(config))
}
#[must_use]
pub fn from_config(config: Config) -> Self {
let base_client = ReqwestClient::new();
let http: Arc<dyn HttpClient> = if let Some(rate_limit_config) = config.rate_limit() {
let retry_policy = RetryPolicy::exponential(config.retry_attempts());
let retry_client = RetryClient::new(base_client, retry_policy);
let limiter = Arc::new(RateLimiter::new(
rate_limit_config.max_requests,
rate_limit_config.per_duration,
));
Arc::new(RateLimitedClient::new(retry_client, limiter))
} else {
let retry_policy = RetryPolicy::exponential(config.retry_attempts());
Arc::new(RetryClient::new(base_client, retry_policy))
};
let config = Arc::new(config);
let recent_tracks_client = RecentTracksClient::new(http.clone(), config.clone());
let loved_tracks_client = LovedTracksClient::new(http.clone(), config.clone());
let top_tracks_client = TopTracksClient::new(http, config.clone());
Self {
config,
recent_tracks_client,
loved_tracks_client,
top_tracks_client,
}
}
pub fn with_http(config: Config, http: Arc<dyn HttpClient>) -> Self {
let config = Arc::new(config);
let recent_tracks_client = RecentTracksClient::new(http.clone(), config.clone());
let loved_tracks_client = LovedTracksClient::new(http.clone(), config.clone());
let top_tracks_client = TopTracksClient::new(http, config.clone());
Self {
config,
recent_tracks_client,
loved_tracks_client,
top_tracks_client,
}
}
pub fn recent_tracks(
&self,
username: impl Into<String>,
) -> crate::api::RecentTracksRequestBuilder {
self.recent_tracks_client.builder(username)
}
pub fn loved_tracks(
&self,
username: impl Into<String>,
) -> crate::api::LovedTracksRequestBuilder {
self.loved_tracks_client.builder(username)
}
pub fn top_tracks(&self, username: impl Into<String>) -> crate::api::TopTracksRequestBuilder {
self.top_tracks_client.builder(username)
}
#[must_use]
pub fn config(&self) -> &Config {
&self.config
}
}
impl ConfigBuilder {
pub fn build_client(self) -> Result<LastFmClient> {
self.build().map(LastFmClient::from_config)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::MockClient;
#[test]
fn test_client_from_config() {
let config = ConfigBuilder::new().api_key("test_key").build().unwrap();
let client = LastFmClient::from_config(config);
assert_eq!(client.config().api_key(), "test_key");
}
#[test]
fn test_client_with_mock() {
let config = ConfigBuilder::new().api_key("test_key").build().unwrap();
let mock = MockClient::new();
let client = LastFmClient::with_http(config, Arc::new(mock));
assert_eq!(client.config().api_key(), "test_key");
}
#[test]
fn test_builder() {
let client = LastFmClient::builder()
.api_key("test_key")
.build()
.map(LastFmClient::from_config)
.unwrap();
assert_eq!(client.config().api_key(), "test_key");
}
}