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