| 1 | use std::iter::Peekable; | 
| 2 |  | 
|---|
| 3 | use num_conv::prelude::*; | 
|---|
| 4 | use proc_macro::{token_stream, Span, TokenTree}; | 
|---|
| 5 | use time_core::convert::*; | 
|---|
| 6 |  | 
|---|
| 7 | use crate::helpers::{consume_any_ident, consume_number, consume_punct}; | 
|---|
| 8 | use crate::to_tokens::ToTokenTree; | 
|---|
| 9 | use crate::Error; | 
|---|
| 10 |  | 
|---|
| 11 | pub(crate) struct Offset { | 
|---|
| 12 | pub(crate) hours: i8, | 
|---|
| 13 | pub(crate) minutes: i8, | 
|---|
| 14 | pub(crate) seconds: i8, | 
|---|
| 15 | } | 
|---|
| 16 |  | 
|---|
| 17 | pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Offset, Error> { | 
|---|
| 18 | if consume_any_ident(&[ "utc", "UTC"], chars).is_ok() { | 
|---|
| 19 | return Ok(Offset { | 
|---|
| 20 | hours: 0, | 
|---|
| 21 | minutes: 0, | 
|---|
| 22 | seconds: 0, | 
|---|
| 23 | }); | 
|---|
| 24 | } | 
|---|
| 25 |  | 
|---|
| 26 | let sign = if consume_punct( '+', chars).is_ok() { | 
|---|
| 27 | 1 | 
|---|
| 28 | } else if consume_punct( '-', chars).is_ok() { | 
|---|
| 29 | -1 | 
|---|
| 30 | } else if let Some(tree) = chars.next() { | 
|---|
| 31 | return Err(Error::UnexpectedToken { tree }); | 
|---|
| 32 | } else { | 
|---|
| 33 | return Err(Error::MissingComponent { | 
|---|
| 34 | name: "sign", | 
|---|
| 35 | span_start: None, | 
|---|
| 36 | span_end: None, | 
|---|
| 37 | }); | 
|---|
| 38 | }; | 
|---|
| 39 |  | 
|---|
| 40 | let (hours_span, hours) = consume_number::<i8>( "hour", chars)?; | 
|---|
| 41 | let (mut minutes_span, mut minutes) = (Span::mixed_site(), 0); | 
|---|
| 42 | let (mut seconds_span, mut seconds) = (Span::mixed_site(), 0); | 
|---|
| 43 |  | 
|---|
| 44 | if consume_punct( ':', chars).is_ok() { | 
|---|
| 45 | let min = consume_number::<i8>( "minute", chars)?; | 
|---|
| 46 | minutes_span = min.0; | 
|---|
| 47 | minutes = min.1; | 
|---|
| 48 |  | 
|---|
| 49 | if consume_punct( ':', chars).is_ok() { | 
|---|
| 50 | let sec = consume_number::<i8>( "second", chars)?; | 
|---|
| 51 | seconds_span = sec.0; | 
|---|
| 52 | seconds = sec.1; | 
|---|
| 53 | } | 
|---|
| 54 | } | 
|---|
| 55 |  | 
|---|
| 56 | if hours > 25 { | 
|---|
| 57 | Err(Error::InvalidComponent { | 
|---|
| 58 | name: "hour", | 
|---|
| 59 | value: hours.to_string(), | 
|---|
| 60 | span_start: Some(hours_span), | 
|---|
| 61 | span_end: Some(hours_span), | 
|---|
| 62 | }) | 
|---|
| 63 | } else if minutes >= Minute::per(Hour).cast_signed() { | 
|---|
| 64 | Err(Error::InvalidComponent { | 
|---|
| 65 | name: "minute", | 
|---|
| 66 | value: minutes.to_string(), | 
|---|
| 67 | span_start: Some(minutes_span), | 
|---|
| 68 | span_end: Some(minutes_span), | 
|---|
| 69 | }) | 
|---|
| 70 | } else if seconds >= Second::per(Minute).cast_signed() { | 
|---|
| 71 | Err(Error::InvalidComponent { | 
|---|
| 72 | name: "second", | 
|---|
| 73 | value: seconds.to_string(), | 
|---|
| 74 | span_start: Some(seconds_span), | 
|---|
| 75 | span_end: Some(seconds_span), | 
|---|
| 76 | }) | 
|---|
| 77 | } else { | 
|---|
| 78 | Ok(Offset { | 
|---|
| 79 | hours: sign * hours, | 
|---|
| 80 | minutes: sign * minutes, | 
|---|
| 81 | seconds: sign * seconds, | 
|---|
| 82 | }) | 
|---|
| 83 | } | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 86 | impl ToTokenTree for Offset { | 
|---|
| 87 | fn into_token_tree(self) -> TokenTree { | 
|---|
| 88 | quote_group! {{ | 
|---|
| 89 | const OFFSET: ::time::UtcOffset = unsafe { | 
|---|
| 90 | ::time::UtcOffset::__from_hms_unchecked( | 
|---|
| 91 | #(self.hours), | 
|---|
| 92 | #(self.minutes), | 
|---|
| 93 | #(self.seconds), | 
|---|
| 94 | ) | 
|---|
| 95 | }; | 
|---|
| 96 | OFFSET | 
|---|
| 97 | }} | 
|---|
| 98 | } | 
|---|
| 99 | } | 
|---|
| 100 |  | 
|---|