twilight_http/request/template/
create_guild_from_template.rs

1use crate::{
2    client::Client,
3    error::Error,
4    request::{Request, TryIntoRequest},
5    response::{Response, ResponseFuture},
6    routing::Route,
7};
8use serde::Serialize;
9use std::future::IntoFuture;
10use twilight_model::guild::Guild;
11use twilight_validate::request::{guild_name as validate_guild_name, ValidationError};
12
13#[derive(Serialize)]
14struct CreateGuildFromTemplateFields<'a> {
15    name: &'a str,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    icon: Option<&'a str>,
18}
19
20/// Create a new guild based on a template.
21///
22/// This endpoint can only be used by bots in less than 10 guilds.
23///
24/// # Errors
25///
26/// Returns an error of type [`GuildName`] if the name length is too short or
27/// too long.
28///
29/// [`GuildName`]: twilight_validate::request::ValidationErrorType::GuildName
30#[must_use = "requests must be configured and executed"]
31pub struct CreateGuildFromTemplate<'a> {
32    fields: Result<CreateGuildFromTemplateFields<'a>, ValidationError>,
33    http: &'a Client,
34    template_code: &'a str,
35}
36
37impl<'a> CreateGuildFromTemplate<'a> {
38    pub(crate) fn new(http: &'a Client, template_code: &'a str, name: &'a str) -> Self {
39        let fields = Ok(CreateGuildFromTemplateFields { name, icon: None }).and_then(|fields| {
40            validate_guild_name(name)?;
41
42            Ok(fields)
43        });
44
45        Self {
46            fields,
47            http,
48            template_code,
49        }
50    }
51
52    /// Set the icon.
53    ///
54    /// This must be a Data URI, in the form of
55    /// `data:image/{type};base64,{data}` where `{type}` is the image MIME type
56    /// and `{data}` is the base64-encoded image. See [Discord Docs/Image Data].
57    ///
58    /// [Discord Docs/Image Data]: https://discord.com/developers/docs/reference#image-data
59    pub fn icon(mut self, icon: &'a str) -> Self {
60        if let Ok(fields) = self.fields.as_mut() {
61            fields.icon = Some(icon);
62        }
63
64        self
65    }
66}
67
68impl IntoFuture for CreateGuildFromTemplate<'_> {
69    type Output = Result<Response<Guild>, Error>;
70
71    type IntoFuture = ResponseFuture<Guild>;
72
73    fn into_future(self) -> Self::IntoFuture {
74        let http = self.http;
75
76        match self.try_into_request() {
77            Ok(request) => http.request(request),
78            Err(source) => ResponseFuture::error(source),
79        }
80    }
81}
82
83impl TryIntoRequest for CreateGuildFromTemplate<'_> {
84    fn try_into_request(self) -> Result<Request, Error> {
85        let fields = self.fields.map_err(Error::validation)?;
86
87        Request::builder(&Route::CreateGuildFromTemplate {
88            template_code: self.template_code,
89        })
90        .json(&fields)
91        .build()
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    use std::error::Error;
99
100    #[test]
101    fn test_create_guild_from_template() -> Result<(), Box<dyn Error>> {
102        let client = Client::new("token".into());
103
104        {
105            let expected = r#"{"name":"New Guild"}"#;
106            let actual =
107                CreateGuildFromTemplate::new(&client, "code", "New Guild").try_into_request()?;
108
109            assert_eq!(Some(expected.as_bytes()), actual.body());
110        }
111
112        {
113            let expected = r#"{"name":"New Guild","icon":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI"}"#;
114            let actual = CreateGuildFromTemplate::new(&client, "code", "New Guild")
115            .icon("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI")
116            .try_into_request()?;
117
118            assert_eq!(Some(expected.as_bytes()), actual.body());
119        }
120        Ok(())
121    }
122}