summaryrefslogtreecommitdiffstats
path: root/vendor/time-macros/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/time-macros/src/lib.rs')
-rw-r--r--vendor/time-macros/src/lib.rs167
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())
+}