slack_hook2/
payload.rs

1use crate::{Attachment, SlackText};
2use crate::helper::bool_to_u8;
3use crate::error::Result;
4use serde::{Serialize, Serializer};
5use url::Url;
6
7/// Payload to send to slack
8/// https://api.slack.com/incoming-webhooks
9/// https://api.slack.com/methods/chat.postMessage
10#[derive(Serialize, Debug, Default)]
11pub struct Payload {
12    /// text to send
13    /// despite `text` stated as required, it does not seem to be
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub text: Option<SlackText>,
16    /// channel to send payload to
17    /// note: if not provided, this will default to channel
18    /// setup in slack
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub channel: Option<String>,
21    /// username override
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub username: Option<String>,
24    /// specific url for icon
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub icon_url: Option<Url>,
27    /// emjoi for icon
28    /// https://api.slack.com/methods/emoji.list
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub icon_emoji: Option<String>,
31    /// attachments to send
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub attachments: Option<Vec<Attachment>>,
34    /// whether slack will try to fetch links and create an attachment
35    /// https://api.slack.com/docs/unfurling
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub unfurl_links: Option<bool>,
38    /// Pass false to disable unfurling of media content
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub unfurl_media: Option<bool>,
41    /// find and link channel names and usernames
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub link_names: Option<u8>,
44    /// Change how messages are treated.
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub parse: Option<Parse>,
47}
48
49/// Change how messages are treated.
50#[derive(Debug)]
51pub enum Parse {
52    /// Full
53    Full,
54    /// None
55    None,
56}
57
58impl Serialize for Parse {
59    fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
60    where
61        S: Serializer,
62    {
63        let st = match *self {
64            Parse::Full => "full",
65            Parse::None => "none",
66        };
67        serializer.serialize_str(st)
68    }
69}
70/// `PayloadBuilder` is used to build a `Payload`
71#[derive(Debug)]
72pub struct PayloadBuilder {
73    inner: Result<Payload>,
74}
75
76impl Default for PayloadBuilder {
77    fn default() -> PayloadBuilder {
78        PayloadBuilder {
79            inner: Ok(Default::default()),
80        }
81    }
82}
83
84impl PayloadBuilder {
85    /// Make a new `PayloadBuilder`
86    pub fn new() -> PayloadBuilder {
87        Default::default()
88    }
89
90    /// Set the text
91    pub fn text<S: Into<SlackText>>(self, text: S) -> PayloadBuilder {
92        match self.inner {
93            Ok(mut inner) => {
94                inner.text = Some(text.into());
95                PayloadBuilder { inner: Ok(inner) }
96            }
97            _ => self,
98        }
99    }
100
101    /// Set the channel
102    pub fn channel<S: Into<String>>(self, channel: S) -> PayloadBuilder {
103        match self.inner {
104            Ok(mut inner) => {
105                inner.channel = Some(channel.into());
106                PayloadBuilder { inner: Ok(inner) }
107            }
108            _ => self,
109        }
110    }
111
112    /// Set the username
113    pub fn username<S: Into<String>>(self, username: S) -> PayloadBuilder {
114        match self.inner {
115            Ok(mut inner) => {
116                inner.username = Some(username.into());
117                PayloadBuilder { inner: Ok(inner) }
118            }
119            _ => self,
120        }
121    }
122
123    /// Set the icon_emoji
124    pub fn icon_emoji<S: Into<String>>(self, icon_emoji: S) -> PayloadBuilder {
125        match self.inner {
126            Ok(mut inner) => {
127                inner.icon_emoji = Some(icon_emoji.into());
128                PayloadBuilder { inner: Ok(inner) }
129            }
130            _ => self,
131        }
132    }
133
134    url_builder_fn! {
135        /// Set the icon_url
136        icon_url, PayloadBuilder
137    }
138
139    /// Set the attachments
140    pub fn attachments(self, attachments: Vec<Attachment>) -> PayloadBuilder {
141        match self.inner {
142            Ok(mut inner) => {
143                inner.attachments = Some(attachments);
144                PayloadBuilder { inner: Ok(inner) }
145            }
146            _ => self,
147        }
148    }
149
150    /// whether slack will try to fetch links and create an attachment
151    /// https://api.slack.com/docs/unfurling
152    pub fn unfurl_links(self, b: bool) -> PayloadBuilder {
153        match self.inner {
154            Ok(mut inner) => {
155                inner.unfurl_links = Some(b);
156                PayloadBuilder { inner: Ok(inner) }
157            }
158            _ => self,
159        }
160    }
161
162    /// Pass false to disable unfurling of media content
163    pub fn unfurl_media(self, b: bool) -> PayloadBuilder {
164        match self.inner {
165            Ok(mut inner) => {
166                inner.unfurl_media = Some(b);
167                PayloadBuilder { inner: Ok(inner) }
168            }
169            _ => self,
170        }
171    }
172
173    /// Find and link channel names and usernames.
174    // NOTE: The Slack API doesn't seem to actually require setting `link_names` to 1, any value
175    // seems to work. However, to be faithful to their spec, we will keep the `bool_to_u8` fn
176    // around.
177    pub fn link_names(self, b: bool) -> PayloadBuilder {
178        match self.inner {
179            Ok(mut inner) => {
180                inner.link_names = Some(bool_to_u8(b));
181                PayloadBuilder { inner: Ok(inner) }
182            }
183            _ => self,
184        }
185    }
186
187    /// Change how messages are treated.
188    pub fn parse(self, p: Parse) -> PayloadBuilder {
189        match self.inner {
190            Ok(mut inner) => {
191                inner.parse = Some(p);
192                PayloadBuilder { inner: Ok(inner) }
193            }
194            _ => self,
195        }
196    }
197
198    /// Attempt to build the `Payload`
199    pub fn build(self) -> Result<Payload> {
200        self.inner
201    }
202}