| 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 |  | 
|---|