| 1 | use std::iter::Peekable; | 
| 2 |  | 
|---|
| 3 | use proc_macro::{token_stream, Span, TokenTree}; | 
|---|
| 4 | use time_core::convert::*; | 
|---|
| 5 |  | 
|---|
| 6 | use crate::helpers::{consume_any_ident, consume_number, consume_punct}; | 
|---|
| 7 | use crate::to_tokens::ToTokenTree; | 
|---|
| 8 | use crate::Error; | 
|---|
| 9 |  | 
|---|
| 10 | enum Period { | 
|---|
| 11 | Am, | 
|---|
| 12 | Pm, | 
|---|
| 13 | _24, | 
|---|
| 14 | } | 
|---|
| 15 |  | 
|---|
| 16 | pub(crate) struct Time { | 
|---|
| 17 | pub(crate) hour: u8, | 
|---|
| 18 | pub(crate) minute: u8, | 
|---|
| 19 | pub(crate) second: u8, | 
|---|
| 20 | pub(crate) nanosecond: u32, | 
|---|
| 21 | } | 
|---|
| 22 |  | 
|---|
| 23 | pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Time, Error> { | 
|---|
| 24 | fn consume_period(chars: &mut Peekable<token_stream::IntoIter>) -> (Option<Span>, Period) { | 
|---|
| 25 | if let Ok(span) = consume_any_ident(&[ "am", "AM"], chars) { | 
|---|
| 26 | (Some(span), Period::Am) | 
|---|
| 27 | } else if let Ok(span) = consume_any_ident(&[ "pm", "PM"], chars) { | 
|---|
| 28 | (Some(span), Period::Pm) | 
|---|
| 29 | } else { | 
|---|
| 30 | (None, Period::_24) | 
|---|
| 31 | } | 
|---|
| 32 | } | 
|---|
| 33 |  | 
|---|
| 34 | let (hour_span, hour) = consume_number( "hour", chars)?; | 
|---|
| 35 |  | 
|---|
| 36 | let ((minute_span, minute), (second_span, second), (period_span, period)) = | 
|---|
| 37 | match consume_period(chars) { | 
|---|
| 38 | // Nothing but the 12-hour clock hour and AM/PM | 
|---|
| 39 | (period_span @ Some(_), period) => ( | 
|---|
| 40 | (Span::mixed_site(), 0), | 
|---|
| 41 | (Span::mixed_site(), 0.), | 
|---|
| 42 | (period_span, period), | 
|---|
| 43 | ), | 
|---|
| 44 | (None, _) => { | 
|---|
| 45 | consume_punct( ':', chars)?; | 
|---|
| 46 | let (minute_span, minute) = consume_number::<u8>( "minute", chars)?; | 
|---|
| 47 | let (second_span, second): (_, f64) = if consume_punct( ':', chars).is_ok() { | 
|---|
| 48 | consume_number( "second", chars)? | 
|---|
| 49 | } else { | 
|---|
| 50 | (Span::mixed_site(), 0.) | 
|---|
| 51 | }; | 
|---|
| 52 | let (period_span, period) = consume_period(chars); | 
|---|
| 53 | ( | 
|---|
| 54 | (minute_span, minute), | 
|---|
| 55 | (second_span, second), | 
|---|
| 56 | (period_span, period), | 
|---|
| 57 | ) | 
|---|
| 58 | } | 
|---|
| 59 | }; | 
|---|
| 60 |  | 
|---|
| 61 | let hour = match (hour, period) { | 
|---|
| 62 | (0, Period::Am | Period::Pm) => { | 
|---|
| 63 | return Err(Error::InvalidComponent { | 
|---|
| 64 | name: "hour", | 
|---|
| 65 | value: hour.to_string(), | 
|---|
| 66 | span_start: Some(hour_span), | 
|---|
| 67 | span_end: Some(period_span.unwrap_or(hour_span)), | 
|---|
| 68 | }); | 
|---|
| 69 | } | 
|---|
| 70 | (12, Period::Am) => 0, | 
|---|
| 71 | (12, Period::Pm) => 12, | 
|---|
| 72 | (hour, Period::Am | Period::_24) => hour, | 
|---|
| 73 | (hour, Period::Pm) => hour + 12, | 
|---|
| 74 | }; | 
|---|
| 75 |  | 
|---|
| 76 | if hour >= Hour::per(Day) { | 
|---|
| 77 | Err(Error::InvalidComponent { | 
|---|
| 78 | name: "hour", | 
|---|
| 79 | value: hour.to_string(), | 
|---|
| 80 | span_start: Some(hour_span), | 
|---|
| 81 | span_end: Some(period_span.unwrap_or(hour_span)), | 
|---|
| 82 | }) | 
|---|
| 83 | } else if minute >= Minute::per(Hour) { | 
|---|
| 84 | Err(Error::InvalidComponent { | 
|---|
| 85 | name: "minute", | 
|---|
| 86 | value: minute.to_string(), | 
|---|
| 87 | span_start: Some(minute_span), | 
|---|
| 88 | span_end: Some(minute_span), | 
|---|
| 89 | }) | 
|---|
| 90 | } else if second >= Second::per(Minute) as _ { | 
|---|
| 91 | Err(Error::InvalidComponent { | 
|---|
| 92 | name: "second", | 
|---|
| 93 | value: second.to_string(), | 
|---|
| 94 | span_start: Some(second_span), | 
|---|
| 95 | span_end: Some(second_span), | 
|---|
| 96 | }) | 
|---|
| 97 | } else { | 
|---|
| 98 | Ok(Time { | 
|---|
| 99 | hour, | 
|---|
| 100 | minute, | 
|---|
| 101 | second: second.trunc() as _, | 
|---|
| 102 | nanosecond: (second.fract() * Nanosecond::per(Second) as f64).round() as _, | 
|---|
| 103 | }) | 
|---|
| 104 | } | 
|---|
| 105 | } | 
|---|
| 106 |  | 
|---|
| 107 | impl ToTokenTree for Time { | 
|---|
| 108 | fn into_token_tree(self) -> TokenTree { | 
|---|
| 109 | quote_group! {{ | 
|---|
| 110 | const TIME: ::time::Time = unsafe { | 
|---|
| 111 | ::time::Time::__from_hms_nanos_unchecked( | 
|---|
| 112 | #(self.hour), | 
|---|
| 113 | #(self.minute), | 
|---|
| 114 | #(self.second), | 
|---|
| 115 | #(self.nanosecond), | 
|---|
| 116 | ) | 
|---|
| 117 | }; | 
|---|
| 118 | TIME | 
|---|
| 119 | }} | 
|---|
| 120 | } | 
|---|
| 121 | } | 
|---|
| 122 |  | 
|---|