1#[cfg(any(feature = "formatting", feature = "parsing"))]
2mod string;
3
4use std::iter::Peekable;
5use std::str::FromStr;
6
7use num_conv::prelude::*;
8use proc_macro::{Span, TokenTree, token_stream};
9use time_core::util::{days_in_year, is_leap_year};
10
11use crate::Error;
12use crate::date::MAX_YEAR;
13
14#[cfg(any(feature = "formatting", feature = "parsing"))]
15pub(crate) fn get_string_literal(
16 permit_byte_strings: bool,
17 mut tokens: impl Iterator<Item = TokenTree>,
18) -> Result<(Span, Vec<u8>), Error> {
19 match (tokens.next(), tokens.next()) {
20 (Some(TokenTree::Literal(literal)), None) => string::parse(permit_byte_strings, &literal),
21 (Some(tree), None) => Err(Error::ExpectedString {
22 span_start: Some(tree.span()),
23 span_end: Some(tree.span()),
24 }),
25 (_, Some(tree)) => Err(Error::UnexpectedToken { tree }),
26 (None, None) => Err(Error::ExpectedString {
27 span_start: None,
28 span_end: None,
29 }),
30 }
31}
32
33pub(crate) fn parse_number<T: FromStr>(
34 component_name: &'static str,
35 chars: &str,
36) -> Result<T, Error> {
37 chars
38 .replace('_', "")
39 .parse()
40 .map_err(|_| Error::InvalidComponent {
41 name: component_name,
42 value: chars.to_string(),
43 span_start: None,
44 span_end: None,
45 })
46}
47
48pub(crate) fn consume_number<T: FromStr>(
49 component_name: &'static str,
50 chars: &mut Peekable<token_stream::IntoIter>,
51) -> Result<(Span, T), Error> {
52 match chars.next() {
53 Some(TokenTree::Literal(literal)) => {
54 let span = literal.span();
55 match parse_number(component_name, &literal.to_string()) {
56 Ok(val) => Ok((span, val)),
57 Err(Error::InvalidComponent {
58 name,
59 value,
60 span_start: _,
61 span_end: _,
62 }) => Err(Error::InvalidComponent {
63 name,
64 value,
65 span_start: Some(span.start()),
66 span_end: Some(span.end()),
67 }),
68 Err(e) => Err(e),
69 }
70 }
71 Some(tree) => Err(Error::UnexpectedToken { tree }),
72 None => Err(Error::UnexpectedEndOfInput),
73 }
74}
75
76pub(crate) fn consume_any_ident(
77 idents: &[&str],
78 chars: &mut Peekable<token_stream::IntoIter>,
79) -> Result<Span, Error> {
80 match chars.peek() {
81 Some(TokenTree::Ident(char)) if idents.contains(&char.to_string().as_str()) => {
82 let ret = Ok(char.span());
83 drop(chars.next());
84 ret
85 }
86 Some(tree) => Err(Error::UnexpectedToken { tree: tree.clone() }),
87 None => Err(Error::UnexpectedEndOfInput),
88 }
89}
90
91pub(crate) fn consume_punct(
92 c: char,
93 chars: &mut Peekable<token_stream::IntoIter>,
94) -> Result<Span, Error> {
95 match chars.peek() {
96 Some(TokenTree::Punct(punct)) if *punct == c => {
97 let ret = Ok(punct.span());
98 drop(chars.next());
99 ret
100 }
101 Some(tree) => Err(Error::UnexpectedToken { tree: tree.clone() }),
102 None => Err(Error::UnexpectedEndOfInput),
103 }
104}
105
106fn jan_weekday(year: i32, ordinal: i32) -> u8 {
107 macro_rules! div_floor {
108 ($a:expr, $b:expr) => {{
109 let (_quotient, _remainder) = ($a / $b, $a % $b);
110 if (_remainder > 0 && $b < 0) || (_remainder < 0 && $b > 0) {
111 _quotient - 1
112 } else {
113 _quotient
114 }
115 }};
116 }
117
118 let adj_year = year - 1;
119 (ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
120 + div_floor!(adj_year, 400)
121 + 6)
122 .rem_euclid(7)
123 .cast_unsigned()
124 .truncate()
125}
126
127pub(crate) fn days_in_year_month(year: i32, month: u8) -> u8 {
128 [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month.widen::<usize>() - 1]
129 + u8::from(month == 2 && is_leap_year(year))
130}
131
132pub(crate) fn ywd_to_yo(
133 year: i32,
134 week: u8,
135 iso_weekday_number: u8,
136 iso_weekday_span: Span,
137) -> Result<(i32, u16), Error> {
138 let (ordinal, overflow) = (u16::from(week) * 7 + u16::from(iso_weekday_number))
139 .overflowing_sub(u16::from(jan_weekday(year, 4)) + 4);
140
141 if overflow || ordinal == 0 {
142 return Ok((year - 1, (ordinal.wrapping_add(days_in_year(year - 1)))));
143 }
144
145 let days_in_cur_year = days_in_year(year);
146 if ordinal > days_in_cur_year {
147 if year == MAX_YEAR {
148 return Err(Error::InvalidComponent {
149 name: "weekday",
150 value: iso_weekday_number.to_string(),
151 span_start: Some(iso_weekday_span),
152 span_end: Some(iso_weekday_span),
153 });
154 }
155 Ok((year + 1, ordinal - days_in_cur_year))
156 } else {
157 Ok((year, ordinal))
158 }
159}
160
161pub(crate) fn ymd_to_yo(year: i32, month: u8, day: u8) -> (i32, u16) {
162 let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
163 [month.widen::<usize>() - 1]
164 + u16::from(month > 2 && is_leap_year(year));
165
166 (year, ordinal + u16::from(day))
167}