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 SlackCanvasId(pub String);
103
104#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
106pub struct SlackCanvasSectionId(pub String);
107
108#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
109pub struct SlackChannelId(pub String);
110
111#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
112pub struct SlackClientMessageId(pub String);
113
114impl SlackTextFormat for SlackChannelId {
115 fn to_slack_format(&self) -> String {
116 format!("<#{}>", self.value())
117 }
118}
119
120#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
121pub struct SlackChannelType(pub String);
122
123#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
124pub struct SlackConversationId(pub String);
125
126#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
127pub struct SlackActionId(pub String);
128
129#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
130pub struct SlackActionType(pub String);
131
132#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize, ValueStruct)]
133pub struct SlackUserId(pub String);
134
135#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize, ValueStruct)]
136pub struct SlackUserGroupId(pub String);
137
138impl SlackTextFormat for SlackUserId {
139 fn to_slack_format(&self) -> String {
140 format!("<@{}>", self.value())
141 }
142}
143
144impl SlackTextFormat for SlackUserGroupId {
145 fn to_slack_format(&self) -> String {
146 format!("<!subteam^{}>", self.value())
147 }
148}
149
150#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize, ValueStruct)]
151pub struct SlackBotId(pub String);
152
153#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
154pub struct SlackDateTime(#[serde(with = "unix_seconds")] pub SlackUtcDateTime);
155
156impl SlackDateTime {
157 pub fn now() -> Self {
158 Self(datetime::now())
159 }
160}
161
162#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
163pub struct SlackDate(pub String);
164
165impl SlackDate {
166 pub fn to_naive_date(&self) -> Option<SlackCivilDate> {
167 datetime::parse_civil_date(self.value())
168 }
169}
170
171#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
172pub struct SlackLocale(pub String);
173
174#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
175pub struct SlackCursorId(pub String);
176
177#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
178pub struct SlackColor(pub String);
179
180#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
181pub struct SlackCallbackId(pub String);
182
183#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
184pub struct SlackTriggerId(pub String);
185
186#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
187pub struct SlackViewId(pub String);
188
189#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
190pub struct SlackCommandId(pub String);
191
192#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
193pub struct SlackClientId(pub String);
194
195#[derive(Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
196pub struct SlackClientSecret(pub String);
197
198impl fmt::Debug for SlackClientSecret {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
200 write!(f, "SlackClientSecret(len:{})", self.value().len())
201 }
202}
203
204#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
205pub struct SlackApiTokenScope(pub String);
206
207#[derive(Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
208pub struct SlackVerificationToken(pub String);
209
210impl fmt::Debug for SlackVerificationToken {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
212 write!(f, "SlackVerificationToken(len:{})", self.value().len())
213 }
214}
215
216#[derive(Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
217pub struct SlackSigningSecret(pub String);
218
219impl fmt::Debug for SlackSigningSecret {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
221 write!(f, "SlackSigningSecret(len:{})", self.value().len())
222 }
223}
224
225#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
226pub struct EmailAddress(pub String);
227
228#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
229pub struct PhoneNumber(pub String);
230
231#[serde_as]
232#[skip_serializing_none]
233#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
234pub struct SlackResponseMetadata {
235 #[serde(default)]
236 #[serde_as(as = "serde_with::NoneAsEmptyString")]
237 pub next_cursor: Option<SlackCursorId>,
238}
239
240#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
241pub enum SlackConversationType {
242 #[serde(rename = "im")]
243 Im,
244 #[serde(rename = "mpim")]
245 Mpim,
246 #[serde(rename = "private_channel")]
247 Private,
248 #[serde(rename = "public_channel")]
249 Public,
250}
251
252#[allow(clippy::to_string_trait_impl)]
253impl ToString for SlackConversationType {
254 fn to_string(&self) -> String {
255 match self {
256 SlackConversationType::Im => "im".into(),
257 SlackConversationType::Mpim => "mpim".into(),
258 SlackConversationType::Private => "private_channel".into(),
259 SlackConversationType::Public => "public_channel".into(),
260 }
261 }
262}
263
264#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
265pub struct SlackResponseUrl(pub Url);
266
267#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
268pub struct SlackWebSocketsUrl(pub Url);
269
270impl SlackWebSocketsUrl {
271 pub fn to_debug_url(&self) -> Self {
272 Self(Url::parse(format!("{}&debug_reconnects=true", self.value()).as_str()).unwrap())
273 }
274}
275
276#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
277pub struct SlackTeamUrl(pub Url);
278
279#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
280pub struct SlackUnfurlId(pub String);
281
282#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
283pub struct SlackMimeType(pub String);
284
285#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
286pub struct SlackEmoji(pub String);
287
288impl SlackEmoji {
289 pub const SMILE: &'static str = ":smile:";
290 pub const SPEECH_BALLOON: &'static str = ":speech_balloon:";
291 pub const HEAVY_CHECK_MARK: &'static str = ":heavy_check_mark:";
292}
293
294#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
295pub enum SlackShortcutType {
296 #[serde(rename = "message")]
297 Message,
298 #[serde(rename = "global")]
299 Global,
300}
301
302#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
303pub struct SlackEventType(pub String);
304
305#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, ValueStruct)]
306pub struct SlackAppContextEntityType(pub String);
307
308#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize, ValueStruct)]
309pub struct SlackEnterpriseId(pub String);
310
311#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize, ValueStruct)]
315pub struct SlackRelaxedUrl(pub String);
316
317impl SlackRelaxedUrl {
318 pub fn to_url(&self) -> Result<Url, url::ParseError> {
319 self.try_into()
320 }
321}
322
323impl TryFrom<&SlackRelaxedUrl> for Url {
324 type Error = url::ParseError;
325
326 fn try_from(relaxed_url: &SlackRelaxedUrl) -> Result<Self, Self::Error> {
327 Url::parse(relaxed_url.value())
328 }
329}
330
331impl TryFrom<SlackRelaxedUrl> for Url {
332 type Error = url::ParseError;
333
334 fn try_from(relaxed_url: SlackRelaxedUrl) -> Result<Self, Self::Error> {
335 Url::parse(relaxed_url.value())
336 }
337}
338
339impl From<Url> for SlackRelaxedUrl {
340 fn from(url: Url) -> Self {
341 SlackRelaxedUrl(url.to_string())
342 }
343}
344
345impl From<&Url> for SlackRelaxedUrl {
346 fn from(url: &Url) -> Self {
347 SlackRelaxedUrl(url.to_string())
348 }
349}
350
351#[cfg(test)]
352mod test {
353 use super::*;
354 use serde_json::json;
355
356 fn test_date_time() -> SlackUtcDateTime {
357 "2020-01-01T00:42:42Z".parse::<SlackUtcDateTime>().unwrap()
358 }
359
360 #[test]
361 fn test_slack_date_time() {
362 let dt = SlackDateTime(test_date_time());
363 let json = serde_json::to_value(&dt).unwrap();
364 assert_eq!(json.as_u64().unwrap(), 1577839362);
365
366 let parsed: SlackDateTime = serde_json::from_value(json!(1577839362)).unwrap();
367 assert_eq!(parsed, dt);
368 assert_eq!(serde_json::from_value::<SlackDateTime>(json).unwrap(), dt);
369 }
370
371 #[test]
372 fn test_slack_ts_to_date_time_with_micros() {
373 let ts = SlackTs("1577839362.000400".to_string());
374 let dt = ts.to_date_time_opt().unwrap();
375
376 assert_eq!(datetime::unix_seconds(&dt), 1577839362);
377 assert_eq!(
378 Some(dt),
379 datetime::from_unix_seconds_micros(1577839362, 400)
380 );
381 assert_ne!(Some(dt), datetime::from_unix_seconds_micros(1577839362, 0));
382 }
383
384 #[test]
385 fn test_slack_ts_to_date_time_without_fraction() {
386 let ts = SlackTs("1577839362".to_string());
387 let dt = ts.to_date_time_opt().unwrap();
388
389 assert_eq!(datetime::unix_seconds(&dt), 1577839362);
390 assert_eq!(Some(dt), datetime::from_unix_seconds_micros(1577839362, 0));
391 }
392
393 #[test]
394 fn test_slack_ts_to_date_time_invalid() {
395 assert_eq!(SlackTs("garbage".to_string()).to_date_time_opt(), None);
396 }
397
398 #[test]
399 fn test_slack_date_to_naive_date() {
400 assert!(SlackDate("2020-01-01".to_string())
401 .to_naive_date()
402 .is_some());
403 assert_eq!(SlackDate("2020-13-01".to_string()).to_naive_date(), None);
404 assert_eq!(SlackDate("garbage".to_string()).to_naive_date(), None);
405 }
406}