use chrono::{DateTime, Utc};
use serde::Serialize;
use crate::client::Client;
use crate::error::Result;
use crate::models::{
AlbumBasic, AlbumsEnvelope, ArtistBasic, ArtistsEnvelope, Compatibility, Neighbour,
NeighboursEnvelope, PlaylistBasic, PlaylistsEnvelope, Profile, Scrobble, ScrobblesEnvelope,
SongBasic, SongsEnvelope,
};
#[derive(Debug)]
pub struct ActorApi<'a> {
client: &'a Client,
}
impl<'a> ActorApi<'a> {
pub(crate) fn new(client: &'a Client) -> Self {
Self { client }
}
pub async fn get_profile(&self, did_or_handle: impl Into<String>) -> Result<Profile> {
#[derive(Serialize)]
struct Params {
did: String,
}
self.client
.query_as(
"app.rocksky.actor.getProfile",
&Params {
did: did_or_handle.into(),
},
false,
)
.await
}
pub async fn get_profile_me(&self) -> Result<Profile> {
self.client
.query_as("app.rocksky.actor.getProfile", &(), true)
.await
}
pub fn get_albums(&self, did: impl Into<String>) -> GetActorAlbums<'_> {
GetActorAlbums {
client: self.client,
params: ActorRangeParams {
did: did.into(),
limit: None,
offset: None,
start_date: None,
end_date: None,
},
}
}
pub fn get_artists(&self, did: impl Into<String>) -> GetActorArtists<'_> {
GetActorArtists {
client: self.client,
params: ActorRangeParams {
did: did.into(),
limit: None,
offset: None,
start_date: None,
end_date: None,
},
}
}
pub fn get_songs(&self, did: impl Into<String>) -> GetActorSongs<'_> {
GetActorSongs {
client: self.client,
params: ActorRangeParams {
did: did.into(),
limit: None,
offset: None,
start_date: None,
end_date: None,
},
}
}
pub fn get_scrobbles(&self, did: impl Into<String>) -> GetActorScrobbles<'_> {
GetActorScrobbles {
client: self.client,
params: ActorPageParams {
did: did.into(),
limit: None,
offset: None,
},
}
}
pub fn get_loved_songs(&self, did: impl Into<String>) -> GetActorLovedSongs<'_> {
GetActorLovedSongs {
client: self.client,
params: ActorPageParams {
did: did.into(),
limit: None,
offset: None,
},
}
}
pub fn get_playlists(&self, did: impl Into<String>) -> GetActorPlaylists<'_> {
GetActorPlaylists {
client: self.client,
params: ActorPageParams {
did: did.into(),
limit: None,
offset: None,
},
}
}
pub async fn get_neighbours(&self, did: impl Into<String>) -> Result<Vec<Neighbour>> {
#[derive(Serialize)]
struct P {
did: String,
}
let env: NeighboursEnvelope = self
.client
.query_as(
"app.rocksky.actor.getActorNeighbours",
&P { did: did.into() },
false,
)
.await?;
Ok(env.neighbours)
}
pub async fn get_compatibility(&self, did: impl Into<String>) -> Result<Compatibility> {
#[derive(Serialize)]
struct P {
did: String,
}
self.client
.query_as(
"app.rocksky.actor.getActorCompatibility",
&P { did: did.into() },
true,
)
.await
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
struct ActorRangeParams {
did: String,
#[serde(skip_serializing_if = "Option::is_none")]
limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
offset: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
start_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
end_date: Option<String>,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
struct ActorPageParams {
did: String,
#[serde(skip_serializing_if = "Option::is_none")]
limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
offset: Option<u32>,
}
macro_rules! range_builder {
($name:ident, $envelope:ty, $field:ident, $item:ty, $method:expr) => {
#[doc = concat!("Builder for [`", stringify!($method), "`].")]
#[derive(Debug)]
pub struct $name<'a> {
client: &'a Client,
params: ActorRangeParams,
}
impl<'a> $name<'a> {
pub fn limit(mut self, limit: u32) -> Self {
self.params.limit = Some(limit);
self
}
pub fn offset(mut self, offset: u32) -> Self {
self.params.offset = Some(offset);
self
}
pub fn start_date(mut self, when: DateTime<Utc>) -> Self {
self.params.start_date = Some(when.to_rfc3339());
self
}
pub fn end_date(mut self, when: DateTime<Utc>) -> Self {
self.params.end_date = Some(when.to_rfc3339());
self
}
pub async fn send(self) -> Result<Vec<$item>> {
let env: $envelope = self
.client
.query_as($method, &self.params, false)
.await?;
Ok(env.$field)
}
}
};
}
macro_rules! page_builder {
($name:ident, $envelope:ty, $field:ident, $item:ty, $method:expr) => {
#[doc = concat!("Builder for [`", stringify!($method), "`].")]
#[derive(Debug)]
pub struct $name<'a> {
client: &'a Client,
params: ActorPageParams,
}
impl<'a> $name<'a> {
pub fn limit(mut self, limit: u32) -> Self {
self.params.limit = Some(limit);
self
}
pub fn offset(mut self, offset: u32) -> Self {
self.params.offset = Some(offset);
self
}
pub async fn send(self) -> Result<Vec<$item>> {
let env: $envelope = self
.client
.query_as($method, &self.params, false)
.await?;
Ok(env.$field)
}
}
};
}
range_builder!(
GetActorAlbums,
AlbumsEnvelope,
albums,
AlbumBasic,
"app.rocksky.actor.getActorAlbums"
);
range_builder!(
GetActorArtists,
ArtistsEnvelope,
artists,
ArtistBasic,
"app.rocksky.actor.getActorArtists"
);
range_builder!(
GetActorSongs,
SongsEnvelope,
songs,
SongBasic,
"app.rocksky.actor.getActorSongs"
);
page_builder!(
GetActorScrobbles,
ScrobblesEnvelope,
scrobbles,
Scrobble,
"app.rocksky.actor.getActorScrobbles"
);
#[derive(Debug)]
pub struct GetActorLovedSongs<'a> {
client: &'a Client,
params: ActorPageParams,
}
impl<'a> GetActorLovedSongs<'a> {
pub fn limit(mut self, limit: u32) -> Self {
self.params.limit = Some(limit);
self
}
pub fn offset(mut self, offset: u32) -> Self {
self.params.offset = Some(offset);
self
}
pub async fn send(self) -> Result<Vec<SongBasic>> {
let env: crate::models::LovedSongsEnvelope = self
.client
.query_as("app.rocksky.actor.getActorLovedSongs", &self.params, false)
.await?;
Ok(env.loved_songs)
}
}
page_builder!(
GetActorPlaylists,
PlaylistsEnvelope,
playlists,
PlaylistBasic,
"app.rocksky.actor.getActorPlaylists"
);