discord_webhook_rs/
embed.rs1use super::discord::Error;
2use serde_json::{Value, json};
3
4pub struct Author {
5 name: Option<String>,
6 url: Option<String>,
7 icon_url: Option<String>,
8}
9
10pub struct Field {
11 name: Option<String>,
12 value: Option<String>,
13 inline: Option<bool>,
14}
15
16pub struct Footer {
17 text: Option<String>,
18 icon_url: Option<String>,
19}
20
21pub struct Embed {
22 author: Option<Author>,
23 title: Option<String>,
24 url: Option<String>,
25 description: Option<String>,
26 color: Option<u32>,
27 fields: Vec<Field>,
28 thumbnail: Option<String>,
29 image: Option<String>,
30 footer: Option<Footer>,
31}
32
33impl Author {
34 pub fn new() -> Self {
35 Author {
36 name: None,
37 url: None,
38 icon_url: None,
39 }
40 }
41
42 pub fn name(mut self, name: impl Into<String>) -> Self {
43 self.name = Some(name.into());
44 self
45 }
46
47 pub fn url(mut self, url: impl Into<String>) -> Self {
48 self.url = Some(url.into());
49 self
50 }
51
52 pub fn icon_url(mut self, icon_url: impl Into<String>) -> Self {
53 self.icon_url = Some(icon_url.into());
54 self
55 }
56
57 fn verify(&self) -> Result<(), Error> {
58 if let Some(name) = &self.name {
59 if name.len() > 256 {
60 return Err(Error::MaxContent);
61 }
62 }
63
64 Ok(())
65 }
66
67 pub fn build(&self) -> Result<Value, Error> {
68 self.verify()?;
69
70 let mut obj = serde_json::Map::new();
71
72 if let Some(name) = &self.name {
73 obj.insert("name".into(), Value::String(name.clone()));
74 };
75
76 if let Some(url) = &self.url {
77 obj.insert("url".into(), Value::String(url.clone()));
78 };
79
80 Ok(Value::Object(obj))
81 }
82}
83
84impl Field {
85 pub fn new() -> Self {
86 Field {
87 name: None,
88 value: None,
89 inline: None,
90 }
91 }
92
93 pub fn name(mut self, name: impl Into<String>) -> Self {
94 self.name = Some(name.into());
95 self
96 }
97
98 pub fn value(mut self, value: impl Into<String>) -> Self {
99 self.value = Some(value.into());
100 self
101 }
102
103 pub fn inline(mut self, inline: bool) -> Self {
104 self.inline = Some(inline);
105 self
106 }
107
108 fn verify(&self) -> Result<(), Error> {
109 if let Some(name) = &self.name {
110 if name.len() > 256 {
111 return Err(Error::MaxContent);
112 }
113 }
114
115 if let Some(value) = &self.value {
116 if value.len() > 1024 {
117 return Err(Error::MaxContent);
118 }
119 }
120
121 Ok(())
122 }
123
124 pub fn build(&self) -> Result<Value, Error> {
125 self.verify()?;
126
127 let mut obj = serde_json::Map::new();
128
129 if let Some(name) = &self.name {
130 obj.insert("name".into(), Value::String(name.clone()));
131 } else {
132 return Err(Error::MissingNameField);
133 };
134
135 if let Some(value) = &self.value {
136 obj.insert("value".into(), Value::String(value.clone()));
137 } else {
138 return Err(Error::MissingValueField);
139 };
140
141 if let Some(inline) = &self.inline {
142 obj.insert("inline".into(), Value::Bool(inline.clone()));
143 };
144
145 Ok(Value::Object(obj))
146 }
147}
148
149impl Footer {
150 pub fn new() -> Self {
151 Footer {
152 text: None,
153 icon_url: None,
154 }
155 }
156
157 pub fn text(mut self, text: impl Into<String>) -> Self {
158 self.text = Some(text.into());
159 self
160 }
161
162 pub fn icon_url(mut self, icon_url: impl Into<String>) -> Self {
163 self.icon_url = Some(icon_url.into());
164 self
165 }
166
167 fn verify(&self) -> Result<(), Error> {
168 if let Some(text) = &self.text {
169 if text.len() > 2048 {
170 return Err(Error::MaxContent);
171 }
172 }
173
174 Ok(())
175 }
176
177 pub fn build(&self) -> Result<Value, Error> {
178 self.verify()?;
179
180 let mut obj = serde_json::Map::new();
181
182 if let Some(text) = &self.text {
183 obj.insert("text".into(), Value::String(text.clone()));
184 };
185
186 if let Some(icon_url) = &self.icon_url {
187 obj.insert("icon_url".into(), Value::String(icon_url.clone()));
188 };
189
190 Ok(Value::Object(obj))
191 }
192}
193
194impl Embed {
195 pub fn new() -> Self {
196 Embed {
197 author: None,
198 title: None,
199 url: None,
200 description: None,
201 color: None,
202 fields: Vec::new(),
203 thumbnail: None,
204 image: None,
205 footer: None,
206 }
207 }
208
209 pub fn author(mut self, author: Author) -> Self {
210 self.author = Some(author);
211 self
212 }
213
214 pub fn title(mut self, title: impl Into<String>) -> Self {
215 self.title = Some(title.into());
216 self
217 }
218
219 pub fn url(mut self, url: impl Into<String>) -> Self {
220 self.url = Some(url.into());
221 self
222 }
223
224 pub fn description(mut self, description: impl Into<String>) -> Self {
225 self.description = Some(description.into());
226 self
227 }
228
229 pub fn color(mut self, color: u32) -> Self {
230 self.color = Some(color);
231 self
232 }
233
234 pub fn add_field(mut self, field: Field) -> Self {
235 self.fields.push(field);
236 self
237 }
238
239 pub fn thumbnail(mut self, thumbnail: impl Into<String>) -> Self {
240 self.thumbnail = Some(thumbnail.into());
241 self
242 }
243
244 pub fn image(mut self, image: impl Into<String>) -> Self {
245 self.image = Some(image.into());
246 self
247 }
248
249 pub fn footer(mut self, footer: Footer) -> Self {
250 self.footer = Some(footer);
251 self
252 }
253
254 fn verify(&self) -> Result<(), Error> {
255 if let Some(title) = &self.title {
256 if title.len() > 256 {
257 return Err(Error::MaxContent);
258 }
259 }
260
261 if let Some(description) = &self.description {
262 if description.len() > 4096 {
263 return Err(Error::MaxContent);
264 }
265 }
266
267 if let Some(color) = self.color {
268 if color > (1 << 8 * 3) - 1 {
270 return Err(Error::InvalidColor);
271 }
272 }
273
274 if self.fields.len() > 25 {
275 return Err(Error::MaxField);
276 }
277
278 Ok(())
279 }
280
281 pub fn build(&self) -> Result<Value, Error> {
282 self.verify()?;
283
284 let mut obj = serde_json::Map::new();
285
286 if let Some(author) = &self.author {
287 obj.insert("author".into(), author.build()?);
288 }
289
290 if let Some(footer) = &self.footer {
291 obj.insert("footer".into(), footer.build()?);
292 }
293
294 if let Some(title) = &self.title {
295 obj.insert("title".into(), Value::String(title.clone()));
296 }
297
298 if let Some(url) = &self.url {
299 obj.insert("url".into(), Value::String(url.clone()));
300 }
301
302 if let Some(description) = &self.description {
303 obj.insert("description".into(), Value::String(description.clone()));
304 }
305
306 if let Some(color) = &self.color {
307 obj.insert("color".into(), json!(self.color));
308 }
309
310 if let Some(thumbnail) = &self.thumbnail {
311 obj.insert("thumbnail".into(), json!({"url": thumbnail}));
312 }
313
314 if let Some(image) = &self.image {
315 obj.insert("image".into(), json!({"url": image}));
316 }
317
318 obj.insert("fields".into(), Value::Array(Vec::new()));
319 if let Value::Array(ref mut fields) = obj["fields"] {
320 for field in &self.fields {
321 fields.push(field.build()?);
322 }
323 };
324
325 if obj.contains_key("author")
327 || obj.contains_key("footer")
328 || obj.contains_key("title")
329 || obj.contains_key("description")
330 || obj.contains_key("thumbnail")
331 || obj.contains_key("image")
332 || self.fields.len() > 0
333 {
334 Ok(Value::Object(obj))
335 } else {
336 Err(Error::InvalidEmbed)
337 }
338 }
339}