use crate::internal::prelude::*;
use crate::model::channel::Embed;
use crate::utils;
use chrono::{DateTime, TimeZone};
use serde_json::{json, Value};
use std::fmt::Display;
use std::collections::HashMap;
#[cfg(feature = "utils")]
use crate::utils::Colour;
#[derive(Clone, Debug)]
pub struct CreateEmbed(pub HashMap<&'static str, Value>);
impl CreateEmbed {
pub fn author<F>(&mut self, f: F) -> &mut Self
where F: FnOnce(&mut CreateEmbedAuthor) -> &mut CreateEmbedAuthor {
let mut author = CreateEmbedAuthor::default();
f(&mut author);
self.set_author(author)
}
pub fn set_author(&mut self, author: CreateEmbedAuthor) -> &mut Self {
let map = utils::hashmap_to_json_map(author.0);
self.0.insert("author", Value::Object(map));
self
}
#[cfg(feature = "utils")]
#[inline]
pub fn color<C: Into<Colour>>(&mut self, colour: C) -> &mut Self {
self.colour(colour);
self
}
#[cfg(feature = "utils")]
#[inline]
pub fn colour<C: Into<Colour>>(&mut self, colour: C) -> &mut Self {
self._colour(colour.into());
self
}
#[cfg(feature = "utils")]
fn _colour(&mut self, colour: Colour) {
self.0.insert(
"color",
Value::Number(Number::from(u64::from(colour.0))),
);
}
#[cfg(not(feature = "utils"))]
#[inline]
pub fn color(&mut self, colour: u32) -> &mut Self {
self.colour(colour);
self
}
#[cfg(not(feature = "utils"))]
pub fn colour(&mut self, colour: u32) -> &mut Self {
self.0.insert("color", Value::Number(Number::from(colour)));
self
}
#[inline]
pub fn description<D: ToString>(&mut self, description: D) -> &mut Self {
self.0.insert("description", Value::String(description.to_string()));
self
}
#[inline]
pub fn field<T, U>(&mut self, name: T, value: U, inline: bool) -> &mut Self
where T: ToString, U: ToString {
self._field(name.to_string(), value.to_string(), inline);
self
}
fn _field(&mut self, name: String, value: String, inline: bool) {
{
let entry = self.0
.entry("fields")
.or_insert_with(|| Value::Array(vec![]));
if let Value::Array(ref mut inner) = *entry {
inner.push(json!({
"inline": inline,
"name": name,
"value": value,
}));
}
}
}
pub fn fields<T, U, It>(&mut self, fields: It) -> &mut Self
where It: IntoIterator<Item=(T, U, bool)>,
T: ToString,
U: ToString {
for (name, value, inline) in fields {
self.field(name, value, inline);
}
self
}
pub fn footer<F>(&mut self, f: F) -> &mut Self
where F: FnOnce(&mut CreateEmbedFooter) -> &mut CreateEmbedFooter {
let mut create_embed_footer = CreateEmbedFooter::default();
f(&mut create_embed_footer);
self.set_footer(create_embed_footer)
}
pub fn set_footer(&mut self, create_embed_footer: CreateEmbedFooter) -> &mut Self {
let footer = create_embed_footer.0;
let map = utils::hashmap_to_json_map(footer);
self.0.insert("footer", Value::Object(map));
self
}
fn url_object(&mut self, name: &'static str, url: String) -> &mut Self {
let obj = json!({
"url": url,
});
self.0.insert(name, obj);
self
}
#[inline]
pub fn image<S: ToString>(&mut self, url: S) -> &mut Self {
self.url_object("image", url.to_string());
self
}
#[inline]
pub fn thumbnail<S: ToString>(&mut self, url: S) -> &mut Self {
self.url_object("thumbnail", url.to_string());
self
}
#[inline]
pub fn timestamp<T: Into<Timestamp>>(&mut self, timestamp: T) -> &mut Self {
self._timestamp(timestamp.into());
self
}
fn _timestamp(&mut self, timestamp: Timestamp) {
self.0.insert("timestamp", Value::String(timestamp.ts));
}
#[inline]
pub fn title<D: ToString>(&mut self, title: D) -> &mut Self {
self.0.insert("title", Value::String(title.to_string()));
self
}
#[inline]
pub fn url<S: ToString>(&mut self, url: S) -> &mut Self {
self.0.insert("url", Value::String(url.to_string()));
self
}
#[inline]
pub fn attachment<S: ToString>(&mut self, filename: S) -> &mut Self {
let mut filename = filename.to_string();
filename.insert_str(0, "attachment://");
self.url_object("image", filename);
self
}
}
impl Default for CreateEmbed {
fn default() -> CreateEmbed {
let mut map = HashMap::new();
map.insert("type", Value::String("rich".to_string()));
CreateEmbed(map)
}
}
impl From<Embed> for CreateEmbed {
fn from(embed: Embed) -> Self {
let mut b = CreateEmbed::default();
b.colour(embed.colour);
if let Some(author) = embed.author {
b.author(move |a| {
a.name(&author.name);
if let Some(icon_url) = author.icon_url {
a.icon_url(&icon_url);
}
if let Some(url) = author.url {
a.url(&url);
}
a
});
}
if let Some(description) = embed.description {
b.description(&description);
}
for field in embed.fields {
b.field(field.name, field.value, field.inline);
}
if let Some(image) = embed.image {
b.image(&image.url);
}
if let Some(timestamp) = embed.timestamp {
b.timestamp(timestamp);
}
if let Some(thumbnail) = embed.thumbnail {
b.thumbnail(&thumbnail.url);
}
if let Some(url) = embed.url {
b.url(&url);
}
if let Some(title) = embed.title {
b.title(&title);
}
if let Some(footer) = embed.footer {
b.footer(move |f| {
f.text(&footer.text);
if let Some(icon_url) = footer.icon_url {
f.icon_url(&icon_url);
}
f
});
}
b
}
}
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedAuthor(pub HashMap<&'static str, Value>);
impl CreateEmbedAuthor {
pub fn icon_url<S: ToString>(&mut self, icon_url: S) -> &mut Self {
self.0.insert("icon_url", Value::String(icon_url.to_string()));
self
}
pub fn name<S: ToString>(&mut self, name: S) -> &mut Self {
self.0.insert("name", Value::String(name.to_string()));
self
}
pub fn url<S: ToString>(&mut self, url: S) -> &mut Self {
self.0.insert("url", Value::String(url.to_string()));
self
}
}
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedFooter(pub HashMap<&'static str, Value>);
impl CreateEmbedFooter {
pub fn icon_url<S: ToString>(&mut self, icon_url: S) -> &mut Self {
self.0.insert("icon_url", Value::String(icon_url.to_string()));
self
}
pub fn text<S: ToString>(&mut self, text: S) -> &mut Self {
self.0.insert("text", Value::String(text.to_string()));
self
}
}
#[derive(Clone, Debug)]
pub struct Timestamp {
pub ts: String,
}
impl From<String> for Timestamp {
fn from(ts: String) -> Self {
Self {
ts,
}
}
}
impl<'a> From<&'a str> for Timestamp {
fn from(ts: &'a str) -> Self {
Self {
ts: ts.to_string(),
}
}
}
impl<'a, Tz: TimeZone> From<&'a DateTime<Tz>> for Timestamp
where Tz::Offset: Display {
fn from(dt: &'a DateTime<Tz>) -> Self {
Self {
ts: dt.to_rfc3339(),
}
}
}
#[cfg(test)]
mod test {
use crate::{model::channel::{Embed, EmbedField, EmbedFooter, EmbedImage, EmbedVideo},
utils::{self, Colour}};
use serde_json::{json, Value};
use super::CreateEmbed;
#[test]
fn test_from_embed() {
let embed = Embed {
author: None,
colour: Colour::new(0xFF0011),
description: Some("This is a test description".to_string()),
fields: vec![
EmbedField {
inline: false,
name: "a".to_string(),
value: "b".to_string(),
_nonexhaustive: (),
},
EmbedField {
inline: true,
name: "c".to_string(),
value: "z".to_string(),
_nonexhaustive: (),
},
],
footer: Some(EmbedFooter {
icon_url: Some("https://i.imgur.com/XfWpfCV.gif".to_string()),
proxy_icon_url: None,
text: "This is a hakase footer".to_string(),
_nonexhaustive: (),
}),
image: Some(EmbedImage {
height: 213,
proxy_url: "a".to_string(),
url: "https://i.imgur.com/XfWpfCV.gif".to_string(),
width: 224,
_nonexhaustive: (),
}),
kind: "rich".to_string(),
provider: None,
thumbnail: None,
timestamp: None,
title: Some("hakase".to_string()),
url: Some("https://i.imgur.com/XfWpfCV.gif".to_string()),
video: Some(EmbedVideo {
height: 213,
url: "https://i.imgur.com/XfWpfCV.mp4".to_string(),
width: 224,
_nonexhaustive: (),
}),
_nonexhaustive: (),
};
let mut builder = CreateEmbed::from(embed);
builder.colour(0xFF0011);
builder.description("This is a hakase description");
builder.image("https://i.imgur.com/XfWpfCV.gif");
builder.title("still a hakase");
builder.url("https://i.imgur.com/XfWpfCV.gif");
let built = Value::Object(utils::hashmap_to_json_map(builder.0));
let obj = json!({
"color": 0xFF0011,
"description": "This is a hakase description",
"title": "still a hakase",
"type": "rich",
"url": "https://i.imgur.com/XfWpfCV.gif",
"fields": [
{
"inline": false,
"name": "a",
"value": "b",
},
{
"inline": true,
"name": "c",
"value": "z",
},
],
"image": {
"url": "https://i.imgur.com/XfWpfCV.gif",
},
"footer": {
"text": "This is a hakase footer",
"icon_url": "https://i.imgur.com/XfWpfCV.gif",
}
});
assert_eq!(built, obj);
}
}