1use std::fmt;
4use strum_macros::{EnumIter, EnumString, IntoStaticStr};
5
6#[derive(
8 Clone, Copy, PartialEq, Eq, Debug, EnumString, IntoStaticStr, EnumIter, Hash, PartialOrd, Ord,
9)]
10pub enum Model {
11 #[strum(
13 to_string = "gfs",
14 serialize = "gfs3",
15 serialize = "GFS",
16 serialize = "GFS3"
17 )]
18 GFS,
19 #[strum(
21 to_string = "nam",
22 serialize = "namm",
23 serialize = "NAM",
24 serialize = "NAMM"
25 )]
26 NAM,
27 #[strum(to_string = "nam4km", serialize = "NAM4KM")]
29 NAM4KM,
30}
31
32impl fmt::Display for Model {
33 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34 use crate::models::Model::*;
35
36 match *self {
37 GFS => write!(f, "{}", stringify!(GFS)),
38 NAM => write!(f, "{}", stringify!(NAM)),
39 NAM4KM => write!(f, "{}", stringify!(NAM4KM)),
40 }
41 }
42}
43
44impl Model {
45 pub fn hours_between_runs(self) -> i64 {
47 match self {
48 Model::GFS | Model::NAM | Model::NAM4KM => 6,
49 }
50 }
51
52 pub fn base_hour(self) -> i64 {
58 match self {
59 Model::GFS | Model::NAM | Model::NAM4KM => 0,
60 }
61 }
62
63 pub fn all_runs(
65 self,
66 start: &chrono::NaiveDateTime,
67 end: &chrono::NaiveDateTime,
68 ) -> impl Iterator<Item = chrono::NaiveDateTime> {
69 let delta_t = self.hours_between_runs();
70
71 let round_start = if *start < *end {
73 let mut strt =
74 start.date().and_hms_opt(0, 0, 0).unwrap() + chrono::Duration::hours(self.base_hour());
75 while strt > *start {
77 strt -= chrono::Duration::hours(self.hours_between_runs());
78 }
79 while strt < *start {
81 strt += chrono::Duration::hours(self.hours_between_runs());
82 }
83
84 strt
85 } else {
86 let mut strt =
87 start.date().and_hms_opt(0, 0, 0).unwrap() + chrono::Duration::hours(self.base_hour());
88 while strt < *start {
89 strt += chrono::Duration::hours(self.hours_between_runs());
90 }
91 while strt > *start {
92 strt -= chrono::Duration::hours(self.hours_between_runs());
93 }
94
95 strt
96 };
97
98 let steps: i64 = (*end - round_start).num_hours() / self.hours_between_runs();
99
100 (0..=steps.abs())
101 .map(move |step| round_start + chrono::Duration::hours(steps.signum() * step * delta_t))
102 }
103
104 pub fn as_static_str(self) -> &'static str {
106 self.into()
107 }
108}
109
110#[cfg(test)]
114mod unit {
115 use super::*;
116
117 use chrono::NaiveDate;
118
119 #[test]
120 fn test_all_runs() {
121 assert_eq!(
122 Model::GFS.hours_between_runs(),
123 6,
124 "test pre-condition failed."
125 );
126
127 let start = &NaiveDate::from_ymd_opt(2018, 9, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();
128 let end = &NaiveDate::from_ymd_opt(2018, 9, 2).unwrap().and_hms_opt(0, 0, 0).unwrap();
129 assert_eq!(Model::GFS.all_runs(start, end).count(), 5);
130 Model::GFS
131 .all_runs(start, end)
132 .scan(*start, |prev, rt| {
133 eprintln!("{} <= {}", prev, rt);
134 assert!(*prev <= rt);
135 *prev = rt;
136 Some(rt)
137 })
138 .for_each(|rt| assert!(rt >= *start && rt <= *end));
139 eprintln!();
140
141 let start = &NaiveDate::from_ymd_opt(2018, 9, 1).unwrap().and_hms_opt(0, 1, 0).unwrap();
142 let end = &NaiveDate::from_ymd_opt(2018, 9, 2).unwrap().and_hms_opt(0, 0, 0).unwrap();
143 assert_eq!(Model::GFS.all_runs(start, end).count(), 4);
144 Model::GFS
145 .all_runs(start, end)
146 .scan(*start, |prev, rt| {
147 eprintln!("{} <= {}", prev, rt);
148 assert!(*prev <= rt);
149 *prev = rt;
150 Some(rt)
151 })
152 .for_each(|rt| assert!(rt >= *start && rt <= *end));
153 eprintln!();
154
155 let end = &NaiveDate::from_ymd_opt(2018, 9, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();
156 let start = &NaiveDate::from_ymd_opt(2018, 9, 2).unwrap().and_hms_opt(0, 0, 0).unwrap();
157 assert_eq!(Model::GFS.all_runs(start, end).count(), 5);
158 Model::GFS
159 .all_runs(start, end)
160 .scan(*start, |prev, rt| {
161 eprintln!("{} >= {}", prev, rt);
162 assert!(*prev >= rt);
163 *prev = rt;
164 Some(rt)
165 })
166 .for_each(|rt| assert!(rt >= *end && rt <= *start));
167 eprintln!();
168
169 let end = &NaiveDate::from_ymd_opt(2018, 9, 1).unwrap().and_hms_opt(0, 1, 0).unwrap();
170 let start = &NaiveDate::from_ymd_opt(2018, 9, 2).unwrap().and_hms_opt(0, 0, 0).unwrap();
171 assert_eq!(Model::GFS.all_runs(start, end).count(), 4);
172 Model::GFS
173 .all_runs(start, end)
174 .scan(*start, |prev, rt| {
175 eprintln!("{} >= {}", prev, rt);
176 assert!(*prev >= rt);
177 *prev = rt;
178 Some(rt)
179 })
180 .for_each(|rt| assert!(rt >= *end && rt <= *start));
181 eprintln!();
182
183 let end = &NaiveDate::from_ymd_opt(2018, 9, 1).unwrap().and_hms_opt(0, 1, 0).unwrap();
184 let start = &NaiveDate::from_ymd_opt(2018, 9, 2).unwrap().and_hms_opt(0, 2, 0).unwrap();
185 assert_eq!(Model::GFS.all_runs(start, end).count(), 4);
186 Model::GFS
187 .all_runs(start, end)
188 .scan(*start, |prev, rt| {
189 eprintln!("{} >= {}", prev, rt);
190 assert!(*prev >= rt);
191 *prev = rt;
192 Some(rt)
193 })
194 .for_each(|rt| assert!(rt >= *end && rt <= *start));
195 }
196}