use crate::error::{Error, Result};
use crate::{HexColor, SlackText, SlackTime};
use chrono::NaiveDateTime;
use reqwest::Url;
use serde::Serialize;
use std::convert::TryInto;
#[derive(Serialize, Debug, Default, Clone, PartialEq)]
pub struct Attachment {
pub fallback: SlackText,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<SlackText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pretext: Option<SlackText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<HexColor>,
#[serde(skip_serializing_if = "Option::is_none")]
pub actions: Option<Vec<Action>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fields: Option<Vec<Field>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub author_name: Option<SlackText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub author_link: Option<Url>,
#[serde(skip_serializing_if = "Option::is_none")]
pub author_icon: Option<Url>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<SlackText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title_link: Option<Url>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_url: Option<Url>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thumb_url: Option<Url>,
#[serde(skip_serializing_if = "Option::is_none")]
pub footer: Option<SlackText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub footer_icon: Option<Url>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ts: Option<SlackTime>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mrkdwn_in: Option<Vec<Section>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub callback_id: Option<SlackText>,
}
#[derive(Eq, PartialEq, Copy, Clone, Serialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum Section {
Pretext,
Text,
Fields,
}
#[derive(Serialize, Debug, Clone, PartialEq)]
pub struct Action {
#[serde(rename = "type")]
pub action_type: String,
pub text: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub style: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
}
impl Action {
pub fn new<S: Into<String>>(
action_type: S,
text: S,
name: S,
style: Option<String>,
value: Option<String>,
) -> Action {
Action {
action_type: action_type.into(),
text: text.into(),
name: name.into(),
style,
value,
}
}
}
#[derive(Serialize, Debug, Clone, PartialEq)]
pub struct Field {
pub title: String,
pub value: SlackText,
#[serde(skip_serializing_if = "Option::is_none")]
pub short: Option<bool>,
}
impl Field {
pub fn new<S: Into<String>, ST: Into<SlackText>>(
title: S,
value: ST,
short: Option<bool>,
) -> Field {
Field {
title: title.into(),
value: value.into(),
short,
}
}
}
#[derive(Debug)]
pub struct AttachmentBuilder {
inner: Result<Attachment>,
}
impl AttachmentBuilder {
pub fn new<S: Into<SlackText>>(fallback: S) -> AttachmentBuilder {
AttachmentBuilder {
inner: Ok(Attachment {
fallback: fallback.into(),
..Default::default()
}),
}
}
pub fn text<S: Into<SlackText>>(self, text: S) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => {
inner.text = Some(text.into());
AttachmentBuilder { inner: Ok(inner) }
}
_ => self,
}
}
pub fn color<C: TryInto<HexColor, Error = Error>>(self, color: C) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => match color.try_into() {
Ok(c) => {
inner.color = Some(c);
AttachmentBuilder { inner: Ok(inner) }
}
Err(e) => AttachmentBuilder { inner: Err(e) },
},
_ => self,
}
}
pub fn pretext<S: Into<SlackText>>(self, pretext: S) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => {
inner.pretext = Some(pretext.into());
AttachmentBuilder { inner: Ok(inner) }
}
_ => self,
}
}
pub fn actions(self, actions: Vec<Action>) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => {
inner.actions = Some(actions);
AttachmentBuilder { inner: Ok(inner) }
}
_ => self,
}
}
pub fn fields(self, fields: Vec<Field>) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => {
inner.fields = Some(fields);
AttachmentBuilder { inner: Ok(inner) }
}
_ => self,
}
}
pub fn author_name<S: Into<SlackText>>(self, author_name: S) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => {
inner.author_name = Some(author_name.into());
AttachmentBuilder { inner: Ok(inner) }
}
_ => self,
}
}
url_builder_fn! {
author_link, AttachmentBuilder
}
url_builder_fn! {
author_icon, AttachmentBuilder
}
pub fn title<S: Into<SlackText>>(self, title: S) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => {
inner.title = Some(title.into());
AttachmentBuilder { inner: Ok(inner) }
}
_ => self,
}
}
pub fn callback_id<S: Into<SlackText>>(self, callback_id: S) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => {
inner.callback_id = Some(callback_id.into());
AttachmentBuilder { inner: Ok(inner) }
}
_ => self,
}
}
url_builder_fn! {
title_link, AttachmentBuilder
}
url_builder_fn! {
image_url, AttachmentBuilder
}
url_builder_fn! {
thumb_url, AttachmentBuilder
}
pub fn footer<S: Into<SlackText>>(self, footer: S) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => {
inner.footer = Some(footer.into());
AttachmentBuilder { inner: Ok(inner) }
}
_ => self,
}
}
url_builder_fn! {
footer_icon, AttachmentBuilder
}
pub fn ts(self, time: &NaiveDateTime) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => {
inner.ts = Some(SlackTime::new(time));
AttachmentBuilder { inner: Ok(inner) }
}
_ => self,
}
}
pub fn markdown_in<'a, I: IntoIterator<Item = &'a Section>>(
self,
sections: I,
) -> AttachmentBuilder {
match self.inner {
Ok(mut inner) => {
inner.mrkdwn_in = Some(sections.into_iter().cloned().collect());
AttachmentBuilder { inner: Ok(inner) }
}
_ => self,
}
}
pub fn build(self) -> Result<Attachment> {
match self.inner {
Ok(mut inner) => {
if inner.text.is_none() {
inner.text = Some(inner.fallback.clone())
}
Ok(inner)
}
_ => self.inner,
}
}
}