diff options
Diffstat (limited to 'vendor/time-macros/src/lib.rs')
-rw-r--r-- | vendor/time-macros/src/lib.rs | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/vendor/time-macros/src/lib.rs b/vendor/time-macros/src/lib.rs new file mode 100644 index 000000000..1afc313ea --- /dev/null +++ b/vendor/time-macros/src/lib.rs @@ -0,0 +1,167 @@ +#![deny( + anonymous_parameters, + clippy::all, + const_err, + illegal_floating_point_literal_pattern, + late_bound_lifetime_arguments, + path_statements, + patterns_in_fns_without_body, + rust_2018_idioms, + trivial_casts, + trivial_numeric_casts, + unreachable_pub, + unsafe_code, + unused_extern_crates +)] +#![warn( + clippy::dbg_macro, + clippy::decimal_literal_representation, + clippy::get_unwrap, + clippy::nursery, + clippy::print_stdout, + clippy::todo, + clippy::unimplemented, + clippy::unnested_or_patterns, + clippy::unwrap_used, + clippy::use_debug, + single_use_lifetimes, + unused_qualifications, + variant_size_differences +)] +#![allow( + clippy::missing_const_for_fn, // useless in proc macro + clippy::redundant_pub_crate, // suggests bad style + clippy::option_if_let_else, // suggests terrible code +)] + +#[macro_use] +mod quote; + +mod date; +mod datetime; +mod error; +#[cfg(any(feature = "formatting", feature = "parsing"))] +mod format_description; +mod helpers; +mod offset; +#[cfg(all(feature = "serde", any(feature = "formatting", feature = "parsing")))] +mod serde_format_description; +mod time; +mod to_tokens; + +use proc_macro::TokenStream; +#[cfg(all(feature = "serde", any(feature = "formatting", feature = "parsing")))] +use proc_macro::TokenTree; + +use self::error::Error; + +macro_rules! impl_macros { + ($($name:ident)*) => {$( + #[proc_macro] + pub fn $name(input: TokenStream) -> TokenStream { + use crate::to_tokens::ToTokenTree; + + let mut iter = input.into_iter().peekable(); + match $name::parse(&mut iter) { + Ok(value) => match iter.peek() { + Some(tree) => Error::UnexpectedToken { tree: tree.clone() }.to_compile_error(), + None => TokenStream::from(value.into_token_tree()), + }, + Err(err) => err.to_compile_error(), + } + } + )*}; +} + +impl_macros![date datetime offset time]; + +#[cfg(any(feature = "formatting", feature = "parsing"))] +#[proc_macro] +pub fn format_description(input: TokenStream) -> TokenStream { + (|| { + let (span, string) = helpers::get_string_literal(input)?; + let items = format_description::parse(&string, span)?; + + Ok(quote! {{ + const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#S( + items + .into_iter() + .map(|item| quote! { #S(item), }) + .collect::<TokenStream>() + )]; + DESCRIPTION + }}) + })() + .unwrap_or_else(|err: Error| err.to_compile_error()) +} + +#[cfg(all(feature = "serde", any(feature = "formatting", feature = "parsing")))] +#[proc_macro] +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), + }?; + + // Followed by a comma + helpers::consume_punct(',', &mut tokens)?; + + // Then, the type to create serde serializers for (e.g., `OffsetDateTime`). + let formattable = match tokens.next() { + Some(tree @ TokenTree::Ident(_)) => Ok(tree), + Some(tree) => Err(Error::UnexpectedToken { tree }), + None => Err(Error::UnexpectedEndOfInput), + }?; + + // Another comma + helpers::consume_punct(',', &mut tokens)?; + + // We now have two options. The user can either provide a format description as a string or + // they can provide a path to a format description. If the latter, all remaining tokens are + // assumed to be part of the path. + 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 items: TokenStream = + items.into_iter().map(|item| quote! { #S(item), }).collect(); + let items = quote! { &[#S(items)] }; + + ( + items, + Some(String::from_utf8_lossy(&format_string).into_owned()), + ) + } + // path + Some(_) => ( + quote! {{ + // We can't just do `super::path` because the path could be an absolute path. In + // that case, we'd be generating `super::::path`, which is invalid. Even if we + // took that into account, it's not possible to know if it's an external crate, + // which would just require emitting `path` directly. By taking this approach, + // we can leave it to the compiler to do the actual resolution. + mod __path_hack { + pub(super) use super::super::*; + pub(super) use #S(tokens.collect::<TokenStream>()) as FORMAT; + } + __path_hack::FORMAT + }}, + None, + ), + None => return Err(Error::UnexpectedEndOfInput), + }; + + Ok(serde_format_description::build( + mod_name, + formattable, + format, + raw_format_string, + )) + })() + .unwrap_or_else(|err: Error| err.to_compile_error_standalone()) +} |