use serde::{Deserialize, Serialize};
use super::Interval;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TimeRange {
#[serde(rename = "1d")]
OneDay,
#[serde(rename = "5d", alias = "1wk")]
FiveDays,
#[serde(rename = "1mo")]
OneMonth,
#[serde(rename = "3mo")]
ThreeMonths,
#[serde(rename = "6mo")]
SixMonths,
#[serde(rename = "1y")]
OneYear,
#[serde(rename = "2y")]
TwoYears,
#[serde(rename = "5y")]
FiveYears,
#[serde(rename = "10y")]
TenYears,
#[serde(rename = "ytd")]
YearToDate,
#[serde(rename = "max")]
Max,
}
impl TimeRange {
pub fn as_str(&self) -> &'static str {
match self {
TimeRange::OneDay => "1d",
TimeRange::FiveDays => "5d",
TimeRange::OneMonth => "1mo",
TimeRange::ThreeMonths => "3mo",
TimeRange::SixMonths => "6mo",
TimeRange::OneYear => "1y",
TimeRange::TwoYears => "2y",
TimeRange::FiveYears => "5y",
TimeRange::TenYears => "10y",
TimeRange::YearToDate => "ytd",
TimeRange::Max => "max",
}
}
pub fn default_interval(&self) -> Interval {
match self {
TimeRange::OneDay => Interval::FiveMinutes,
TimeRange::FiveDays => Interval::FifteenMinutes,
TimeRange::OneMonth
| TimeRange::ThreeMonths
| TimeRange::SixMonths
| TimeRange::OneYear
| TimeRange::YearToDate => Interval::OneDay,
TimeRange::TwoYears | TimeRange::FiveYears => Interval::OneWeek,
TimeRange::TenYears | TimeRange::Max => Interval::OneMonth,
}
}
pub const fn approx_duration_secs(&self) -> i64 {
const DAY: i64 = 86_400;
match self {
TimeRange::OneDay => DAY,
TimeRange::FiveDays => 5 * DAY,
TimeRange::OneMonth => 30 * DAY,
TimeRange::ThreeMonths => 90 * DAY,
TimeRange::SixMonths => 180 * DAY,
TimeRange::OneYear | TimeRange::YearToDate => 365 * DAY,
TimeRange::TwoYears => 730 * DAY,
TimeRange::FiveYears => 1_825 * DAY,
TimeRange::TenYears => 3_650 * DAY,
TimeRange::Max => 36_500 * DAY,
}
}
}
impl std::fmt::Display for TimeRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl std::str::FromStr for TimeRange {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim().to_lowercase().as_str() {
"1d" => Ok(TimeRange::OneDay),
"5d" | "1wk" => Ok(TimeRange::FiveDays),
"1mo" => Ok(TimeRange::OneMonth),
"3mo" => Ok(TimeRange::ThreeMonths),
"6mo" => Ok(TimeRange::SixMonths),
"1y" => Ok(TimeRange::OneYear),
"2y" => Ok(TimeRange::TwoYears),
"5y" => Ok(TimeRange::FiveYears),
"10y" => Ok(TimeRange::TenYears),
"ytd" => Ok(TimeRange::YearToDate),
"max" => Ok(TimeRange::Max),
_ => Err(()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_time_range_as_str() {
assert_eq!(TimeRange::OneDay.as_str(), "1d");
assert_eq!(TimeRange::OneMonth.as_str(), "1mo");
assert_eq!(TimeRange::OneYear.as_str(), "1y");
assert_eq!(TimeRange::Max.as_str(), "max");
}
#[test]
fn test_time_range_from_str_round_trips_as_str() {
for range in [
TimeRange::OneDay,
TimeRange::FiveDays,
TimeRange::OneMonth,
TimeRange::ThreeMonths,
TimeRange::SixMonths,
TimeRange::OneYear,
TimeRange::TwoYears,
TimeRange::FiveYears,
TimeRange::TenYears,
TimeRange::YearToDate,
TimeRange::Max,
] {
assert_eq!(range.as_str().parse(), Ok(range));
}
assert_eq!("1wk".parse(), Ok(TimeRange::FiveDays));
assert_eq!("YTD".parse(), Ok(TimeRange::YearToDate));
assert_eq!("bogus".parse::<TimeRange>(), Err(()));
}
#[test]
fn test_default_interval_buckets() {
assert_eq!(TimeRange::OneDay.default_interval(), Interval::FiveMinutes);
assert_eq!(
TimeRange::FiveDays.default_interval(),
Interval::FifteenMinutes
);
assert_eq!(TimeRange::OneMonth.default_interval(), Interval::OneDay);
assert_eq!(TimeRange::OneYear.default_interval(), Interval::OneDay);
assert_eq!(TimeRange::TwoYears.default_interval(), Interval::OneWeek);
assert_eq!(TimeRange::Max.default_interval(), Interval::OneMonth);
}
}