comn-pms 0.1.0

proc macros used in comn
Documentation
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()
}