extern crate proc_macro;
use once_cell::sync::Lazy;
use proc_macro::TokenStream;
use quote::{format_ident, quote, ToTokens};
use time::{macros::format_description, OffsetDateTime};
static COMPILE_TIME: Lazy<OffsetDateTime> = Lazy::new(OffsetDateTime::now_utc);
static RUSTC_VERSION: Lazy<rustc_version::Result<rustc_version::Version>> = Lazy::new(rustc_version::version);
#[proc_macro]
pub fn date(_item: TokenStream) -> TokenStream {
let date = COMPILE_TIME.date();
let year = date.year();
let month = format_ident!("{}", format!("{:?}", date.month()));
let day = date.day();
quote! {
match ::time::Date::from_calendar_date(#year, ::time::Month::#month, #day) {
Ok(date) => date,
_ => ::core::unreachable!(),
}
}
.into()
}
#[proc_macro]
pub fn date_str(_item: TokenStream) -> TokenStream {
let date = COMPILE_TIME.date();
let fmt = format_description!("[year]-[month]-[day]");
let date_str = date.format(&fmt).unwrap();
quote! { #date_str }.into()
}
#[proc_macro]
pub fn time(_item: TokenStream) -> TokenStream {
let time = COMPILE_TIME.time();
let hour = time.hour();
let minute = time.minute();
let second = time.second();
quote! {
match ::time::Time::from_hms(#hour, #minute, #second) {
Ok(time) => time,
_ => ::core::unreachable!(),
}
}
.into()
}
#[proc_macro]
pub fn time_str(_item: TokenStream) -> TokenStream {
let time = COMPILE_TIME.time();
let fmt = format_description!("[hour]:[minute]:[second]");
let time_str = time.format(&fmt).unwrap();
quote! { #time_str }.into()
}
#[proc_macro]
pub fn datetime(_item: TokenStream) -> TokenStream {
let datetime = *COMPILE_TIME;
let year = datetime.year();
let month = format_ident!("{}", format!("{:?}", datetime.month()));
let day = datetime.day();
let hour = datetime.hour();
let minute = datetime.minute();
let second = datetime.second();
let date = quote! {
match ::time::Date::from_calendar_date(#year, ::time::Month::#month, #day) {
Ok(date) => date,
_ => ::core::unreachable!(),
}
};
let time = quote! {
match ::time::Time::from_hms(#hour, #minute, #second) {
Ok(time) => time,
_ => ::core::unreachable!(),
}
};
quote! {
::time::PrimitiveDateTime::new(#date, #time).assume_utc()
}
.into()
}
#[proc_macro]
pub fn datetime_str(_item: TokenStream) -> TokenStream {
let datetime = *COMPILE_TIME;
let fmt = format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z");
let datetime_str = datetime.format(&fmt).unwrap();
quote! { #datetime_str }.into()
}
#[proc_macro]
pub fn unix(_item: TokenStream) -> TokenStream {
let datetime = *COMPILE_TIME;
let unix_timestamp = proc_macro2::Literal::i64_unsuffixed(datetime.unix_timestamp());
quote! {
#unix_timestamp
}
.into()
}
#[proc_macro]
pub fn rustc_version(_item: TokenStream) -> TokenStream {
let rustc_version::Version { major, minor, patch, pre, build } = match &*RUSTC_VERSION {
Ok(rustc_version) => rustc_version,
Err(err) => panic!("Failed to get version: {}", err),
};
let pre = if pre.is_empty() {
quote! { ::semver::Prerelease::EMPTY }
} else {
let pre = pre.as_str();
quote! {
if let Ok(pre) = ::semver::Prerelease::new(#pre) {
pre
} else {
::core::unreachable!()
}
}
};
let build = if build.is_empty() {
quote! { ::semver::BuildMetadata::EMPTY }
} else {
let build = build.as_str();
quote! {
if let Ok(build) = ::semver::BuildMetadata::new(#build) {
build
} else {
::core::unreachable!()
}
}
};
quote! {
::semver::Version {
major: #major,
minor: #minor,
patch: #patch,
pre: #pre,
build: #build,
}
}
.into()
}
#[proc_macro]
pub fn rustc_version_str(_item: TokenStream) -> TokenStream {
let rustc_version = match &*RUSTC_VERSION {
Ok(rustc_version) => rustc_version,
Err(err) => panic!("Failed to get version: {}", err),
};
let rustc_version_string = rustc_version.to_string();
quote! { #rustc_version_string }.into()
}
#[proc_macro]
pub fn rustc_version_major(_item: TokenStream) -> TokenStream {
let major = match &*RUSTC_VERSION {
Ok(rustc_version) => rustc_version.major,
Err(err) => panic!("Failed to get version: {}", err),
};
proc_macro2::Literal::u64_unsuffixed(major).to_token_stream().into()
}
#[proc_macro]
pub fn rustc_version_minor(_item: TokenStream) -> TokenStream {
let minor = match &*RUSTC_VERSION {
Ok(rustc_version) => rustc_version.minor,
Err(err) => panic!("Failed to get version: {}", err),
};
proc_macro2::Literal::u64_unsuffixed(minor).to_token_stream().into()
}
#[proc_macro]
pub fn rustc_version_patch(_item: TokenStream) -> TokenStream {
let patch = match &*RUSTC_VERSION {
Ok(rustc_version) => rustc_version.patch,
Err(err) => panic!("Failed to get version: {}", err),
};
proc_macro2::Literal::u64_unsuffixed(patch).to_token_stream().into()
}
#[proc_macro]
pub fn rustc_version_pre(_item: TokenStream) -> TokenStream {
let pre = match &*RUSTC_VERSION {
Ok(rustc_version) => rustc_version.pre.as_str(),
Err(err) => panic!("Failed to get version: {}", err),
};
quote! { #pre }.into()
}
#[proc_macro]
pub fn rustc_version_build(_item: TokenStream) -> TokenStream {
let build = match &*RUSTC_VERSION {
Ok(rustc_version) => rustc_version.build.as_str(),
Err(err) => panic!("Failed to get version: {}", err),
};
quote! { #build }.into()
}