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