twilight_http/request/guild/member/
update_guild_member.rs1use crate::{
2 client::Client,
3 error::Error,
4 request::{self, AuditLogReason, Nullable, Request, TryIntoRequest},
5 response::{Response, ResponseFuture},
6 routing::Route,
7};
8use serde::Serialize;
9use std::future::IntoFuture;
10use twilight_model::{
11 guild::Member,
12 id::{
13 marker::{ChannelMarker, GuildMarker, RoleMarker, UserMarker},
14 Id,
15 },
16 util::Timestamp,
17};
18use twilight_validate::request::{
19 audit_reason as validate_audit_reason,
20 communication_disabled_until as validate_communication_disabled_until,
21 nickname as validate_nickname, ValidationError,
22};
23
24#[derive(Serialize)]
25struct UpdateGuildMemberFields<'a> {
26 #[allow(clippy::option_option)]
27 #[serde(skip_serializing_if = "Option::is_none")]
28 channel_id: Option<Nullable<Id<ChannelMarker>>>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 communication_disabled_until: Option<Nullable<Timestamp>>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 deaf: Option<bool>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 mute: Option<bool>,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 nick: Option<Nullable<&'a str>>,
37 #[serde(skip_serializing_if = "Option::is_none")]
38 roles: Option<&'a [Id<RoleMarker>]>,
39}
40
41#[must_use = "requests must be configured and executed"]
47pub struct UpdateGuildMember<'a> {
48 fields: Result<UpdateGuildMemberFields<'a>, ValidationError>,
49 guild_id: Id<GuildMarker>,
50 http: &'a Client,
51 user_id: Id<UserMarker>,
52 reason: Result<Option<&'a str>, ValidationError>,
53}
54
55impl<'a> UpdateGuildMember<'a> {
56 pub(crate) const fn new(
57 http: &'a Client,
58 guild_id: Id<GuildMarker>,
59 user_id: Id<UserMarker>,
60 ) -> Self {
61 Self {
62 fields: Ok(UpdateGuildMemberFields {
63 channel_id: None,
64 communication_disabled_until: None,
65 deaf: None,
66 mute: None,
67 nick: None,
68 roles: None,
69 }),
70 guild_id,
71 http,
72 user_id,
73 reason: Ok(None),
74 }
75 }
76
77 pub fn channel_id(mut self, channel_id: Option<Id<ChannelMarker>>) -> Self {
79 if let Ok(fields) = self.fields.as_mut() {
80 fields.channel_id = Some(Nullable(channel_id));
81 }
82
83 self
84 }
85
86 pub fn communication_disabled_until(mut self, timestamp: Option<Timestamp>) -> Self {
103 self.fields = self.fields.and_then(|mut fields| {
104 if let Some(timestamp) = timestamp {
105 validate_communication_disabled_until(timestamp)?;
106 }
107
108 fields.communication_disabled_until = Some(Nullable(timestamp));
109
110 Ok(fields)
111 });
112
113 self
114 }
115
116 pub fn deaf(mut self, deaf: bool) -> Self {
118 if let Ok(fields) = self.fields.as_mut() {
119 fields.deaf = Some(deaf);
120 }
121
122 self
123 }
124
125 pub fn mute(mut self, mute: bool) -> Self {
127 if let Ok(fields) = self.fields.as_mut() {
128 fields.mute = Some(mute);
129 }
130
131 self
132 }
133
134 pub fn nick(mut self, nick: Option<&'a str>) -> Self {
145 self.fields = self.fields.and_then(|mut fields| {
146 if let Some(nick) = nick {
147 validate_nickname(nick)?;
148 }
149
150 fields.nick = Some(Nullable(nick));
151
152 Ok(fields)
153 });
154
155 self
156 }
157
158 pub fn roles(mut self, roles: &'a [Id<RoleMarker>]) -> Self {
160 if let Ok(fields) = self.fields.as_mut() {
161 fields.roles = Some(roles);
162 }
163
164 self
165 }
166}
167
168impl<'a> AuditLogReason<'a> for UpdateGuildMember<'a> {
169 fn reason(mut self, reason: &'a str) -> Self {
170 self.reason = validate_audit_reason(reason).and(Ok(Some(reason)));
171
172 self
173 }
174}
175
176impl IntoFuture for UpdateGuildMember<'_> {
177 type Output = Result<Response<Member>, Error>;
178
179 type IntoFuture = ResponseFuture<Member>;
180
181 fn into_future(self) -> Self::IntoFuture {
182 let http = self.http;
183
184 match self.try_into_request() {
185 Ok(request) => http.request(request),
186 Err(source) => ResponseFuture::error(source),
187 }
188 }
189}
190
191impl TryIntoRequest for UpdateGuildMember<'_> {
192 fn try_into_request(self) -> Result<Request, Error> {
193 let fields = self.fields.map_err(Error::validation)?;
194 let mut request = Request::builder(&Route::UpdateMember {
195 guild_id: self.guild_id.get(),
196 user_id: self.user_id.get(),
197 })
198 .json(&fields);
199
200 if let Some(reason) = self.reason.map_err(Error::validation)? {
201 request = request.headers(request::audit_header(reason)?);
202 }
203
204 request.build()
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::{UpdateGuildMember, UpdateGuildMemberFields};
211 use crate::{
212 request::{Nullable, Request, TryIntoRequest},
213 routing::Route,
214 Client,
215 };
216 use std::error::Error;
217 use twilight_model::id::{
218 marker::{GuildMarker, UserMarker},
219 Id,
220 };
221
222 const GUILD_ID: Id<GuildMarker> = Id::new(1);
223 const USER_ID: Id<UserMarker> = Id::new(1);
224
225 #[test]
226 fn request() -> Result<(), Box<dyn Error>> {
227 let client = Client::new("foo".to_owned());
228 let builder = UpdateGuildMember::new(&client, GUILD_ID, USER_ID)
229 .deaf(true)
230 .mute(true);
231 let actual = builder.try_into_request()?;
232
233 let body = UpdateGuildMemberFields {
234 channel_id: None,
235 communication_disabled_until: None,
236 deaf: Some(true),
237 mute: Some(true),
238 nick: None,
239 roles: None,
240 };
241 let route = Route::UpdateMember {
242 guild_id: GUILD_ID.get(),
243 user_id: USER_ID.get(),
244 };
245 let expected = Request::builder(&route).json(&body).build()?;
246
247 assert_eq!(actual.body, expected.body);
248 assert_eq!(actual.path, expected.path);
249
250 Ok(())
251 }
252
253 #[test]
254 fn nick_set_null() -> Result<(), Box<dyn Error>> {
255 let client = Client::new("foo".to_owned());
256 let builder = UpdateGuildMember::new(&client, GUILD_ID, USER_ID).nick(None);
257 let actual = builder.try_into_request()?;
258
259 let body = UpdateGuildMemberFields {
260 channel_id: None,
261 communication_disabled_until: None,
262 deaf: None,
263 mute: None,
264 nick: Some(Nullable(None)),
265 roles: None,
266 };
267 let route = Route::UpdateMember {
268 guild_id: GUILD_ID.get(),
269 user_id: USER_ID.get(),
270 };
271 let expected = Request::builder(&route).json(&body).build()?;
272
273 assert_eq!(actual.body, expected.body);
274
275 Ok(())
276 }
277
278 #[test]
279 fn nick_set_value() -> Result<(), Box<dyn Error>> {
280 let client = Client::new("foo".to_owned());
281 let builder = UpdateGuildMember::new(&client, GUILD_ID, USER_ID).nick(Some("foo"));
282 let actual = builder.try_into_request()?;
283
284 let body = UpdateGuildMemberFields {
285 channel_id: None,
286 communication_disabled_until: None,
287 deaf: None,
288 mute: None,
289 nick: Some(Nullable(Some("foo"))),
290 roles: None,
291 };
292 let route = Route::UpdateMember {
293 guild_id: GUILD_ID.get(),
294 user_id: USER_ID.get(),
295 };
296 let expected = Request::builder(&route).json(&body).build()?;
297
298 assert_eq!(actual.body, expected.body);
299
300 Ok(())
301 }
302}