use crate::error::{MyError, MyResult};
use crate::util::calendar::Calendar;
use chrono::{DateTime, Duration, TimeZone, Utc};
use regex::Regex;
use std::ops::Sub;
use termcolor::WriteColor;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PeriodKind {
Sec(i64),
Min(i64),
Hour(i64),
Day(i64),
Week(i64),
Month(i64),
Year(i64),
}
impl PeriodKind {
pub fn print_time<W: WriteColor>(&self, writer: &mut W, padding: bool) -> MyResult<()> {
match self {
PeriodKind::Sec(count) => write!(writer, "{:2} sec", count)?,
PeriodKind::Min(count) => write!(writer, "{:2} min", count)?,
PeriodKind::Hour(count) => write!(writer, "{:2} hour", count)?,
PeriodKind::Day(count) => write!(writer, "{:2} day", count)?,
PeriodKind::Week(count) => write!(writer, "{:2} week", count)?,
PeriodKind::Month(count) => write!(writer, "{:2} month", count)?,
PeriodKind::Year(count) => write!(writer, "{:2} year", count)?,
}
if padding {
match self {
PeriodKind::Sec(_) => write!(writer, " ")?,
PeriodKind::Min(_) => write!(writer, " ")?,
PeriodKind::Hour(_) => write!(writer, " ")?,
PeriodKind::Day(_) => write!(writer, " ")?,
PeriodKind::Week(_) => write!(writer, " ")?,
PeriodKind::Month(_) => (),
PeriodKind::Year(_) => write!(writer, " ")?,
}
}
Ok(())
}
fn subtract_from<Tz: TimeZone>(&self, time: &DateTime<Utc>, zone: &Tz) -> DateTime<Utc> {
match self {
Self::Sec(count) => time.sub(Duration::seconds(*count)),
Self::Min(count) => time.sub(Duration::minutes(*count)),
Self::Hour(count) => time.sub(Duration::hours(*count)),
Self::Day(count) => time.sub(Duration::days(*count)),
Self::Week(count) => time.sub(Duration::weeks(*count)),
Self::Month(count) => Calendar::from_time(time, zone).subtract_month(*count, zone),
Self::Year(count) => Calendar::from_time(time, zone).subtract_year(*count, zone),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RecentKind<T> {
None,
Before(T),
After(T),
}
impl<T> RecentKind<T> {
pub fn to_option(self) -> Option<T> {
match self {
Self::None => None,
Self::Before(value) => Some(value),
Self::After(value) => Some(value),
}
}
}
impl RecentKind<PeriodKind> {
pub fn from_str(value: &str) -> MyResult<Self> {
let re = Regex::new(r"^([ymwdhHMS])(\d+)?([+-])?$")?;
match re.captures(value) {
Some(captures) => {
let count = captures
.get(2)
.map(|x| x.as_str())
.map(|x| x.parse())
.unwrap_or(Ok(1))?;
let before = captures
.get(3)
.map(|x| x.as_str())
.map(|x| x == "-")
.unwrap_or(false);
let recent = captures
.get(1)
.map(|x| x.as_str())
.map(|x| Self::from_tuple(x, count, before))
.unwrap_or(Self::None);
Ok(recent)
}
None => Err(MyError::create_clap("recent", value)),
}
}
fn from_tuple(period: &str, count: i64, before: bool) -> Self {
if let Some(period) = Self::from_period(period, count) {
if before {
Self::Before(period)
} else {
Self::After(period)
}
} else {
Self::None
}
}
fn from_period(period: &str, count: i64) -> Option<PeriodKind> {
match period {
"S" => Some(PeriodKind::Sec(count)),
"M" => Some(PeriodKind::Min(count)),
"H" | "h" => Some(PeriodKind::Hour(count)),
"d" => Some(PeriodKind::Day(count)),
"w" => Some(PeriodKind::Week(count)),
"m" => Some(PeriodKind::Month(count)),
"y" => Some(PeriodKind::Year(count)),
_ => None,
}
}
pub fn from_times<Tz: TimeZone>(
file_time: &DateTime<Utc>,
curr_time: &DateTime<Utc>,
curr_calendar: &Calendar,
zone: &Tz,
) -> Self {
let delta = curr_time.signed_duration_since(file_time);
let count = delta.num_days();
if count > 0 {
let file_calendar = Calendar::from_time(file_time, zone);
if let Some(count) = file_calendar.num_years_to(&curr_calendar) {
return Self::After(PeriodKind::Year(count));
}
if let Some(count) = file_calendar.num_months_to(&curr_calendar) {
return Self::After(PeriodKind::Month(count));
}
return Self::After(PeriodKind::Day(count));
}
let count = delta.num_hours();
if count > 0 {
return Self::After(PeriodKind::Hour(count));
}
let count = delta.num_minutes();
if count > 0 {
return Self::After(PeriodKind::Min(count));
}
let count = delta.num_seconds();
if count >= 0 {
return Self::After(PeriodKind::Sec(count));
}
Self::None
}
pub fn subtract_from<Tz: TimeZone>(
&self,
time: &DateTime<Utc>,
zone: &Tz,
) -> RecentKind<DateTime<Utc>> {
match self {
Self::None => RecentKind::None,
Self::Before(period) => RecentKind::Before(period.subtract_from(time, zone)),
Self::After(period) => RecentKind::After(period.subtract_from(time, zone)),
}
}
}
impl<T> Default for RecentKind<T> {
fn default() -> Self {
Self::None
}
}