use chrono::{Duration, NaiveDateTime};
use std::fmt;
use strum_macros::{EnumIter, EnumString, IntoStaticStr};
#[derive(Clone, Copy, PartialEq, Eq, Debug, EnumString, IntoStaticStr, EnumIter, Hash)]
pub enum Model {
#[strum(
to_string = "gfs",
serialize = "gfs3",
serialize = "GFS",
serialize = "GFS3"
)]
GFS,
#[strum(
to_string = "nam",
serialize = "namm",
serialize = "NAM",
serialize = "NAMM"
)]
NAM,
#[strum(to_string = "nam4km", serialize = "NAM4KM")]
NAM4KM,
}
impl fmt::Display for Model {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use crate::Model::*;
match *self {
GFS => write!(f, "{}", stringify!(GFS)),
NAM => write!(f, "{}", stringify!(NAM)),
NAM4KM => write!(f, "{}", stringify!(NAM4KM)),
}
}
}
impl Model {
pub fn hours_between_runs(self) -> i64 {
match self {
Model::GFS => 6,
Model::NAM => 6,
Model::NAM4KM => 6,
}
}
pub fn base_hour(self) -> i64 {
0
}
pub fn all_runs(
self,
start: &NaiveDateTime,
end: &NaiveDateTime,
) -> impl Iterator<Item = NaiveDateTime> {
let delta_t = self.hours_between_runs();
let round_start = if *start < *end {
let mut strt = start.date().and_hms(0, 0, 0) + Duration::hours(self.base_hour());
while strt > *start {
strt -= Duration::hours(self.hours_between_runs());
}
while strt < *start {
strt += Duration::hours(self.hours_between_runs());
}
strt
} else {
let mut strt = start.date().and_hms(0, 0, 0) + Duration::hours(self.base_hour());
while strt < *start {
strt += Duration::hours(self.hours_between_runs());
}
while strt > *start {
strt -= Duration::hours(self.hours_between_runs());
}
strt
};
let steps: i64 = (*end - round_start).num_hours() / self.hours_between_runs();
(0..=steps.abs())
.map(move |step| round_start + Duration::hours(steps.signum() * step * delta_t))
}
pub fn as_static_str(self) -> &'static str {
self.into()
}
}
#[cfg(test)]
mod unit {
use super::*;
use chrono::NaiveDate;
#[test]
fn test_all_runs() {
assert_eq!(
Model::GFS.hours_between_runs(),
6,
"test pre-condition failed."
);
let start = &NaiveDate::from_ymd(2018, 9, 1).and_hms(0, 0, 0);
let end = &NaiveDate::from_ymd(2018, 9, 2).and_hms(0, 0, 0);
assert_eq!(Model::GFS.all_runs(start, end).count(), 5);
Model::GFS
.all_runs(start, end)
.scan(*start, |prev, rt| {
eprintln!("{} <= {}", prev, rt);
assert!(*prev <= rt);
*prev = rt;
Some(rt)
})
.for_each(|rt| assert!(rt >= *start && rt <= *end));
eprintln!();
let start = &NaiveDate::from_ymd(2018, 9, 1).and_hms(0, 1, 0);
let end = &NaiveDate::from_ymd(2018, 9, 2).and_hms(0, 0, 0);
assert_eq!(Model::GFS.all_runs(start, end).count(), 4);
Model::GFS
.all_runs(start, end)
.scan(*start, |prev, rt| {
eprintln!("{} <= {}", prev, rt);
assert!(*prev <= rt);
*prev = rt;
Some(rt)
})
.for_each(|rt| assert!(rt >= *start && rt <= *end));
eprintln!();
let end = &NaiveDate::from_ymd(2018, 9, 1).and_hms(0, 0, 0);
let start = &NaiveDate::from_ymd(2018, 9, 2).and_hms(0, 0, 0);
assert_eq!(Model::GFS.all_runs(start, end).count(), 5);
Model::GFS
.all_runs(start, end)
.scan(*start, |prev, rt| {
eprintln!("{} >= {}", prev, rt);
assert!(*prev >= rt);
*prev = rt;
Some(rt)
})
.for_each(|rt| assert!(rt >= *end && rt <= *start));
eprintln!();
let end = &NaiveDate::from_ymd(2018, 9, 1).and_hms(0, 1, 0);
let start = &NaiveDate::from_ymd(2018, 9, 2).and_hms(0, 0, 0);
assert_eq!(Model::GFS.all_runs(start, end).count(), 4);
Model::GFS
.all_runs(start, end)
.scan(*start, |prev, rt| {
eprintln!("{} >= {}", prev, rt);
assert!(*prev >= rt);
*prev = rt;
Some(rt)
})
.for_each(|rt| assert!(rt >= *end && rt <= *start));
eprintln!();
let end = &NaiveDate::from_ymd(2018, 9, 1).and_hms(0, 1, 0);
let start = &NaiveDate::from_ymd(2018, 9, 2).and_hms(0, 2, 0);
assert_eq!(Model::GFS.all_runs(start, end).count(), 4);
Model::GFS
.all_runs(start, end)
.scan(*start, |prev, rt| {
eprintln!("{} >= {}", prev, rt);
assert!(*prev >= rt);
*prev = rt;
Some(rt)
})
.for_each(|rt| assert!(rt >= *end && rt <= *start));
}
}