11//! Holiday hours metadata parsing and evaluation logic
22
3+ #[ allow( deprecated) ]
34use {
45 super :: legacy_schedule:: {
56 LegacySchedule ,
1920 chrono_tz:: Tz ,
2021 std:: {
2122 fmt:: Display ,
23+ ops:: RangeInclusive ,
2224 str:: FromStr ,
2325 } ,
2426 winnow:: {
@@ -129,6 +131,7 @@ impl FromStr for MarketSchedule {
129131 }
130132}
131133
134+ #[ allow( deprecated) ]
132135impl From < LegacySchedule > for MarketSchedule {
133136 fn from ( legacy : LegacySchedule ) -> Self {
134137 Self {
@@ -190,19 +193,19 @@ impl Display for HolidayDaySchedule {
190193 }
191194}
192195
193- #[ derive( Clone , Debug , Eq , PartialEq , Copy ) ]
196+ #[ derive( Clone , Debug , Eq , PartialEq ) ]
194197pub enum ScheduleDayKind {
195198 Open ,
196199 Closed ,
197- TimeRange ( NaiveTime , NaiveTime ) ,
200+ TimeRanges ( Vec < RangeInclusive < NaiveTime > > ) ,
198201}
199202
200203impl ScheduleDayKind {
201204 pub fn can_publish_at ( & self , when_local : NaiveTime ) -> bool {
202205 match self {
203206 Self :: Open => true ,
204207 Self :: Closed => false ,
205- Self :: TimeRange ( start , end ) => start <= & when_local && & when_local <= end ,
208+ Self :: TimeRanges ( ranges ) => ranges . iter ( ) . any ( |range| range . contains ( & when_local) ) ,
206209 }
207210 }
208211}
@@ -218,8 +221,20 @@ impl Display for ScheduleDayKind {
218221 match self {
219222 Self :: Open => write ! ( f, "O" ) ,
220223 Self :: Closed => write ! ( f, "C" ) ,
221- Self :: TimeRange ( start, end) => {
222- write ! ( f, "{}-{}" , start. format( "%H%M" ) , end. format( "%H%M" ) )
224+ Self :: TimeRanges ( ranges) => {
225+ let mut ranges = ranges. iter ( ) . peekable ( ) ;
226+ while let Some ( range) = ranges. next ( ) {
227+ write ! (
228+ f,
229+ "{}-{}" ,
230+ range. start( ) . format( "%H%M" ) ,
231+ range. end( ) . format( "%H%M" )
232+ ) ?;
233+ if ranges. peek ( ) . is_some ( ) {
234+ write ! ( f, "&" ) ?;
235+ }
236+ }
237+ Ok ( ( ) )
223238 }
224239 }
225240 }
@@ -234,21 +249,21 @@ fn time_parser<'s>(input: &mut &'s str) -> PResult<NaiveTime> {
234249 . parse_next ( input)
235250}
236251
237- fn time_range_parser < ' s > ( input : & mut & ' s str ) -> PResult < ScheduleDayKind > {
252+ fn time_range_parser < ' s > ( input : & mut & ' s str ) -> PResult < RangeInclusive < NaiveTime > > {
238253 seq ! (
239254 time_parser,
240255 _: "-" ,
241256 time_parser,
242257 )
243- . map ( |s| ScheduleDayKind :: TimeRange ( s. 0 , s. 1 ) )
258+ . map ( |s| s. 0 ..= s. 1 )
244259 . parse_next ( input)
245260}
246261
247262fn schedule_day_kind_parser < ' s > ( input : & mut & ' s str ) -> PResult < ScheduleDayKind > {
248263 alt ( (
249264 "C" . map ( |_| ScheduleDayKind :: Closed ) ,
250265 "O" . map ( |_| ScheduleDayKind :: Open ) ,
251- time_range_parser,
266+ separated ( 1 .. , time_range_parser, "&" ) . map ( ScheduleDayKind :: TimeRanges ) ,
252267 ) )
253268 . parse_next ( input)
254269}
@@ -267,7 +282,7 @@ impl From<MHKind> for ScheduleDayKind {
267282 match mhkind {
268283 MHKind :: Open => ScheduleDayKind :: Open ,
269284 MHKind :: Closed => ScheduleDayKind :: Closed ,
270- MHKind :: TimeRange ( start, end) => ScheduleDayKind :: TimeRange ( start, end) ,
285+ MHKind :: TimeRange ( start, end) => ScheduleDayKind :: TimeRanges ( vec ! [ start..= end] ) ,
271286 }
272287 }
273288}
@@ -290,6 +305,7 @@ mod tests {
290305 let open = "O" ;
291306 let closed = "C" ;
292307 let valid = "1234-1347" ;
308+ let valid_double = "1234-1347&1400-1500" ;
293309 let valid2400 = "1234-2400" ;
294310 let invalid = "1234-5668" ;
295311 let invalid_format = "1234-56" ;
@@ -304,17 +320,25 @@ mod tests {
304320 ) ;
305321 assert_eq ! (
306322 valid. parse:: <ScheduleDayKind >( ) . unwrap( ) ,
307- ScheduleDayKind :: TimeRange (
308- NaiveTime :: from_hms_opt( 12 , 34 , 0 ) . unwrap( ) ,
309- NaiveTime :: from_hms_opt( 13 , 47 , 0 ) . unwrap( ) ,
310- )
323+ ScheduleDayKind :: TimeRanges ( vec![
324+ NaiveTime :: from_hms_opt( 12 , 34 , 0 ) . unwrap( )
325+ ..=NaiveTime :: from_hms_opt( 13 , 47 , 0 ) . unwrap( )
326+ ] )
327+ ) ;
328+ assert_eq ! (
329+ valid_double. parse:: <ScheduleDayKind >( ) . unwrap( ) ,
330+ ScheduleDayKind :: TimeRanges ( vec![
331+ NaiveTime :: from_hms_opt( 12 , 34 , 0 ) . unwrap( )
332+ ..=NaiveTime :: from_hms_opt( 13 , 47 , 0 ) . unwrap( ) ,
333+ NaiveTime :: from_hms_opt( 14 , 0 , 0 ) . unwrap( )
334+ ..=NaiveTime :: from_hms_opt( 15 , 0 , 0 ) . unwrap( ) ,
335+ ] )
311336 ) ;
312337 assert_eq ! (
313338 valid2400. parse:: <ScheduleDayKind >( ) . unwrap( ) ,
314- ScheduleDayKind :: TimeRange (
315- NaiveTime :: from_hms_opt( 12 , 34 , 0 ) . unwrap( ) ,
316- MAX_TIME_INSTANT ,
317- )
339+ ScheduleDayKind :: TimeRanges ( vec![
340+ NaiveTime :: from_hms_opt( 12 , 34 , 0 ) . unwrap( ) ..=MAX_TIME_INSTANT
341+ ] )
318342 ) ;
319343 assert ! ( invalid. parse:: <ScheduleDayKind >( ) . is_err( ) ) ;
320344 assert ! ( invalid_format. parse:: <ScheduleDayKind >( ) . is_err( ) ) ;
@@ -347,10 +371,10 @@ mod tests {
347371 let expected = HolidayDaySchedule {
348372 month : 04 ,
349373 day : 12 ,
350- kind : ScheduleDayKind :: TimeRange (
351- NaiveTime :: from_hms_opt ( 12 , 34 , 0 ) . unwrap ( ) ,
352- NaiveTime :: from_hms_opt ( 13 , 47 , 0 ) . unwrap ( ) ,
353- ) ,
374+ kind : ScheduleDayKind :: TimeRanges ( vec ! [
375+ NaiveTime :: from_hms_opt( 12 , 34 , 0 ) . unwrap( )
376+ ..= NaiveTime :: from_hms_opt( 13 , 47 , 0 ) . unwrap( ) ,
377+ ] ) ,
354378 } ;
355379 let parsed = input. parse :: < HolidayDaySchedule > ( ) ?;
356380 assert_eq ! ( parsed, expected) ;
@@ -371,14 +395,13 @@ mod tests {
371395 timezone : Tz :: America__New_York ,
372396 weekly_schedule : vec ! [
373397 ScheduleDayKind :: Open ,
374- ScheduleDayKind :: TimeRange (
375- NaiveTime :: from_hms_opt( 12 , 34 , 0 ) . unwrap( ) ,
376- NaiveTime :: from_hms_opt( 13 , 47 , 0 ) . unwrap( ) ,
377- ) ,
378- ScheduleDayKind :: TimeRange (
379- NaiveTime :: from_hms_opt( 09 , 30 , 0 ) . unwrap( ) ,
380- MAX_TIME_INSTANT ,
381- ) ,
398+ ScheduleDayKind :: TimeRanges ( vec![
399+ NaiveTime :: from_hms_opt( 12 , 34 , 0 ) . unwrap( )
400+ ..=NaiveTime :: from_hms_opt( 13 , 47 , 0 ) . unwrap( ) ,
401+ ] ) ,
402+ ScheduleDayKind :: TimeRanges ( vec![
403+ NaiveTime :: from_hms_opt( 09 , 30 , 0 ) . unwrap( ) ..=MAX_TIME_INSTANT ,
404+ ] ) ,
382405 ScheduleDayKind :: Closed ,
383406 ScheduleDayKind :: Closed ,
384407 ScheduleDayKind :: Closed ,
@@ -398,18 +421,17 @@ mod tests {
398421 HolidayDaySchedule {
399422 month: 04 ,
400423 day: 14 ,
401- kind: ScheduleDayKind :: TimeRange (
402- NaiveTime :: from_hms_opt( 12 , 34 , 0 ) . unwrap( ) ,
403- NaiveTime :: from_hms_opt( 13 , 47 , 0 ) . unwrap( ) ,
404- ) ,
424+ kind: ScheduleDayKind :: TimeRanges ( vec! [
425+ NaiveTime :: from_hms_opt( 12 , 34 , 0 ) . unwrap( )
426+ ..= NaiveTime :: from_hms_opt( 13 , 47 , 0 ) . unwrap( ) ,
427+ ] ) ,
405428 } ,
406429 HolidayDaySchedule {
407430 month: 12 ,
408431 day: 30 ,
409- kind: ScheduleDayKind :: TimeRange (
410- NaiveTime :: from_hms_opt( 09 , 30 , 0 ) . unwrap( ) ,
411- MAX_TIME_INSTANT ,
412- ) ,
432+ kind: ScheduleDayKind :: TimeRanges ( vec![
433+ NaiveTime :: from_hms_opt( 09 , 30 , 0 ) . unwrap( ) ..=MAX_TIME_INSTANT ,
434+ ] ) ,
413435 } ,
414436 ] ,
415437 } ;
@@ -476,18 +498,24 @@ mod tests {
476498 }
477499
478500 prop_compose ! {
479- fn schedule_day_kind( ) (
480- r in any:: <u8 >( ) ,
501+ fn time_range( ) (
481502 t1 in any:: <u32 >( ) ,
482503 t2 in any:: <u32 >( ) ,
504+ ) -> RangeInclusive <NaiveTime > {
505+ NaiveTime :: from_hms_opt( t1 % 24 , t1 / 24 % 60 , 0 ) . unwrap( ) ..=
506+ NaiveTime :: from_hms_opt( t2 % 24 , t2 / 24 % 60 , 0 ) . unwrap( )
507+ }
508+ }
509+
510+ prop_compose ! {
511+ fn schedule_day_kind( ) (
512+ r in any:: <u8 >( ) ,
513+ ranges in proptest:: collection:: vec( time_range( ) , 1 ..3 ) ,
483514 ) -> ScheduleDayKind {
484515 match r % 3 {
485516 0 => ScheduleDayKind :: Open ,
486517 1 => ScheduleDayKind :: Closed ,
487- _ => ScheduleDayKind :: TimeRange (
488- NaiveTime :: from_hms_opt( t1 % 24 , t1 / 24 % 60 , 0 ) . unwrap( ) ,
489- NaiveTime :: from_hms_opt( t2 % 24 , t2 / 24 % 60 , 0 ) . unwrap( ) ,
490- ) ,
518+ _ => ScheduleDayKind :: TimeRanges ( ranges) ,
491519 }
492520 }
493521 }
0 commit comments