1use std::future::{Future, IntoFuture};
4use std::pin::Pin;
5
6use reqwest::Method;
7use serde_json::{Map, Value};
8
9use crate::client::Client;
10use crate::dispatch::{decode_json, message_path};
11use crate::error::{Error, Result};
12use crate::types::{ReactionSummary, ReactionType, RoomName, Serial};
13
14#[derive(Clone, Debug)]
18pub struct Reactions {
19 pub(crate) client: Client,
20 pub(crate) room: RoomName,
21}
22
23impl Reactions {
24 pub(crate) fn new(client: Client, room: RoomName) -> Self {
25 Self { client, room }
26 }
27
28 pub fn send(&self, serial: impl Into<Serial>, name: impl Into<String>) -> SendReaction {
36 SendReaction {
37 client: self.client.clone(),
38 room: self.room.clone(),
39 serial: serial.into(),
40 name: name.into(),
41 kind: ReactionType::Distinct,
42 count: None,
43 }
44 }
45
46 pub fn delete(&self, serial: impl Into<Serial>) -> DeleteReaction {
57 DeleteReaction {
58 client: self.client.clone(),
59 room: self.room.clone(),
60 serial: serial.into(),
61 kind: ReactionType::Distinct,
62 name: None,
63 }
64 }
65
66 pub fn for_client(&self, serial: impl Into<Serial>) -> ClientReactions {
73 ClientReactions {
74 client: self.client.clone(),
75 room: self.room.clone(),
76 serial: serial.into(),
77 client_id: None,
78 }
79 }
80}
81
82#[derive(Clone, Debug)]
85pub struct SendReaction {
86 client: Client,
87 room: RoomName,
88 serial: Serial,
89 name: String,
90 kind: ReactionType,
91 count: Option<u64>,
92}
93
94impl SendReaction {
95 pub fn kind(mut self, kind: ReactionType) -> Self {
97 self.kind = kind;
98 self
99 }
100
101 pub fn count(mut self, count: u64) -> Self {
104 self.count = Some(count);
105 self
106 }
107}
108
109impl IntoFuture for SendReaction {
110 type Output = Result<()>;
111 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
112
113 fn into_future(self) -> Self::IntoFuture {
114 Box::pin(async move {
115 let mut obj = Map::new();
116 obj.insert("type".to_owned(), Value::String(self.kind.into()));
117 obj.insert("name".to_owned(), Value::String(self.name));
118 if let Some(count) = self.count {
119 obj.insert("count".to_owned(), Value::from(count));
120 }
121 self.client
122 .inner
123 .send(
124 Method::POST,
125 &message_path(self.room.as_str(), self.serial.as_str(), "/reactions"),
126 &[],
127 Some(Value::Object(obj)),
128 false,
131 )
132 .await?;
133 Ok(())
134 })
135 }
136}
137
138#[derive(Clone, Debug)]
141pub struct DeleteReaction {
142 client: Client,
143 room: RoomName,
144 serial: Serial,
145 kind: ReactionType,
146 name: Option<String>,
147}
148
149impl DeleteReaction {
150 pub fn kind(mut self, kind: ReactionType) -> Self {
152 self.kind = kind;
153 self
154 }
155
156 pub fn name(mut self, name: impl Into<String>) -> Self {
159 self.name = Some(name.into());
160 self
161 }
162}
163
164impl IntoFuture for DeleteReaction {
165 type Output = Result<()>;
166 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
167
168 fn into_future(self) -> Self::IntoFuture {
169 Box::pin(async move {
170 let name_required =
173 matches!(self.kind, ReactionType::Distinct | ReactionType::Multiple);
174 if name_required && self.name.is_none() {
175 return Err(Error::InvalidRequest(format!(
176 "reaction name is required to delete a `{}` reaction",
177 String::from(self.kind)
178 )));
179 }
180 let mut query: Vec<(&str, String)> = vec![("type", self.kind.into())];
181 if let Some(name) = self.name {
182 query.push(("name", name));
183 }
184 self.client
185 .inner
186 .send(
187 Method::DELETE,
188 &message_path(self.room.as_str(), self.serial.as_str(), "/reactions"),
189 &query,
190 None,
191 false,
192 )
193 .await?;
194 Ok(())
195 })
196 }
197}
198
199#[derive(Clone, Debug)]
203pub struct ClientReactions {
204 client: Client,
205 room: RoomName,
206 serial: Serial,
207 client_id: Option<String>,
208}
209
210impl ClientReactions {
211 pub fn client_id(mut self, client_id: impl Into<String>) -> Self {
213 self.client_id = Some(client_id.into());
214 self
215 }
216}
217
218impl IntoFuture for ClientReactions {
219 type Output = Result<ReactionSummary>;
220 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
221
222 fn into_future(self) -> Self::IntoFuture {
223 Box::pin(async move {
224 let mut query: Vec<(&str, String)> = Vec::new();
225 if let Some(cid) = self.client_id {
226 query.push(("forClientId", cid));
227 }
228 let resp = self
229 .client
230 .inner
231 .send(
232 Method::GET,
233 &message_path(
234 self.room.as_str(),
235 self.serial.as_str(),
236 "/client-reactions",
237 ),
238 &query,
239 None,
240 false,
241 )
242 .await?;
243 decode_json(&resp.body)
244 })
245 }
246}