summaryrefslogtreecommitdiffstats
path: root/vendor/time-macros/src/lib.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:47:55 +0000
commit2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 (patch)
tree033cc839730fda84ff08db877037977be94e5e3a /vendor/time-macros/src/lib.rs
parentInitial commit. (diff)
downloadcargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.tar.xz
cargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.zip
Adding upstream version 0.70.1+ds1.upstream/0.70.1+ds1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/time-macros/src/lib.rs')
-rw-r--r--vendor/time-macros/src/lib.rs253
1 files changed, 253 insertions, 0 deletions
diff --git a/vendor/time-macros/src/lib.rs b/vendor/time-macros/src/lib.rs
new file mode 100644
index 0000000..d9e4693
--- /dev/null
+++ b/vendor/time-macros/src/lib.rs
@@ -0,0 +1,253 @@
+#![allow(
+ clippy::missing_const_for_fn, // irrelevant for proc macros
+ clippy::missing_docs_in_private_items, // TODO remove
+ clippy::std_instead_of_core, // irrelevant for proc macros
+ clippy::std_instead_of_alloc, // irrelevant for proc macros
+ clippy::alloc_instead_of_core, // irrelevant for proc macros
+ missing_docs, // TODO remove
+)]
+
+#[allow(unused_macros)]
+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;
+
+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;
+
+#[cfg(any(feature = "formatting", feature = "parsing"))]
+use std::iter::Peekable;
+
+use proc_macro::TokenStream;
+#[cfg(any(feature = "formatting", feature = "parsing"))]
+use proc_macro::{Ident, 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"))]
+enum FormatDescriptionVersion {
+ V1,
+ V2,
+}
+
+#[cfg(any(feature = "formatting", feature = "parsing"))]
+enum VersionOrModuleName {
+ Version(FormatDescriptionVersion),
+ #[cfg_attr(not(feature = "serde"), allow(unused_tuple_struct_fields))]
+ 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_with_version(version, &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, 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)?;
+
+ // 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, format_description_display) = match tokens.peek() {
+ // string literal
+ Some(TokenTree::Literal(_)) => {
+ 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! {
+ const ITEMS: &[::time::format_description::FormatItem<'_>] = &[#S(items)];
+ ITEMS
+ };
+
+ (items, String::from_utf8_lossy(&format_string).into_owned())
+ }
+ // path
+ Some(_) => {
+ let tokens = tokens.collect::<TokenStream>();
+ let tokens_string = tokens.to_string();
+ (
+ 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) as FORMAT;
+ }
+ __path_hack::FORMAT
+ }},
+ tokens_string,
+ )
+ }
+ None => return Err(Error::UnexpectedEndOfInput),
+ };
+
+ Ok(serde_format_description::build(
+ mod_name,
+ formattable,
+ format,
+ format_description_display,
+ ))
+ })()
+ .unwrap_or_else(|err: Error| err.to_compile_error_standalone())
+}