use std::sync::Arc;
use std::time::Duration;
use serde::Serialize;
use serde::de::DeserializeOwned;
use serde_json::Value;
use crate::error::{Error, Result};
use crate::http::{DEFAULT_BASE_URL, Shared, Transport};
use crate::resources::{
ActorApi, AlbumApi, ApikeyApi, ArtistApi, ChartsApi, DropboxApi, FeedApi, GoogleDriveApi,
GraphApi, LikeApi, MirrorApi, PlayerApi, PlaylistApi, ScrobbleApi, ShoutApi, SongApi,
SpotifyApi, StatsApi,
};
pub fn default_user_agent() -> String {
format!("rocksky-rust/{}", env!("CARGO_PKG_VERSION"))
}
#[derive(Debug, Clone)]
pub struct Client {
transport: Shared,
}
impl Default for Client {
fn default() -> Self {
Self::new()
}
}
impl Client {
pub fn new() -> Self {
ClientBuilder::new().build()
}
pub fn builder() -> ClientBuilder {
ClientBuilder::new()
}
pub fn base_url(&self) -> &str {
&self.transport.base_url
}
pub async fn token(&self) -> Option<String> {
self.transport.current_token().await
}
pub async fn set_token(&self, token: impl Into<Option<String>>) {
self.transport.set_token(token.into()).await;
}
pub fn actor(&self) -> ActorApi<'_> {
ActorApi::new(self)
}
pub fn album(&self) -> AlbumApi<'_> {
AlbumApi::new(self)
}
pub fn apikey(&self) -> ApikeyApi<'_> {
ApikeyApi::new(self)
}
pub fn artist(&self) -> ArtistApi<'_> {
ArtistApi::new(self)
}
pub fn charts(&self) -> ChartsApi<'_> {
ChartsApi::new(self)
}
pub fn dropbox(&self) -> DropboxApi<'_> {
DropboxApi::new(self)
}
pub fn feed(&self) -> FeedApi<'_> {
FeedApi::new(self)
}
pub fn googledrive(&self) -> GoogleDriveApi<'_> {
GoogleDriveApi::new(self)
}
pub fn graph(&self) -> GraphApi<'_> {
GraphApi::new(self)
}
pub fn like(&self) -> LikeApi<'_> {
LikeApi::new(self)
}
pub fn mirror(&self) -> MirrorApi<'_> {
MirrorApi::new(self)
}
pub fn player(&self) -> PlayerApi<'_> {
PlayerApi::new(self)
}
pub fn playlist(&self) -> PlaylistApi<'_> {
PlaylistApi::new(self)
}
pub fn scrobble(&self) -> ScrobbleApi<'_> {
ScrobbleApi::new(self)
}
pub fn shout(&self) -> ShoutApi<'_> {
ShoutApi::new(self)
}
pub fn song(&self) -> SongApi<'_> {
SongApi::new(self)
}
pub fn spotify(&self) -> SpotifyApi<'_> {
SpotifyApi::new(self)
}
pub fn stats(&self) -> StatsApi<'_> {
StatsApi::new(self)
}
pub async fn call(&self, method: &str) -> Result<Value> {
self.transport.query(method, &(), false).await
}
pub async fn call_with<P: Serialize + ?Sized>(
&self,
method: &str,
params: &P,
auth: bool,
) -> Result<Value> {
self.transport.query(method, params, auth).await
}
pub async fn procedure<P: Serialize + ?Sized, B: Serialize + ?Sized>(
&self,
method: &str,
params: Option<&P>,
body: Option<&B>,
auth: bool,
) -> Result<Value> {
self.transport.procedure(method, params, body, auth).await
}
pub(crate) async fn query_as<T, P>(&self, method: &str, params: &P, auth: bool) -> Result<T>
where
T: DeserializeOwned,
P: Serialize + ?Sized,
{
self.transport.query_as(method, params, auth).await
}
pub(crate) async fn procedure_as<T, P, B>(
&self,
method: &str,
params: Option<&P>,
body: Option<&B>,
auth: bool,
) -> Result<T>
where
T: DeserializeOwned,
P: Serialize + ?Sized,
B: Serialize + ?Sized,
{
self.transport.procedure_as(method, params, body, auth).await
}
}
#[derive(Debug, Default)]
pub struct ClientBuilder {
base_url: Option<String>,
token: Option<String>,
user_agent: Option<String>,
timeout: Option<Duration>,
http: Option<reqwest::Client>,
}
impl ClientBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = Some(url.into());
self
}
pub fn token(mut self, token: impl Into<String>) -> Self {
self.token = Some(token.into());
self
}
pub fn user_agent(mut self, ua: impl Into<String>) -> Self {
self.user_agent = Some(ua.into());
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn http_client(mut self, http: reqwest::Client) -> Self {
self.http = Some(http);
self
}
pub fn build(self) -> Client {
let http = if let Some(http) = self.http {
http
} else {
let mut builder = reqwest::Client::builder();
if let Some(t) = self.timeout {
builder = builder.timeout(t);
}
builder.build().unwrap_or_else(|_| reqwest::Client::new())
};
let transport = Transport::new(
http,
self.base_url.unwrap_or_else(|| DEFAULT_BASE_URL.to_string()),
self.token,
self.user_agent.unwrap_or_else(default_user_agent),
);
Client {
transport: Arc::new(transport),
}
}
pub fn try_build(self) -> Result<Client> {
if let Some(url) = self.base_url.as_deref() {
url::Url::parse(url).map_err(|e| Error::InvalidConfig(format!("base_url: {e}")))?;
}
Ok(self.build())
}
}