1 use crate::ext::LitIntExtension; 2 use proc_macro2::TokenStream; 3 use quote::{quote, ToTokens}; 4 use syn::{ 5 parse::{Parse, ParseStream}, 6 LitFloat, LitInt, Result, Token, 7 }; 8 9 #[derive(PartialEq)] 10 enum AmPm { 11 Am, 12 Pm, 13 } 14 15 impl Parse for AmPm { parse(input: ParseStream<'_>) -> Result<Self>16 fn parse(input: ParseStream<'_>) -> Result<Self> { 17 use crate::kw::{am, pm, AM, PM}; 18 if input.peek(am) { 19 input.parse::<am>()?; 20 Ok(AmPm::Am) 21 } else if input.peek(AM) { 22 input.parse::<AM>()?; 23 Ok(AmPm::Am) 24 } else if input.peek(pm) { 25 input.parse::<pm>()?; 26 Ok(AmPm::Pm) 27 } else if input.peek(PM) { 28 input.parse::<PM>()?; 29 Ok(AmPm::Pm) 30 } else { 31 error!("expected am or pm") 32 } 33 } 34 } 35 36 pub(crate) struct Time { 37 pub(crate) hour: LitInt, 38 pub(crate) minute: LitInt, 39 pub(crate) second: LitInt, 40 pub(crate) nanosecond: LitInt, 41 } 42 43 impl Parse for Time { parse(input: ParseStream<'_>) -> Result<Self>44 fn parse(input: ParseStream<'_>) -> Result<Self> { 45 let mut hour = input.parse::<LitInt>()?; 46 input.parse::<Token![:]>()?; 47 let minute = input.parse::<LitInt>()?; 48 49 // Seconds and nanoseconds are optional, defaulting to zero. 50 let (second, nanosecond) = if input.peek(Token![:]) { 51 input.parse::<Token![:]>()?; 52 53 if input.peek(LitFloat) { 54 let float = input.parse::<LitFloat>()?; 55 56 // Temporary value to satisfy the compiler. 57 let float_str = float.to_string(); 58 let parts: Vec<_> = float_str.splitn(2, '.').collect(); 59 60 let seconds = LitInt::new(parts[0], float.span()); 61 let nanoseconds = { 62 // Prepend a zero to avoid having an empty string. 63 // Strip the suffix to avoid syn thinking it's a float. 64 let raw_padded = format!("0{}", parts[1].trim_end_matches(float.suffix())); 65 66 // Take an extra digit due to the padding. 67 let digits: String = raw_padded 68 .chars() 69 .filter(char::is_ascii_digit) 70 .take(10) 71 .collect(); 72 73 // Scale the value based on how many digits were provided. 74 let value = LitInt::new(&digits, float.span()).base10_parse::<usize>()? 75 * 10_usize.pow(10 - digits.len() as u32); 76 77 LitInt::create(value).with_span(float.span()) 78 }; 79 80 (seconds, nanoseconds) 81 } else { 82 (input.parse::<LitInt>()?, LitInt::create(0)) 83 } 84 } else { 85 (LitInt::create(0), LitInt::create(0)) 86 }; 87 88 let am_pm = input.parse::<AmPm>().ok(); 89 90 // Ensure none of the components are out of range. 91 match am_pm { 92 Some(am_pm) => { 93 hour.ensure_in_range(1..=12)?; 94 // Adjust the hour if necessary. 95 hour = match (hour.value()?, am_pm) { 96 (12, AmPm::Am) => LitInt::create(0).with_span(hour.span()), 97 (value, AmPm::Pm) if value != 12 => { 98 LitInt::create(value + 12).with_span(hour.span()) 99 } 100 _ => hour, 101 } 102 } 103 None => hour.ensure_in_range(0..24)?, 104 } 105 minute.ensure_in_range(0..60)?; 106 second.ensure_in_range(0..60)?; 107 // This likely isn't necessary, but it can't hurt. 108 nanosecond.ensure_in_range(0..1_000_000_000)?; 109 110 Ok(Self { 111 hour, 112 minute, 113 second, 114 nanosecond, 115 }) 116 } 117 } 118 119 impl ToTokens for Time { to_tokens(&self, tokens: &mut TokenStream)120 fn to_tokens(&self, tokens: &mut TokenStream) { 121 let Self { 122 hour, 123 minute, 124 second, 125 nanosecond, 126 } = self; 127 128 tokens.extend(quote! { 129 ::time::internals::Time::from_hms_nanos_unchecked(#hour, #minute, #second, #nanosecond) 130 }); 131 } 132 } 133