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