twilight_http/request/user/
update_current_user.rs

1use 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::user::User;
11use twilight_validate::request::{
12    audit_reason as validate_audit_reason, username as validate_username, ValidationError,
13};
14
15#[derive(Serialize)]
16struct UpdateCurrentUserFields<'a> {
17    #[serde(skip_serializing_if = "Option::is_none")]
18    avatar: Option<Nullable<&'a str>>,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    banner: Option<Nullable<&'a str>>,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    username: Option<&'a str>,
23}
24
25/// Update the current user.
26///
27/// All parameters are optional. If the username is changed, it may cause the discriminator to be
28/// randomized.
29#[must_use = "requests must be configured and executed"]
30pub struct UpdateCurrentUser<'a> {
31    fields: Result<UpdateCurrentUserFields<'a>, ValidationError>,
32    http: &'a Client,
33    reason: Result<Option<&'a str>, ValidationError>,
34}
35
36impl<'a> UpdateCurrentUser<'a> {
37    pub(crate) const fn new(http: &'a Client) -> Self {
38        Self {
39            fields: Ok(UpdateCurrentUserFields {
40                avatar: None,
41                banner: None,
42                username: None,
43            }),
44            http,
45            reason: Ok(None),
46        }
47    }
48
49    /// Set the user's avatar.
50    ///
51    /// This must be a Data URI, in the form of
52    /// `data:image/{type};base64,{data}` where `{type}` is the image MIME type
53    /// and `{data}` is the base64-encoded image. See [Discord Docs/Image Data].
54    ///
55    /// [Discord Docs/Image Data]: https://discord.com/developers/docs/reference#image-data
56    pub fn avatar(mut self, avatar: Option<&'a str>) -> Self {
57        if let Ok(fields) = self.fields.as_mut() {
58            fields.avatar = Some(Nullable(avatar));
59        }
60
61        self
62    }
63
64    /// Set the user's banner.
65    ///
66    /// This must be a Data URI, in the form of
67    /// `data:image/{type};base64,{data}` where `{type}` is the image MIME type
68    /// and `{data}` is the base64-encoded image. See [Discord Docs/Image Data].
69    ///
70    /// [Discord Docs/Image Data]: https://discord.com/developers/docs/reference#image-data
71    pub fn banner(mut self, banner: Option<&'a str>) -> Self {
72        if let Ok(fields) = self.fields.as_mut() {
73            fields.banner = Some(Nullable(banner));
74        }
75
76        self
77    }
78
79    /// Set the username.
80    ///
81    /// The minimum length is 1 UTF-16 character and the maximum is 32 UTF-16 characters.
82    ///
83    /// # Errors
84    ///
85    /// Returns an error of type [`Username`] if the username length is too
86    /// short or too long.
87    ///
88    /// [`Username`]: twilight_validate::request::ValidationErrorType::Username
89    pub fn username(mut self, username: &'a str) -> Self {
90        self.fields = self.fields.and_then(|mut fields| {
91            validate_username(username)?;
92            fields.username.replace(username);
93
94            Ok(fields)
95        });
96
97        self
98    }
99}
100
101impl<'a> AuditLogReason<'a> for UpdateCurrentUser<'a> {
102    fn reason(mut self, reason: &'a str) -> Self {
103        self.reason = validate_audit_reason(reason).and(Ok(Some(reason)));
104
105        self
106    }
107}
108
109impl IntoFuture for UpdateCurrentUser<'_> {
110    type Output = Result<Response<User>, Error>;
111
112    type IntoFuture = ResponseFuture<User>;
113
114    fn into_future(self) -> Self::IntoFuture {
115        let http = self.http;
116
117        match self.try_into_request() {
118            Ok(request) => http.request(request),
119            Err(source) => ResponseFuture::error(source),
120        }
121    }
122}
123
124impl TryIntoRequest for UpdateCurrentUser<'_> {
125    fn try_into_request(self) -> Result<Request, Error> {
126        let fields = self.fields.map_err(Error::validation)?;
127
128        let mut request = Request::builder(&Route::UpdateCurrentUser).json(&fields);
129
130        if let Some(reason) = self.reason.map_err(Error::validation)? {
131            request = request.headers(request::audit_header(reason)?);
132        }
133
134        request.build()
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use std::error::Error;
142
143    #[test]
144    fn test_clear_attachment() -> Result<(), Box<dyn Error>> {
145        let client = Client::new("token".into());
146
147        {
148            let expected = r"{}";
149            let actual = UpdateCurrentUser::new(&client).try_into_request()?;
150
151            assert_eq!(Some(expected.as_bytes()), actual.body());
152        }
153
154        {
155            let expected = r#"{"avatar":null}"#;
156            let actual = UpdateCurrentUser::new(&client)
157                .avatar(None)
158                .try_into_request()?;
159
160            assert_eq!(Some(expected.as_bytes()), actual.body());
161
162            let expected = r#"{"avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI"}"#;
163            let actual = UpdateCurrentUser::new(&client).avatar(Some("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI")).try_into_request()?;
164
165            assert_eq!(Some(expected.as_bytes()), actual.body());
166        }
167
168        {
169            let expected = r#"{"username":"other side"}"#;
170            let actual = UpdateCurrentUser::new(&client)
171                .username("other side")
172                .try_into_request()?;
173
174            assert_eq!(Some(expected.as_bytes()), actual.body());
175        }
176
177        {
178            let expected = r#"{"avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI","username":"other side"}"#;
179            let actual = UpdateCurrentUser::new(&client).avatar(Some("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI")).username("other side").try_into_request()?;
180
181            assert_eq!(Some(expected.as_bytes()), actual.body());
182        }
183        Ok(())
184    }
185}