Skip to main content

ex_cli/cli/
recent.rs

1use crate::error::{MyError, MyResult};
2use crate::util::calendar::Calendar;
3use chrono::{DateTime, Duration, TimeZone, Utc};
4use regex::Regex;
5use std::ops::Sub;
6use termcolor::WriteColor;
7
8#[derive(Clone, Copy, Debug, PartialEq)]
9pub enum PeriodKind {
10    Sec(i64),
11    Min(i64),
12    Hour(i64),
13    Day(i64),
14    Week(i64),
15    Month(i64),
16    Year(i64),
17}
18
19impl PeriodKind {
20    pub fn print_time<W: WriteColor>(&self, writer: &mut W, padding: bool) -> MyResult<()> {
21        match self {
22            PeriodKind::Sec(count) => write!(writer, "{:2} sec", count)?,
23            PeriodKind::Min(count) => write!(writer, "{:2} min", count)?,
24            PeriodKind::Hour(count) => write!(writer, "{:2} hour", count)?,
25            PeriodKind::Day(count) => write!(writer, "{:2} day", count)?,
26            PeriodKind::Week(count) => write!(writer, "{:2} week", count)?,
27            PeriodKind::Month(count) => write!(writer, "{:2} month", count)?,
28            PeriodKind::Year(count) => write!(writer, "{:2} year", count)?,
29        }
30        if padding {
31            match self {
32                PeriodKind::Sec(_) => write!(writer, "  ")?,
33                PeriodKind::Min(_) => write!(writer, "  ")?,
34                PeriodKind::Hour(_) => write!(writer, " ")?,
35                PeriodKind::Day(_) => write!(writer, "  ")?,
36                PeriodKind::Week(_) => write!(writer, " ")?,
37                PeriodKind::Month(_) => (),
38                PeriodKind::Year(_) => write!(writer, " ")?,
39            }
40        }
41        Ok(())
42    }
43
44    fn subtract_from<Tz: TimeZone>(&self, time: &DateTime<Utc>, zone: &Tz) -> DateTime<Utc> {
45        match self {
46            Self::Sec(count) => time.sub(Duration::seconds(*count)),
47            Self::Min(count) => time.sub(Duration::minutes(*count)),
48            Self::Hour(count) => time.sub(Duration::hours(*count)),
49            Self::Day(count) => time.sub(Duration::days(*count)),
50            Self::Week(count) => time.sub(Duration::weeks(*count)),
51            Self::Month(count) => Calendar::from_time(time, zone).subtract_month(*count, zone),
52            Self::Year(count) => Calendar::from_time(time, zone).subtract_year(*count, zone),
53        }
54    }
55}
56
57#[derive(Clone, Copy, Debug, PartialEq)]
58pub enum RecentKind<T> {
59    None,
60    Before(T),
61    After(T),
62}
63
64impl<T> RecentKind<T> {
65    pub fn to_option(self) -> Option<T> {
66        match self {
67            Self::None => None,
68            Self::Before(value) => Some(value),
69            Self::After(value) => Some(value),
70        }
71    }
72}
73
74impl RecentKind<PeriodKind> {
75    pub fn from_str(value: &str) -> MyResult<Self> {
76        let re = Regex::new(r"^([ymwdhHMS])(\d+)?([+-])?$")?;
77        match re.captures(value) {
78            Some(captures) => {
79                let count = captures
80                    .get(2)
81                    .map(|x| x.as_str())
82                    .map(|x| x.parse())
83                    .unwrap_or(Ok(1))?;
84                let before = captures
85                    .get(3)
86                    .map(|x| x.as_str())
87                    .map(|x| x == "-")
88                    .unwrap_or(false);
89                let recent = captures
90                    .get(1)
91                    .map(|x| x.as_str())
92                    .map(|x| Self::from_tuple(x, count, before))
93                    .unwrap_or(Self::None);
94                Ok(recent)
95            }
96            None => Err(MyError::create_clap("recent", value)),
97        }
98    }
99
100    fn from_tuple(period: &str, count: i64, before: bool) -> Self {
101        if let Some(period) = Self::from_period(period, count) {
102            if before {
103                Self::Before(period)
104            } else {
105                Self::After(period)
106            }
107        } else {
108            Self::None
109        }
110    }
111
112    fn from_period(period: &str, count: i64) -> Option<PeriodKind> {
113        match period {
114            "S" => Some(PeriodKind::Sec(count)),
115            "M" => Some(PeriodKind::Min(count)),
116            "H" | "h" => Some(PeriodKind::Hour(count)),
117            "d" => Some(PeriodKind::Day(count)),
118            "w" => Some(PeriodKind::Week(count)),
119            "m" => Some(PeriodKind::Month(count)),
120            "y" => Some(PeriodKind::Year(count)),
121            _ => None,
122        }
123    }
124
125    pub fn from_times<Tz: TimeZone>(
126        file_time: &DateTime<Utc>,
127        curr_time: &DateTime<Utc>,
128        curr_calendar: &Calendar,
129        zone: &Tz,
130    ) -> Self {
131        let delta = curr_time.signed_duration_since(file_time);
132        let count = delta.num_days();
133        if count > 0 {
134            let file_calendar = Calendar::from_time(file_time, zone);
135            if let Some(count) = file_calendar.num_years_to(&curr_calendar) {
136                return Self::After(PeriodKind::Year(count));
137            }
138            if let Some(count) = file_calendar.num_months_to(&curr_calendar) {
139                return Self::After(PeriodKind::Month(count));
140            }
141            return Self::After(PeriodKind::Day(count));
142        }
143        let count = delta.num_hours();
144        if count > 0 {
145            return Self::After(PeriodKind::Hour(count));
146        }
147        let count = delta.num_minutes();
148        if count > 0 {
149            return Self::After(PeriodKind::Min(count));
150        }
151        let count = delta.num_seconds();
152        if count >= 0 {
153            return Self::After(PeriodKind::Sec(count));
154        }
155        Self::None
156    }
157
158    pub fn subtract_from<Tz: TimeZone>(
159        &self,
160        time: &DateTime<Utc>,
161        zone: &Tz,
162    ) -> RecentKind<DateTime<Utc>> {
163        match self {
164            Self::None => RecentKind::None,
165            Self::Before(period) => RecentKind::Before(period.subtract_from(time, zone)),
166            Self::After(period) => RecentKind::After(period.subtract_from(time, zone)),
167        }
168    }
169}
170
171impl<T> Default for RecentKind<T> {
172    fn default() -> Self {
173        Self::None
174    }
175}