ferro_notifications/channels/
slack.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7pub struct SlackMessage {
8 pub text: String,
10 pub channel: Option<String>,
12 pub username: Option<String>,
14 pub icon_emoji: Option<String>,
16 pub icon_url: Option<String>,
18 pub attachments: Vec<SlackAttachment>,
20}
21
22#[derive(Debug, Clone, Default, Serialize, Deserialize)]
24pub struct SlackAttachment {
25 pub fallback: Option<String>,
27 pub color: Option<String>,
29 pub pretext: Option<String>,
31 pub author_name: Option<String>,
33 pub author_link: Option<String>,
35 pub title: Option<String>,
37 pub title_link: Option<String>,
39 pub text: Option<String>,
41 pub fields: Vec<SlackField>,
43 pub footer: Option<String>,
45 pub ts: Option<i64>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct SlackField {
52 pub title: String,
54 pub value: String,
56 pub short: bool,
58}
59
60impl SlackMessage {
61 pub fn new(text: impl Into<String>) -> Self {
63 Self {
64 text: text.into(),
65 ..Default::default()
66 }
67 }
68
69 pub fn channel(mut self, channel: impl Into<String>) -> Self {
71 self.channel = Some(channel.into());
72 self
73 }
74
75 pub fn username(mut self, username: impl Into<String>) -> Self {
77 self.username = Some(username.into());
78 self
79 }
80
81 pub fn icon_emoji(mut self, emoji: impl Into<String>) -> Self {
83 self.icon_emoji = Some(emoji.into());
84 self
85 }
86
87 pub fn icon_url(mut self, url: impl Into<String>) -> Self {
89 self.icon_url = Some(url.into());
90 self
91 }
92
93 pub fn attachment(mut self, attachment: SlackAttachment) -> Self {
95 self.attachments.push(attachment);
96 self
97 }
98}
99
100impl SlackAttachment {
101 pub fn new() -> Self {
103 Self::default()
104 }
105
106 pub fn color(mut self, color: impl Into<String>) -> Self {
108 self.color = Some(color.into());
109 self
110 }
111
112 pub fn title(mut self, title: impl Into<String>) -> Self {
114 self.title = Some(title.into());
115 self
116 }
117
118 pub fn title_link(mut self, link: impl Into<String>) -> Self {
120 self.title_link = Some(link.into());
121 self
122 }
123
124 pub fn text(mut self, text: impl Into<String>) -> Self {
126 self.text = Some(text.into());
127 self
128 }
129
130 pub fn field(
132 mut self,
133 title: impl Into<String>,
134 value: impl Into<String>,
135 short: bool,
136 ) -> Self {
137 self.fields.push(SlackField {
138 title: title.into(),
139 value: value.into(),
140 short,
141 });
142 self
143 }
144
145 pub fn footer(mut self, footer: impl Into<String>) -> Self {
147 self.footer = Some(footer.into());
148 self
149 }
150
151 pub fn timestamp(mut self, ts: i64) -> Self {
153 self.ts = Some(ts);
154 self
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_slack_message_builder() {
164 let msg = SlackMessage::new("Hello, Slack!")
165 .channel("#general")
166 .username("MyBot")
167 .icon_emoji(":robot_face:");
168
169 assert_eq!(msg.text, "Hello, Slack!");
170 assert_eq!(msg.channel, Some("#general".into()));
171 assert_eq!(msg.username, Some("MyBot".into()));
172 assert_eq!(msg.icon_emoji, Some(":robot_face:".into()));
173 }
174
175 #[test]
176 fn test_slack_attachment_builder() {
177 let attachment = SlackAttachment::new()
178 .color("good")
179 .title("Order Placed")
180 .text("Order #123 has been placed")
181 .field("Customer", "John Doe", true)
182 .field("Amount", "$99.99", true);
183
184 assert_eq!(attachment.color, Some("good".into()));
185 assert_eq!(attachment.title, Some("Order Placed".into()));
186 assert_eq!(attachment.fields.len(), 2);
187 assert_eq!(attachment.fields[0].title, "Customer");
188 assert!(attachment.fields[0].short);
189 }
190
191 #[test]
192 fn test_slack_message_with_attachment() {
193 let msg = SlackMessage::new("New order received").attachment(
194 SlackAttachment::new()
195 .color("#36a64f")
196 .title("Order Details"),
197 );
198
199 assert_eq!(msg.attachments.len(), 1);
200 }
201}