use std::future::{Future, IntoFuture};
use std::pin::Pin;
use reqwest::Method;
use serde_json::{Map, Value};
use crate::client::Client;
use crate::dispatch::{decode_json, message_path};
use crate::error::{Error, Result};
use crate::types::{ReactionSummary, ReactionType, RoomName, Serial};
#[derive(Clone, Debug)]
pub struct Reactions {
pub(crate) client: Client,
pub(crate) room: RoomName,
}
impl Reactions {
pub(crate) fn new(client: Client, room: RoomName) -> Self {
Self { client, room }
}
pub fn send(&self, serial: impl Into<Serial>, name: impl Into<String>) -> SendReaction {
SendReaction {
client: self.client.clone(),
room: self.room.clone(),
serial: serial.into(),
name: name.into(),
kind: ReactionType::Distinct,
count: None,
}
}
pub fn delete(&self, serial: impl Into<Serial>) -> DeleteReaction {
DeleteReaction {
client: self.client.clone(),
room: self.room.clone(),
serial: serial.into(),
kind: ReactionType::Distinct,
name: None,
}
}
pub fn for_client(&self, serial: impl Into<Serial>) -> ClientReactions {
ClientReactions {
client: self.client.clone(),
room: self.room.clone(),
serial: serial.into(),
client_id: None,
}
}
}
#[derive(Clone, Debug)]
pub struct SendReaction {
client: Client,
room: RoomName,
serial: Serial,
name: String,
kind: ReactionType,
count: Option<u64>,
}
impl SendReaction {
pub fn kind(mut self, kind: ReactionType) -> Self {
self.kind = kind;
self
}
pub fn count(mut self, count: u64) -> Self {
self.count = Some(count);
self
}
}
impl IntoFuture for SendReaction {
type Output = Result<()>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let mut obj = Map::new();
obj.insert("type".to_owned(), Value::String(self.kind.into()));
obj.insert("name".to_owned(), Value::String(self.name));
if let Some(count) = self.count {
obj.insert("count".to_owned(), Value::from(count));
}
self.client
.inner
.send(
Method::POST,
&message_path(self.room.as_str(), self.serial.as_str(), "/reactions"),
&[],
Some(Value::Object(obj)),
false,
)
.await?;
Ok(())
})
}
}
#[derive(Clone, Debug)]
pub struct DeleteReaction {
client: Client,
room: RoomName,
serial: Serial,
kind: ReactionType,
name: Option<String>,
}
impl DeleteReaction {
pub fn kind(mut self, kind: ReactionType) -> Self {
self.kind = kind;
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
impl IntoFuture for DeleteReaction {
type Output = Result<()>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let name_required =
matches!(self.kind, ReactionType::Distinct | ReactionType::Multiple);
if name_required && self.name.is_none() {
return Err(Error::InvalidRequest(format!(
"reaction name is required to delete a `{}` reaction",
String::from(self.kind)
)));
}
let mut query: Vec<(&str, String)> = vec![("type", self.kind.into())];
if let Some(name) = self.name {
query.push(("name", name));
}
self.client
.inner
.send(
Method::DELETE,
&message_path(self.room.as_str(), self.serial.as_str(), "/reactions"),
&query,
None,
false,
)
.await?;
Ok(())
})
}
}
#[derive(Clone, Debug)]
pub struct ClientReactions {
client: Client,
room: RoomName,
serial: Serial,
client_id: Option<String>,
}
impl ClientReactions {
pub fn client_id(mut self, client_id: impl Into<String>) -> Self {
self.client_id = Some(client_id.into());
self
}
}
impl IntoFuture for ClientReactions {
type Output = Result<ReactionSummary>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let mut query: Vec<(&str, String)> = Vec::new();
if let Some(cid) = self.client_id {
query.push(("forClientId", cid));
}
let resp = self
.client
.inner
.send(
Method::GET,
&message_path(
self.room.as_str(),
self.serial.as_str(),
"/client-reactions",
),
&query,
None,
false,
)
.await?;
decode_json(&resp.body)
})
}
}