Skip to main content

twilight_model/channel/message/embed/
mod.rs

1//! Rich message content.
2
3#![allow(missing_docs)]
4mod author;
5mod field;
6mod flags;
7mod footer;
8mod image;
9mod media_flags;
10mod provider;
11mod thumbnail;
12mod video;
13
14pub use self::{
15    author::EmbedAuthor, field::EmbedField, flags::EmbedFlags, footer::EmbedFooter,
16    image::EmbedImage, media_flags::EmbedMediaFlags, provider::EmbedProvider,
17    thumbnail::EmbedThumbnail, video::EmbedVideo,
18};
19
20use crate::util::Timestamp;
21use serde::{Deserialize, Serialize};
22
23#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
24pub struct Embed {
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub author: Option<EmbedAuthor>,
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub color: Option<u32>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub description: Option<String>,
31    #[serde(default, skip_serializing_if = "Vec::is_empty")]
32    pub fields: Vec<EmbedField>,
33    /// Flags of the embed, if available.
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub flags: Option<EmbedFlags>,
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub footer: Option<EmbedFooter>,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub image: Option<EmbedImage>,
40    #[serde(rename = "type")]
41    pub kind: String,
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub provider: Option<EmbedProvider>,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub thumbnail: Option<EmbedThumbnail>,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub timestamp: Option<Timestamp>,
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub title: Option<String>,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub url: Option<String>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub video: Option<EmbedVideo>,
54}
55
56#[cfg(test)]
57mod tests {
58    use super::{
59        Embed, EmbedAuthor, EmbedField, EmbedFooter, EmbedImage, EmbedMediaFlags, EmbedProvider,
60        EmbedThumbnail, EmbedVideo,
61    };
62    use crate::util::datetime::{Timestamp, TimestampParseError};
63    use serde_test::Token;
64    use std::str::FromStr;
65
66    #[test]
67    fn embed() -> Result<(), TimestampParseError> {
68        let timestamp = Timestamp::from_str("2021-08-02T16:56:43.772000+00:00")?;
69
70        let value = Embed {
71            author: None,
72            color: Some(123),
73            description: Some("a description".to_owned()),
74            fields: Vec::new(),
75            flags: None,
76            footer: None,
77            image: None,
78            kind: "rich".to_owned(),
79            provider: None,
80            thumbnail: None,
81            timestamp: Some(timestamp),
82            title: Some("a title".to_owned()),
83            url: Some("https://example.com".to_owned()),
84            video: None,
85        };
86
87        serde_test::assert_tokens(
88            &value,
89            &[
90                Token::Struct {
91                    name: "Embed",
92                    len: 6,
93                },
94                Token::Str("color"),
95                Token::Some,
96                Token::U32(123),
97                Token::Str("description"),
98                Token::Some,
99                Token::Str("a description"),
100                Token::Str("type"),
101                Token::Str("rich"),
102                Token::Str("timestamp"),
103                Token::Some,
104                Token::Str("2021-08-02T16:56:43.772000+00:00"),
105                Token::Str("title"),
106                Token::Some,
107                Token::Str("a title"),
108                Token::Str("url"),
109                Token::Some,
110                Token::Str("https://example.com"),
111                Token::StructEnd,
112            ],
113        );
114
115        Ok(())
116    }
117
118    #[allow(clippy::too_many_lines)]
119    #[test]
120    fn embed_complete() -> Result<(), TimestampParseError> {
121        let timestamp = Timestamp::from_str("2021-08-02T16:56:43.772000+00:00")?;
122
123        let value = Embed {
124            author: Some(EmbedAuthor {
125                icon_url: Some("https://example.com/1.png".to_owned()),
126                name: "test".to_owned(),
127                proxy_icon_url: Some("https://example.com".to_owned()),
128                url: Some("https://example.com".to_owned()),
129            }),
130            color: Some(123),
131            description: Some("a description".to_owned()),
132            fields: vec![EmbedField {
133                inline: true,
134                name: "name".to_owned(),
135                value: "value".to_owned(),
136            }],
137            flags: None,
138            footer: Some(EmbedFooter {
139                icon_url: Some("https://example.com/1.png".to_owned()),
140                proxy_icon_url: Some("https://cdn.example.com/1-hash.png".to_owned()),
141                text: "a footer".to_owned(),
142            }),
143            image: Some(EmbedImage {
144                height: Some(1440),
145                proxy_url: Some("https://cdn.example.com/1-hash.png".to_owned()),
146                url: "https://example.com/1.png".to_owned(),
147                width: Some(2560),
148                content_type: Some("image/png".to_owned()),
149                // Thumbhash of the twilight project logo.
150                placeholder: Some("zSeKDQIoCLiHeIKeiLyfrsBqCIaYaHJ2Vg".to_owned()),
151                placeholder_version: Some(1),
152                flags: Some(EmbedMediaFlags::IS_ANIMATED),
153            }),
154            kind: "rich".to_owned(),
155            provider: Some(EmbedProvider {
156                name: Some("Example".to_owned()),
157                url: Some("https://example.com".to_owned()),
158            }),
159            thumbnail: Some(EmbedThumbnail {
160                height: Some(1440),
161                proxy_url: Some("https://cdn.example.com/1-hash.png".to_owned()),
162                url: "https://example.com/1.png".to_owned(),
163                width: Some(2560),
164            }),
165            timestamp: Some(timestamp),
166            title: Some("a title".to_owned()),
167            url: Some("https://example.com".to_owned()),
168            video: Some(EmbedVideo {
169                height: Some(1440),
170                proxy_url: Some("https://proxy.cdn.example.com/1-hash.mp4".to_owned()),
171                url: Some("https://cdn.example.com/1-hash.mp4".to_owned()),
172                width: Some(2560),
173                content_type: Some("video/mp4".to_owned()),
174                // Thumbhash of the twilight project logo.
175                placeholder: Some("zSeKDQIoCLiHeIKeiLyfrsBqCIaYaHJ2Vg".to_owned()),
176                placeholder_version: Some(1),
177                flags: Some(EmbedMediaFlags::IS_ANIMATED),
178            }),
179        };
180
181        serde_test::assert_tokens(
182            &value,
183            &[
184                Token::Struct {
185                    name: "Embed",
186                    len: 13,
187                },
188                Token::Str("author"),
189                Token::Some,
190                Token::Struct {
191                    name: "EmbedAuthor",
192                    len: 4,
193                },
194                Token::Str("icon_url"),
195                Token::Some,
196                Token::Str("https://example.com/1.png"),
197                Token::Str("name"),
198                Token::Str("test"),
199                Token::Str("proxy_icon_url"),
200                Token::Some,
201                Token::Str("https://example.com"),
202                Token::Str("url"),
203                Token::Some,
204                Token::Str("https://example.com"),
205                Token::StructEnd,
206                Token::Str("color"),
207                Token::Some,
208                Token::U32(123),
209                Token::Str("description"),
210                Token::Some,
211                Token::Str("a description"),
212                Token::Str("fields"),
213                Token::Seq { len: Some(1) },
214                Token::Struct {
215                    name: "EmbedField",
216                    len: 3,
217                },
218                Token::Str("inline"),
219                Token::Bool(true),
220                Token::Str("name"),
221                Token::Str("name"),
222                Token::Str("value"),
223                Token::Str("value"),
224                Token::StructEnd,
225                Token::SeqEnd,
226                Token::Str("footer"),
227                Token::Some,
228                Token::Struct {
229                    name: "EmbedFooter",
230                    len: 3,
231                },
232                Token::Str("icon_url"),
233                Token::Some,
234                Token::Str("https://example.com/1.png"),
235                Token::Str("proxy_icon_url"),
236                Token::Some,
237                Token::Str("https://cdn.example.com/1-hash.png"),
238                Token::Str("text"),
239                Token::Str("a footer"),
240                Token::StructEnd,
241                Token::Str("image"),
242                Token::Some,
243                Token::Struct {
244                    name: "EmbedImage",
245                    len: 8,
246                },
247                Token::Str("height"),
248                Token::Some,
249                Token::U64(1440),
250                Token::Str("proxy_url"),
251                Token::Some,
252                Token::Str("https://cdn.example.com/1-hash.png"),
253                Token::Str("url"),
254                Token::Str("https://example.com/1.png"),
255                Token::Str("width"),
256                Token::Some,
257                Token::U64(2560),
258                Token::Str("content_type"),
259                Token::Some,
260                Token::Str("image/png"),
261                Token::Str("placeholder"),
262                Token::Some,
263                Token::Str("zSeKDQIoCLiHeIKeiLyfrsBqCIaYaHJ2Vg"),
264                Token::Str("placeholder_version"),
265                Token::Some,
266                Token::U64(1),
267                Token::Str("flags"),
268                Token::Some,
269                Token::U64(32),
270                Token::StructEnd,
271                Token::Str("type"),
272                Token::Str("rich"),
273                Token::Str("provider"),
274                Token::Some,
275                Token::Struct {
276                    name: "EmbedProvider",
277                    len: 2,
278                },
279                Token::Str("name"),
280                Token::Some,
281                Token::Str("Example"),
282                Token::Str("url"),
283                Token::Some,
284                Token::Str("https://example.com"),
285                Token::StructEnd,
286                Token::Str("thumbnail"),
287                Token::Some,
288                Token::Struct {
289                    name: "EmbedThumbnail",
290                    len: 4,
291                },
292                Token::Str("height"),
293                Token::Some,
294                Token::U64(1440),
295                Token::Str("proxy_url"),
296                Token::Some,
297                Token::Str("https://cdn.example.com/1-hash.png"),
298                Token::Str("url"),
299                Token::Str("https://example.com/1.png"),
300                Token::Str("width"),
301                Token::Some,
302                Token::U64(2560),
303                Token::StructEnd,
304                Token::Str("timestamp"),
305                Token::Some,
306                Token::Str("2021-08-02T16:56:43.772000+00:00"),
307                Token::Str("title"),
308                Token::Some,
309                Token::Str("a title"),
310                Token::Str("url"),
311                Token::Some,
312                Token::Str("https://example.com"),
313                Token::Str("video"),
314                Token::Some,
315                Token::Struct {
316                    name: "EmbedVideo",
317                    len: 8,
318                },
319                Token::Str("height"),
320                Token::Some,
321                Token::U64(1440),
322                Token::Str("proxy_url"),
323                Token::Some,
324                Token::Str("https://proxy.cdn.example.com/1-hash.mp4"),
325                Token::Str("url"),
326                Token::Some,
327                Token::Str("https://cdn.example.com/1-hash.mp4"),
328                Token::Str("width"),
329                Token::Some,
330                Token::U64(2560),
331                Token::Str("content_type"),
332                Token::Some,
333                Token::Str("video/mp4"),
334                Token::Str("placeholder"),
335                Token::Some,
336                Token::Str("zSeKDQIoCLiHeIKeiLyfrsBqCIaYaHJ2Vg"),
337                Token::Str("placeholder_version"),
338                Token::Some,
339                Token::U64(1),
340                Token::Str("flags"),
341                Token::Some,
342                Token::U64(32),
343                Token::StructEnd,
344                Token::StructEnd,
345            ],
346        );
347
348        Ok(())
349    }
350}