use chrono::prelude::*;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::ToTokens;
use std::{str::FromStr, sync::atomic::AtomicI64};
use syn::{
parse::{Parse, ParseStream},
Block, Ident, LitInt, LitStr, Token, TypePath,
};
#[proc_macro]
pub fn compile_time(_: TokenStream) -> TokenStream {
let dt = Local::now();
let current = format!(
"{}{:02}{:02}.{:02}{:02}{:02}",
dt.year(),
dt.month(),
dt.day(),
dt.hour(),
dt.minute(),
dt.second()
);
let expanded = quote::quote! {
#current
};
expanded.into()
}
enum Parser {
_Block(Block),
_Ident(Ident),
}
impl Parser {
fn parse_ident(input: ParseStream) -> syn::Result<Ident> {
let mut symbol = String::new();
let ident: Ident = input.parse()?;
input.parse::<Token![,]>()?;
let n: LitInt = input.parse()?;
symbol.push_str(ident.to_string().trim_start_matches("r#"));
symbol.push('_');
symbol.push_str(n.to_string().as_str());
Ok(Ident::new(symbol.as_str(), ident.span()))
}
}
impl Parse for Parser {
fn parse(input: ParseStream) -> syn::Result<Self> {
if !input.peek(Ident) {
return Ok(Parser::_Block(input.parse()?));
}
Ok(Parser::_Ident(Parser::parse_ident(input)?))
}
}
fn id64() -> i64 {
static N: AtomicI64 = AtomicI64::new(0);
N.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
}
#[proc_macro]
pub fn int64(input: TokenStream) -> TokenStream {
struct MacroLiteral(i64);
impl Parse for MacroLiteral {
fn parse(input: ParseStream) -> syn::Result<Self> {
if !input.peek(LitInt) {
return Ok(MacroLiteral(0));
}
let n: LitInt = input.parse()?;
Ok(MacroLiteral(n.to_string().parse().unwrap()))
}
}
let MacroLiteral(n) = syn::parse_macro_input!(input as MacroLiteral);
let mut mask: i64 = -1;
if n != 0 {
mask = 0xfff << 52;
}
let id = n << 52 | id64() & mask;
let expanded = quote::quote! {
#id
};
expanded.into()
}
#[proc_macro]
pub fn str64(input: TokenStream) -> TokenStream {
struct MacroLiteral(String);
impl Parse for MacroLiteral {
fn parse(input: ParseStream) -> syn::Result<Self> {
if !input.peek(LitStr) {
return Ok(MacroLiteral("".to_owned()));
}
let s: LitStr = input.parse()?;
Ok(MacroLiteral(s.value()))
}
}
let MacroLiteral(s) = syn::parse_macro_input!(input as MacroLiteral);
let id = format!("{}{}", s, id64());
let expanded = quote::quote! {
#id
};
expanded.into()
}
#[proc_macro]
pub fn _suffix(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as Parser);
match input {
Parser::_Block(b) => {
let stmts = b.stmts;
let seq = LitInt::new(&id64().to_string(), Span::call_site());
let expanded = quote::quote! {
macro_rules! n {
($iden:ident) => {
$crate::_suffix!($iden, #seq)
}
}
#(#stmts)*
};
expanded.into()
}
Parser::_Ident(i) => {
let expanded = quote::quote! {
#i
};
expanded.into()
}
}
}
#[proc_macro]
pub fn make(input: TokenStream) -> TokenStream {
fn gen_code(code: &mut String, template: &str, typename: &str, iface: &str) {
match iface {
"Send" | "Sync" => code.push_str(
format!("unsafe impl{} {} for {} {{}}\n", template, iface, typename).as_str(),
),
"Default" => code.push_str(
format!(
"impl{} {} for {} {{ fn default() -> Self {{ Self::new() }} }}\n",
template, iface, typename
)
.as_str(),
),
_ => panic!("only Send, Sync, Default is allowed"),
};
}
struct MacroLiteral(String);
impl Parse for MacroLiteral {
fn parse(input: ParseStream) -> syn::Result<Self> {
let symbol: TypePath = input.parse()?;
input.parse::<Token!(:)>()?;
let mut typename = symbol.to_token_stream().to_string();
typename.retain(|c| !c.is_whitespace());
let template = match typename.find('<') {
Some(idx) => &typename[idx..],
None => "",
};
fn keep() -> impl FnMut(&char) -> bool {
let mut level = 0;
let mut inner: Option<Box<dyn FnMut(&char) -> bool>> = None;
move |c: &char| -> bool {
match inner {
Some(ref mut fptr) => {
if *c == '<' {
level += 1;
} else if *c == '>' {
level -= 1;
if level == -1 {
inner.take();
return true;
}
}
fptr(c)
}
None => {
if level == 0 {
if *c == ':' {
level = 1;
} else if *c == '<' {
inner = Some(Box::new(keep()));
}
} else if *c == ',' || *c == '>' {
level -= 1;
} else if *c == '<' {
level += 1;
}
level == 0
}
}
}
}
let typename: String = typename.chars().filter(keep()).collect();
let mut code = String::new();
loop {
let symbol: TypePath = input.parse()?;
let iface = symbol.path.get_ident().take().unwrap().to_string();
gen_code(&mut code, template, &typename, &iface);
if !input.peek(Token!(,)) {
break;
}
input.parse::<Token!(,)>()?;
}
Ok(MacroLiteral(code))
}
}
let input = syn::parse_macro_input!(input as MacroLiteral);
TokenStream::from_str(input.0.as_str()).unwrap()
}