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}