Skip to main content

slack_morphism/models/blocks/
datetime.rs

1use crate::SlackTextFormat;
2
3pub enum SlackDateTimeFormats {
4    DateNum,
5    Date,
6    DateShort,
7    DateLong,
8    DatePretty,
9    DateShortPretty,
10    DateLongPretty,
11    Time,
12    TimeSecs,
13}
14
15#[allow(clippy::to_string_trait_impl)]
16impl ToString for SlackDateTimeFormats {
17    fn to_string(&self) -> String {
18        match self {
19            SlackDateTimeFormats::DateNum => "{date_num}".into(),
20            SlackDateTimeFormats::Date => "{date}".into(),
21            SlackDateTimeFormats::DateShort => "{date_short}".into(),
22            SlackDateTimeFormats::DateLong => "{date_long}".into(),
23            SlackDateTimeFormats::DatePretty => "{date_pretty}".into(),
24            SlackDateTimeFormats::DateShortPretty => "{date_short_pretty}".into(),
25            SlackDateTimeFormats::DateLongPretty => "{date_long_pretty}".into(),
26            SlackDateTimeFormats::Time => "{time}".into(),
27            SlackDateTimeFormats::TimeSecs => "{time_secs}".into(),
28        }
29    }
30}
31
32fn fmt_slack_date_parts(
33    timestamp: i64,
34    fallback: &str,
35    token_string: &str,
36    link: Option<&String>,
37) -> String {
38    let link_part = link
39        .map(|value| format!("^{value}"))
40        .unwrap_or_else(|| "".into());
41    format!(
42        "<!date^{timestamp}^{token_string}{link_part}|{fallback}>",
43        timestamp = timestamp,
44        token_string = token_string,
45        link_part = link_part,
46        fallback = fallback
47    )
48}
49
50#[cfg(not(feature = "obsolete-chrono"))]
51pub fn fmt_slack_date(
52    date: &crate::SlackUtcDateTime,
53    token_string: &str,
54    link: Option<&String>,
55) -> String {
56    use crate::models::common::datetime::{to_rfc2822, unix_seconds};
57
58    fmt_slack_date_parts(
59        unix_seconds(date),
60        to_rfc2822(date).as_str(),
61        token_string,
62        link,
63    )
64}
65
66#[cfg(feature = "obsolete-chrono")]
67pub fn fmt_slack_date<TZ: chrono::TimeZone>(
68    date: &chrono::DateTime<TZ>,
69    token_string: &str,
70    link: Option<&String>,
71) -> String
72where
73    <TZ as chrono::offset::TimeZone>::Offset: std::fmt::Display,
74{
75    fmt_slack_date_parts(
76        date.timestamp(),
77        date.to_rfc2822().as_str(),
78        token_string,
79        link,
80    )
81}
82
83#[cfg(feature = "obsolete-chrono")]
84impl<TZ: chrono::TimeZone> SlackTextFormat for chrono::DateTime<TZ>
85where
86    <TZ as chrono::offset::TimeZone>::Offset: std::fmt::Display,
87{
88    fn to_slack_format(&self) -> String {
89        fmt_slack_date(
90            self,
91            SlackDateTimeFormats::DatePretty.to_string().as_str(),
92            None,
93        )
94    }
95}
96
97#[cfg(not(feature = "obsolete-chrono"))]
98impl SlackTextFormat for jiff::Timestamp {
99    fn to_slack_format(&self) -> String {
100        fmt_slack_date(
101            self,
102            SlackDateTimeFormats::DatePretty.to_string().as_str(),
103            None,
104        )
105    }
106}
107
108#[cfg(not(feature = "obsolete-chrono"))]
109impl SlackTextFormat for jiff::Zoned {
110    fn to_slack_format(&self) -> String {
111        let fallback = jiff::fmt::rfc2822::to_string(self).unwrap_or_else(|_| self.to_string());
112        fmt_slack_date_parts(
113            self.timestamp().as_second(),
114            fallback.as_str(),
115            SlackDateTimeFormats::DatePretty.to_string().as_str(),
116            None,
117        )
118    }
119}
120
121#[cfg(test)]
122mod test {
123    use super::*;
124    use crate::prelude::SlackUtcDateTime;
125
126    /// Both the jiff and the chrono backend render RFC 2822 identically here,
127    /// so the expected fallback is the same in both feature modes.
128    const EXPECTED_FALLBACK: &str = "Wed, 1 Jan 2020 00:42:42 +0000";
129
130    #[test]
131    fn test_fmt_slack_date() {
132        let dt = "2020-01-01T00:42:42Z".parse::<SlackUtcDateTime>().unwrap();
133
134        assert_eq!(
135            fmt_slack_date(
136                &dt,
137                SlackDateTimeFormats::DatePretty.to_string().as_str(),
138                None
139            ),
140            format!("<!date^1577839362^{{date_pretty}}|{EXPECTED_FALLBACK}>")
141        );
142    }
143
144    #[test]
145    fn test_fmt_slack_date_with_link() {
146        let dt = "2020-01-01T00:42:42Z".parse::<SlackUtcDateTime>().unwrap();
147
148        assert_eq!(
149            fmt_slack_date(
150                &dt,
151                SlackDateTimeFormats::DateNum.to_string().as_str(),
152                Some(&"https://example.net/".to_string())
153            ),
154            format!("<!date^1577839362^{{date_num}}^https://example.net/|{EXPECTED_FALLBACK}>")
155        );
156    }
157}