diff options
Diffstat (limited to 'vendor/time-macros/src/lib.rs')
-rw-r--r-- | vendor/time-macros/src/lib.rs | 132 |
1 files changed, 120 insertions, 12 deletions
diff --git a/vendor/time-macros/src/lib.rs b/vendor/time-macros/src/lib.rs index 1afc313ea..84ad25113 100644 --- a/vendor/time-macros/src/lib.rs +++ b/vendor/time-macros/src/lib.rs @@ -1,7 +1,6 @@ #![deny( anonymous_parameters, clippy::all, - const_err, illegal_floating_point_literal_pattern, late_bound_lifetime_arguments, path_statements, @@ -34,8 +33,18 @@ clippy::option_if_let_else, // suggests terrible code )] +macro_rules! bug { + () => { compile_error!("provide an error message to help fix a possible bug") }; + ($descr:literal $($rest:tt)?) => { + unreachable!(concat!("internal error: ", $descr) $($rest)?) + } +} + #[macro_use] mod quote; +#[cfg(any(feature = "formatting", feature = "parsing"))] +#[macro_use] +mod shim; mod date; mod datetime; @@ -49,9 +58,12 @@ mod serde_format_description; mod time; mod to_tokens; +#[cfg(any(feature = "formatting", feature = "parsing"))] +use std::iter::Peekable; + use proc_macro::TokenStream; -#[cfg(all(feature = "serde", any(feature = "formatting", feature = "parsing")))] -use proc_macro::TokenTree; +#[cfg(any(feature = "formatting", feature = "parsing"))] +use proc_macro::{Ident, TokenTree}; use self::error::Error; @@ -76,11 +88,94 @@ macro_rules! impl_macros { impl_macros![date datetime offset time]; #[cfg(any(feature = "formatting", feature = "parsing"))] +enum FormatDescriptionVersion { + V1, + V2, +} + +#[cfg(any(feature = "formatting", feature = "parsing"))] +enum VersionOrModuleName { + Version(FormatDescriptionVersion), + ModuleName(Ident), +} + +#[cfg(any(feature = "formatting", feature = "parsing"))] +fn parse_format_description_version<const NO_EQUALS_IS_MOD_NAME: bool>( + iter: &mut Peekable<proc_macro::token_stream::IntoIter>, +) -> Result<Option<VersionOrModuleName>, Error> { + let version_ident = match iter.peek() { + Some(TokenTree::Ident(ident)) if ident.to_string() == "version" => match iter.next() { + Some(TokenTree::Ident(ident)) => ident, + _ => unreachable!(), + }, + _ => return Ok(None), + }; + match iter.peek() { + Some(TokenTree::Punct(punct)) if punct.as_char() == '=' => iter.next(), + _ if NO_EQUALS_IS_MOD_NAME => { + return Ok(Some(VersionOrModuleName::ModuleName(version_ident))); + } + Some(token) => { + return Err(Error::Custom { + message: "expected `=`".into(), + span_start: Some(token.span()), + span_end: Some(token.span()), + }); + } + None => { + return Err(Error::Custom { + message: "expected `=`".into(), + span_start: None, + span_end: None, + }); + } + }; + let version_literal = match iter.next() { + Some(TokenTree::Literal(literal)) => literal, + Some(token) => { + return Err(Error::Custom { + message: "expected 1 or 2".into(), + span_start: Some(token.span()), + span_end: Some(token.span()), + }); + } + None => { + return Err(Error::Custom { + message: "expected 1 or 2".into(), + span_start: None, + span_end: None, + }); + } + }; + let version = match version_literal.to_string().as_str() { + "1" => FormatDescriptionVersion::V1, + "2" => FormatDescriptionVersion::V2, + _ => { + return Err(Error::Custom { + message: "invalid format description version".into(), + span_start: Some(version_literal.span()), + span_end: Some(version_literal.span()), + }); + } + }; + helpers::consume_punct(',', iter)?; + + Ok(Some(VersionOrModuleName::Version(version))) +} + +#[cfg(any(feature = "formatting", feature = "parsing"))] #[proc_macro] pub fn format_description(input: TokenStream) -> TokenStream { (|| { + let mut input = input.into_iter().peekable(); + let version = match parse_format_description_version::<false>(&mut input)? { + Some(VersionOrModuleName::Version(version)) => Some(version), + None => None, + // This branch should never occur here, as `false` is the provided as a const parameter. + Some(VersionOrModuleName::ModuleName(_)) => bug!("branch should never occur"), + }; let (span, string) = helpers::get_string_literal(input)?; - let items = format_description::parse(&string, span)?; + let items = format_description::parse_with_version(version, &string, span)?; Ok(quote! {{ const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#S( @@ -100,12 +195,25 @@ pub fn format_description(input: TokenStream) -> TokenStream { pub fn serde_format_description(input: TokenStream) -> TokenStream { (|| { let mut tokens = input.into_iter().peekable(); - // First, an identifier (the desired module name) - let mod_name = match tokens.next() { - Some(TokenTree::Ident(ident)) => Ok(ident), - Some(tree) => Err(Error::UnexpectedToken { tree }), - None => Err(Error::UnexpectedEndOfInput), - }?; + + // First, the optional format description version. + let version = parse_format_description_version::<true>(&mut tokens)?; + let (version, mod_name) = match version { + Some(VersionOrModuleName::ModuleName(module_name)) => (None, Some(module_name)), + Some(VersionOrModuleName::Version(version)) => (Some(version), None), + None => (None, None), + }; + + // Next, an identifier (the desired module name) + // Only parse this if it wasn't parsed when attempting to get the version. + let mod_name = match mod_name { + Some(mod_name) => mod_name, + None => match tokens.next() { + Some(TokenTree::Ident(ident)) => Ok(ident), + Some(tree) => Err(Error::UnexpectedToken { tree }), + None => Err(Error::UnexpectedEndOfInput), + }?, + }; // Followed by a comma helpers::consume_punct(',', &mut tokens)?; @@ -126,8 +234,8 @@ pub fn serde_format_description(input: TokenStream) -> TokenStream { let (format, raw_format_string) = match tokens.peek() { // string literal Some(TokenTree::Literal(_)) => { - let (span, format_string) = helpers::get_string_literal(tokens.collect())?; - let items = format_description::parse(&format_string, span)?; + let (span, format_string) = helpers::get_string_literal(tokens)?; + let items = format_description::parse_with_version(version, &format_string, span)?; let items: TokenStream = items.into_iter().map(|item| quote! { #S(item), }).collect(); let items = quote! { &[#S(items)] }; |