1#![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 #[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 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 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}