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