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