use std::default::Default;
use std::fmt::{self, Write, Display};
use std::ops::Add;
use ::model::{ChannelId, Emoji, Mentionable, RoleId, UserId};
#[derive(Clone, Debug, Default)]
pub struct MessageBuilder(pub String);
impl MessageBuilder {
pub fn new() -> MessageBuilder {
MessageBuilder::default()
}
pub fn build(self) -> String {
self.0
}
pub fn channel<C: Into<ChannelId>>(mut self, channel: C) -> Self {
let _ = write!(self.0, "{}", channel.into().mention());
self
}
pub fn emoji(mut self, emoji: Emoji) -> Self {
let _ = write!(self.0, "{}", emoji);
self
}
pub fn mention<M: Mentionable>(mut self, item: M) -> Self {
let _ = write!(self.0, "{}", item.mention());
self
}
pub fn push<T: Into<Content>>(mut self, content: T) -> Self {
let content = content.into();
self.0.push_str(&content.to_string());
self
}
pub fn push_codeblock(mut self, content: &str, language: Option<&str>) -> Self {
self.0.push_str("```");
if let Some(language) = language {
self.0.push_str(language);
}
self.0.push('\n');
self.0.push_str(content);
self.0.push_str("\n```");
self
}
pub fn push_mono(mut self, content: &str) -> Self {
self.0.push('`');
self.0.push_str(content);
self.0.push('`');
self
}
pub fn push_italic(mut self, content: &str) -> Self {
self.0.push('_');
self.0.push_str(content);
self.0.push('_');
self
}
pub fn push_bold(mut self, content: &str) -> Self {
self.0.push_str("**");
self.0.push_str(content);
self.0.push_str("**");
self
}
pub fn push_underline(mut self, content: &str) -> Self {
self.0.push_str("__");
self.0.push_str(content);
self.0.push_str("__");
self
}
pub fn push_strike(mut self, content: &str) -> Self {
self.0.push_str("~~");
self.0.push_str(content);
self.0.push_str("~~");
self
}
pub fn push_line<T: Into<Content>>(mut self, content: T) -> Self {
self = self.push(content);
self.0.push('\n');
self
}
pub fn push_mono_line(mut self, content: &str) -> Self {
self = self.push_mono(content);
self.0.push('\n');
self
}
pub fn push_italic_line(mut self, content: &str) -> Self {
self = self.push_italic(content);
self.0.push('\n');
self
}
pub fn push_bold_line(mut self, content: &str) -> Self {
self = self.push_bold(content);
self.0.push('\n');
self
}
pub fn push_underline_line(mut self, content: &str) -> Self {
self = self.push_underline(content);
self.0.push('\n');
self
}
pub fn push_strike_line(mut self, content: &str) -> Self {
self = self.push_strike(content);
self.0.push('\n');
self
}
pub fn push_safe<T: Into<Content>>(mut self, content: T) -> Self {
let mut content: Content = content.into();
content.inner = normalize(&content.inner)
.replace('*', "\\*")
.replace('`', "\\`")
.replace('_', "\\_");
self.0.push_str(&content.to_string());
self
}
pub fn push_codeblock_safe(mut self, content: &str, language: Option<&str>)
-> Self {
let content = &normalize(content)
.replace("```", "'''");
self.0.push_str("```");
if let Some(language) = language {
self.0.push_str(language);
}
self.0.push('\n');
self.0.push_str(content);
self.0.push_str("```");
self
}
pub fn push_mono_safe(mut self, content: &str) -> Self {
self.0.push('`');
self.0.push_str(&normalize(content).replace('`', "'"));
self.0.push('`');
self
}
pub fn push_italic_safe(mut self, content: &str) -> Self {
self.0.push('_');
self.0.push_str(&normalize(content).replace('_', " "));
self.0.push('_');
self
}
pub fn push_bold_safe(mut self, content: &str) -> Self {
self.0.push_str("**");
self.0.push_str(&normalize(content).replace("**", " "));
self.0.push_str("**");
self
}
pub fn push_underline_safe(mut self, content: &str) -> Self {
self.0.push_str("__");
self.0.push_str(&normalize(content).replace("__", " "));
self.0.push_str("__");
self
}
pub fn push_strike_safe(mut self, content: &str) -> Self {
self.0.push_str("~~");
self.0.push_str(&normalize(content).replace("~~", " "));
self.0.push_str("~~");
self
}
pub fn push_line_safe<T: Into<Content>>(mut self, content: T) -> Self {
self = self.push_safe(content);
self.0.push('\n');
self
}
pub fn push_mono_line_safe(mut self, content: &str) -> Self {
self = self.push_mono_safe(content);
self.0.push('\n');
self
}
pub fn push_italic_line_safe(mut self, content: &str) -> Self {
self = self.push_italic_safe(content);
self.0.push('\n');
self
}
pub fn push_bold_line_safe(mut self, content: &str) -> Self {
self = self.push_bold_safe(content);
self.0.push('\n');
self
}
pub fn push_underline_line_safe(mut self, content: &str) -> Self {
self = self.push_underline_safe(content);
self.0.push('\n');
self
}
pub fn push_strike_line_safe(mut self, content: &str) -> Self {
self = self.push_strike_safe(content);
self.0.push('\n');
self
}
pub fn role<R: Into<RoleId>>(mut self, role: R) -> Self {
let _ = write!(self.0, "{}", role.into().mention());
self
}
pub fn user<U: Into<UserId>>(mut self, user: U) -> Self {
let _ = write!(self.0, "{}", user.into().mention());
self
}
}
impl Display for MessageBuilder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
pub enum ContentModifier {
Italic,
Bold,
Strikethrough,
Code,
Underline,
}
#[derive(Default, Clone)]
pub struct Content {
pub italic: bool,
pub bold: bool,
pub strikethrough: bool,
pub inner: String,
pub code: bool,
pub underline: bool
}
impl<T: ToString> Add<T> for Content {
type Output = Content;
fn add(self, rhs: T) -> Content {
let mut nc = self.clone();
nc.inner = nc.inner + &rhs.to_string();
nc
}
}
impl<T: ToString> Add<T> for ContentModifier {
type Output = Content;
fn add(self, rhs: T) -> Content {
let mut nc = self.to_content();
nc.inner = nc.inner + &rhs.to_string();
nc
}
}
impl Add<ContentModifier> for Content {
type Output = Content;
fn add(self, rhs: ContentModifier) -> Content {
let mut nc = self.clone();
nc.apply(&rhs);
nc
}
}
impl Add<ContentModifier> for ContentModifier {
type Output = Content;
fn add(self, rhs: ContentModifier) -> Content {
let mut nc = self.to_content();
nc.apply(&rhs);
nc
}
}
impl ContentModifier {
fn to_content(&self) -> Content {
let mut nc = Content::default();
nc.apply(self);
nc
}
}
impl Content {
pub fn apply(&mut self, modifier: &ContentModifier) {
match *modifier {
ContentModifier::Italic => {
self.italic = true;
},
ContentModifier::Bold => {
self.bold = true;
},
ContentModifier::Strikethrough => {
self.strikethrough = true;
},
ContentModifier::Code => {
self.code = true;
},
ContentModifier::Underline => {
self.underline = true;
},
}
}
pub fn to_string(&self) -> String {
let capacity = self.inner.len()
+ if self.bold { 4 } else { 0 }
+ if self.italic { 2 } else { 0 }
+ if self.strikethrough { 4 } else { 0 }
+ if self.underline { 4 } else { 0 }
+ if self.code { 2 } else { 0 };
let mut new_str = String::with_capacity(capacity);
if self.bold {
new_str.push_str("**");
}
if self.italic {
new_str.push('*');
}
if self.strikethrough {
new_str.push_str("~~");
}
if self.underline {
new_str.push_str("__");
}
if self.code {
new_str.push('`');
}
new_str.push_str(&self.inner);
if self.code {
new_str.push('`');
}
if self.underline {
new_str.push_str("__");
}
if self.strikethrough {
new_str.push_str("~~");
}
if self.italic {
new_str.push('*');
}
if self.bold {
new_str.push_str("**");
}
new_str
}
}
impl From<ContentModifier> for Content {
fn from(cm: ContentModifier) -> Content {
cm.to_content()
}
}
impl<T: ToString> From<T> for Content {
fn from(stringer: T) -> Content {
Content {
italic: false,
bold: false,
strikethrough: false,
inner: stringer.to_string(),
code: false,
underline: false,
}
}
}
fn normalize(text: &str) -> String {
text.replace("discord.gg", "discord\u{2024}gg")
.replace("discord.me", "discord\u{2024}me")
.replace("discordlist.net", "discordlist\u{2024}net")
.replace("discordservers.com", "discordservers\u{2024}com")
.replace("discordapp.com/invite", "discordapp\u{2024}com/invite")
.replace('\u{202E}', " ") .replace('\u{200F}', " ") .replace('\u{202B}', " ") .replace('\u{200B}', " ") .replace('\u{200D}', " ") .replace('\u{200C}', " ") .replace("@everyone", "@\u{200B}everyone")
.replace("@here", "@\u{200B}here")
}