Skip to main content

twilight_http/request/channel/reaction/
get_reactions.rs

1use super::RequestReactionType;
2#[cfg(not(target_os = "wasi"))]
3use crate::response::{Response, ResponseFuture, marker::ListBody};
4use crate::{
5    client::Client,
6    error::Error,
7    request::{Request, TryIntoRequest},
8    routing::Route,
9};
10use std::future::IntoFuture;
11use twilight_model::{
12    channel::message::ReactionType,
13    id::{
14        Id,
15        marker::{ChannelMarker, MessageMarker, UserMarker},
16    },
17    user::User,
18};
19use twilight_validate::request::{
20    ValidationError, get_reactions_limit as validate_get_reactions_limit,
21};
22
23struct GetReactionsFields {
24    after: Option<Id<UserMarker>>,
25    limit: Option<u16>,
26    kind: Option<ReactionType>,
27}
28
29/// Get a list of users that reacted to a message with an `emoji`.
30///
31/// This endpoint is limited to 100 users maximum, so if a message has more than 100 reactions,
32/// requests must be chained until all reactions are retrieved.
33#[must_use = "requests must be configured and executed"]
34pub struct GetReactions<'a> {
35    channel_id: Id<ChannelMarker>,
36    emoji: &'a RequestReactionType<'a>,
37    fields: Result<GetReactionsFields, ValidationError>,
38    http: &'a Client,
39    message_id: Id<MessageMarker>,
40}
41
42impl<'a> GetReactions<'a> {
43    pub(crate) const fn new(
44        http: &'a Client,
45        channel_id: Id<ChannelMarker>,
46        message_id: Id<MessageMarker>,
47        emoji: &'a RequestReactionType<'a>,
48    ) -> Self {
49        Self {
50            channel_id,
51            emoji,
52            fields: Ok(GetReactionsFields {
53                after: None,
54                limit: None,
55                kind: None,
56            }),
57            http,
58            message_id,
59        }
60    }
61
62    /// Get users after this id.
63    pub const fn after(mut self, after: Id<UserMarker>) -> Self {
64        if let Ok(fields) = self.fields.as_mut() {
65            fields.after = Some(after);
66        }
67
68        self
69    }
70
71    /// Set the maximum number of users to retrieve.
72    ///
73    /// The minimum is 1 and the maximum is 100. If no limit is specified, Discord sets the default
74    /// to 25.
75    ///
76    /// # Errors
77    ///
78    /// Returns an error of type [`GetReactions`] if the amount is greater than
79    /// 100.
80    ///
81    /// [`GetReactions`]: twilight_validate::request::ValidationErrorType::GetReactions
82    pub fn limit(mut self, limit: u16) -> Self {
83        self.fields = self.fields.and_then(|mut fields| {
84            validate_get_reactions_limit(limit)?;
85            fields.limit = Some(limit);
86
87            Ok(fields)
88        });
89
90        self
91    }
92
93    /// Set the kind of reaction to retrieve.
94    ///
95    /// This can be either a super reaction or a normal reaction.
96    pub const fn kind(mut self, kind: ReactionType) -> Self {
97        if let Ok(fields) = self.fields.as_mut() {
98            fields.kind = Some(kind);
99        }
100
101        self
102    }
103}
104
105#[cfg(not(target_os = "wasi"))]
106impl IntoFuture for GetReactions<'_> {
107    type Output = Result<Response<ListBody<User>>, Error>;
108
109    type IntoFuture = ResponseFuture<ListBody<User>>;
110
111    fn into_future(self) -> Self::IntoFuture {
112        let http = self.http;
113
114        match self.try_into_request() {
115            Ok(request) => http.request(request),
116            Err(source) => ResponseFuture::error(source),
117        }
118    }
119}
120
121impl TryIntoRequest for GetReactions<'_> {
122    fn try_into_request(self) -> Result<Request, Error> {
123        let fields = self.fields.map_err(Error::validation)?;
124
125        Ok(Request::from_route(&Route::GetReactionUsers {
126            after: fields.after.map(Id::get),
127            channel_id: self.channel_id.get(),
128            emoji: self.emoji,
129            limit: fields.limit,
130            message_id: self.message_id.get(),
131            kind: fields.kind.map(Into::into),
132        }))
133    }
134}