1 use crate::{ 2 ext::LitIntExtension, 3 time_crate, 4 time_crate::{days_in_year, days_in_year_month, weeks_in_year}, 5 }; 6 use proc_macro2::TokenStream; 7 use quote::{quote, ToTokens}; 8 use syn::{ 9 parse::{Parse, ParseStream}, 10 Ident, LitInt, Result, Token, 11 }; 12 13 pub(crate) struct Date { 14 year: i32, 15 ordinal: u16, 16 } 17 18 impl Parse for Date { parse(input: ParseStream<'_>) -> Result<Self>19 fn parse(input: ParseStream<'_>) -> Result<Self> { 20 let (year, year_span) = { 21 let year_sign = if input.peek(Token![+]) { 22 input.parse::<Token![+]>()?; 23 1 24 } else if input.peek(Token![-]) { 25 input.parse::<Token![-]>()?; 26 -1 27 } else { 28 1 29 }; 30 let year = input.parse::<LitInt>()?; 31 (year_sign * year.value::<i32>()?, year.span()) 32 }; 33 input.parse::<Token![-]>()?; 34 35 // year-week-day 36 #[allow(clippy::manual_strip)] 37 let (year, ordinal) = if input.peek(Ident) { 38 let week = { 39 let week = input.parse::<Ident>()?; 40 let week_str = week.to_string(); 41 if week_str.starts_with('W') { 42 LitInt::new(&week_str[1..], week.span()) 43 } else { 44 return error!(week.span(), "expected week value to start with `W`"); 45 } 46 }; 47 input.parse::<Token![-]>()?; 48 let day = input.parse::<LitInt>()?; 49 50 week.ensure_in_range(0..=weeks_in_year(year) as isize)?; 51 day.ensure_in_range(1..=7)?; 52 53 time_crate::Date::from_iso_ywd_unchecked(year, week.value()?, day.value()?).as_yo() 54 } 55 // year-month-day 56 else if input.peek2(Token![-]) { 57 let month = input.parse::<LitInt>()?; 58 input.parse::<Token![-]>()?; 59 let day = input.parse::<LitInt>()?; 60 61 month.ensure_in_range(1..=12)?; 62 day.ensure_in_range(1..=days_in_year_month(year, month.value()?) as isize)?; 63 64 time_crate::Date::from_ymd_unchecked(year, month.value()?, day.value()?).as_yo() 65 } 66 // year-ordinal 67 else { 68 let ordinal = input.parse::<LitInt>()?; 69 ordinal.ensure_in_range(1..=days_in_year(year) as isize)?; 70 (year, ordinal.value()?) 71 }; 72 73 LitInt::create(year) 74 .with_span(year_span) 75 .ensure_in_range(-100_000..=100_000)?; 76 77 Ok(Self { year, ordinal }) 78 } 79 } 80 81 impl ToTokens for Date { to_tokens(&self, tokens: &mut TokenStream)82 fn to_tokens(&self, tokens: &mut TokenStream) { 83 let Self { year, ordinal } = self; 84 85 tokens.extend(quote! { 86 ::time::internals::Date::from_yo_unchecked(#year, #ordinal) 87 }) 88 } 89 } 90