1use rsb_derive::Builder;
2use rvstruct::ValueStruct;
3use serde::{Deserialize, Serialize};
4use serde_with::{serde_as, skip_serializing_none};
5use std::hash::Hash;
6use std::*;
7use url::Url;
8
9pub(crate) mod datetime;
10
11pub use datetime::*;
12
13use datetime::unix_seconds;
14
15mod user;
16
17pub use user::*;
18
19mod team;
20
21pub use team::*;
22
23mod channel;
24
25pub use channel::*;
26
27mod pin;
28
29pub use pin::*;
30
31mod reaction;
32
33pub use reaction::*;
34
35mod star;
36
37pub use star::*;
38
39mod bot;
40
41pub use bot::*;
42
43mod icon;
44
45pub use icon::*;
46
47mod formatters;
48
49pub use formatters::*;
50
51mod emoji;
52
53pub use emoji::*;
54mod assistant;
55pub use assistant::*;
56
57#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
58pub struct SlackTs(pub String);
59
60impl SlackTs {
61 pub fn to_date_time_opt(&self) -> Option<SlackUtcDateTime> {
66 let value = self.value();
67 let (seconds_part, micros_part) = match value.split_once('.') {
68 Some((seconds, micros)) => (seconds, Some(micros)),
69 None => (value.as_str(), None),
70 };
71
72 let seconds: i64 = seconds_part.parse().ok()?;
73
74 let micros: u32 = match micros_part {
75 Some(micros_part) => {
76 let mut digits: String = micros_part.chars().take(6).collect();
77 while digits.len() < 6 {
78 digits.push('0');
79 }
80 digits.parse().ok()?
81 }
82 None => 0,
83 };
84
85 datetime::from_unix_seconds_micros(seconds, micros)
86 }
87}
88
89#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
90pub struct SlackScheduledMid(pub String);
91
92#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
93pub struct SlackTeamId(pub String);
94
95#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
96pub struct SlackEnterpriseSubteamId(pub String);
97
98#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
99pub struct SlackAppId(pub String);
100
101#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
102pub struct SlackChannelId(pub String);
103
104#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
105pub struct SlackClientMessageId(pub String);
106
107impl SlackTextFormat for SlackChannelId {
108 fn to_slack_format(&self) -> String {
109 format!("<#{}>", self.value())
110 }
111}
112
113#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
114pub struct SlackChannelType(pub String);
115
116#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
117pub struct SlackConversationId(pub String);
118
119#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
120pub struct SlackActionId(pub String);
121
122#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
123pub struct SlackActionType(pub String);
124
125#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize, ValueStruct)]
126pub struct SlackUserId(pub String);
127
128#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize, ValueStruct)]
129pub struct SlackUserGroupId(pub String);
130
131impl SlackTextFormat for SlackUserId {
132 fn to_slack_format(&self) -> String {
133 format!("<@{}>", self.value())
134 }
135}
136
137impl SlackTextFormat for SlackUserGroupId {
138 fn to_slack_format(&self) -> String {
139 format!("<!subteam^{}>", self.value())
140 }
141}
142
143#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize, ValueStruct)]
144pub struct SlackBotId(pub String);
145
146#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
147pub struct SlackDateTime(#[serde(with = "unix_seconds")] pub SlackUtcDateTime);
148
149impl SlackDateTime {
150 pub fn now() -> Self {
151 Self(datetime::now())
152 }
153}
154
155#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
156pub struct SlackDate(pub String);
157
158impl SlackDate {
159 pub fn to_naive_date(&self) -> Option<SlackCivilDate> {
160 datetime::parse_civil_date(self.value())
161 }
162}
163
164#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
165pub struct SlackLocale(pub String);
166
167#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
168pub struct SlackCursorId(pub String);
169
170#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
171pub struct SlackColor(pub String);
172
173#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
174pub struct SlackCallbackId(pub String);
175
176#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
177pub struct SlackTriggerId(pub String);
178
179#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
180pub struct SlackViewId(pub String);
181
182#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
183pub struct SlackCommandId(pub String);
184
185#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
186pub struct SlackClientId(pub String);
187
188#[derive(Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
189pub struct SlackClientSecret(pub String);
190
191impl fmt::Debug for SlackClientSecret {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
193 write!(f, "SlackClientSecret(len:{})", self.value().len())
194 }
195}
196
197#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
198pub struct SlackApiTokenScope(pub String);
199
200#[derive(Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
201pub struct SlackVerificationToken(pub String);
202
203impl fmt::Debug for SlackVerificationToken {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
205 write!(f, "SlackVerificationToken(len:{})", self.value().len())
206 }
207}
208
209#[derive(Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
210pub struct SlackSigningSecret(pub String);
211
212impl fmt::Debug for SlackSigningSecret {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
214 write!(f, "SlackSigningSecret(len:{})", self.value().len())
215 }
216}
217
218#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
219pub struct EmailAddress(pub String);
220
221#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
222pub struct PhoneNumber(pub String);
223
224#[serde_as]
225#[skip_serializing_none]
226#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
227pub struct SlackResponseMetadata {
228 #[serde(default)]
229 #[serde_as(as = "serde_with::NoneAsEmptyString")]
230 pub next_cursor: Option<SlackCursorId>,
231}
232
233#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
234pub enum SlackConversationType {
235 #[serde(rename = "im")]
236 Im,
237 #[serde(rename = "mpim")]
238 Mpim,
239 #[serde(rename = "private_channel")]
240 Private,
241 #[serde(rename = "public_channel")]
242 Public,
243}
244
245#[allow(clippy::to_string_trait_impl)]
246impl ToString for SlackConversationType {
247 fn to_string(&self) -> String {
248 match self {
249 SlackConversationType::Im => "im".into(),
250 SlackConversationType::Mpim => "mpim".into(),
251 SlackConversationType::Private => "private_channel".into(),
252 SlackConversationType::Public => "public_channel".into(),
253 }
254 }
255}
256
257#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
258pub struct SlackResponseUrl(pub Url);
259
260#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
261pub struct SlackWebSocketsUrl(pub Url);
262
263impl SlackWebSocketsUrl {
264 pub fn to_debug_url(&self) -> Self {
265 Self(Url::parse(format!("{}&debug_reconnects=true", self.value()).as_str()).unwrap())
266 }
267}
268
269#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
270pub struct SlackTeamUrl(pub Url);
271
272#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
273pub struct SlackUnfurlId(pub String);
274
275#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
276pub struct SlackMimeType(pub String);
277
278#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
279pub struct SlackEmoji(pub String);
280
281impl SlackEmoji {
282 pub const SMILE: &'static str = ":smile:";
283 pub const SPEECH_BALLOON: &'static str = ":speech_balloon:";
284 pub const HEAVY_CHECK_MARK: &'static str = ":heavy_check_mark:";
285}
286
287#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
288pub enum SlackShortcutType {
289 #[serde(rename = "message")]
290 Message,
291 #[serde(rename = "global")]
292 Global,
293}
294
295#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
296pub struct SlackEventType(pub String);
297
298#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize, ValueStruct)]
299pub struct SlackEnterpriseId(pub String);
300
301#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize, ValueStruct)]
305pub struct SlackRelaxedUrl(pub String);
306
307impl SlackRelaxedUrl {
308 pub fn to_url(&self) -> Result<Url, url::ParseError> {
309 self.try_into()
310 }
311}
312
313impl TryFrom<&SlackRelaxedUrl> for Url {
314 type Error = url::ParseError;
315
316 fn try_from(relaxed_url: &SlackRelaxedUrl) -> Result<Self, Self::Error> {
317 Url::parse(relaxed_url.value())
318 }
319}
320
321impl TryFrom<SlackRelaxedUrl> for Url {
322 type Error = url::ParseError;
323
324 fn try_from(relaxed_url: SlackRelaxedUrl) -> Result<Self, Self::Error> {
325 Url::parse(relaxed_url.value())
326 }
327}
328
329impl From<Url> for SlackRelaxedUrl {
330 fn from(url: Url) -> Self {
331 SlackRelaxedUrl(url.to_string())
332 }
333}
334
335impl From<&Url> for SlackRelaxedUrl {
336 fn from(url: &Url) -> Self {
337 SlackRelaxedUrl(url.to_string())
338 }
339}
340
341#[cfg(test)]
342mod test {
343 use super::*;
344 use serde_json::json;
345
346 fn test_date_time() -> SlackUtcDateTime {
347 "2020-01-01T00:42:42Z".parse::<SlackUtcDateTime>().unwrap()
348 }
349
350 #[test]
351 fn test_slack_date_time() {
352 let dt = SlackDateTime(test_date_time());
353 let json = serde_json::to_value(&dt).unwrap();
354 assert_eq!(json.as_u64().unwrap(), 1577839362);
355
356 let parsed: SlackDateTime = serde_json::from_value(json!(1577839362)).unwrap();
357 assert_eq!(parsed, dt);
358 assert_eq!(serde_json::from_value::<SlackDateTime>(json).unwrap(), dt);
359 }
360
361 #[test]
362 fn test_slack_ts_to_date_time_with_micros() {
363 let ts = SlackTs("1577839362.000400".to_string());
364 let dt = ts.to_date_time_opt().unwrap();
365
366 assert_eq!(datetime::unix_seconds(&dt), 1577839362);
367 assert_eq!(
368 Some(dt),
369 datetime::from_unix_seconds_micros(1577839362, 400)
370 );
371 assert_ne!(Some(dt), datetime::from_unix_seconds_micros(1577839362, 0));
372 }
373
374 #[test]
375 fn test_slack_ts_to_date_time_without_fraction() {
376 let ts = SlackTs("1577839362".to_string());
377 let dt = ts.to_date_time_opt().unwrap();
378
379 assert_eq!(datetime::unix_seconds(&dt), 1577839362);
380 assert_eq!(Some(dt), datetime::from_unix_seconds_micros(1577839362, 0));
381 }
382
383 #[test]
384 fn test_slack_ts_to_date_time_invalid() {
385 assert_eq!(SlackTs("garbage".to_string()).to_date_time_opt(), None);
386 }
387
388 #[test]
389 fn test_slack_date_to_naive_date() {
390 assert!(SlackDate("2020-01-01".to_string())
391 .to_naive_date()
392 .is_some());
393 assert_eq!(SlackDate("2020-13-01".to_string()).to_naive_date(), None);
394 assert_eq!(SlackDate("garbage".to_string()).to_naive_date(), None);
395 }
396}