facilitest-macros 0.1.0

internal dependency of 'facilitest' package
Documentation
use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
use std::iter;

#[proc_macro]
pub fn build_ident(input: TokenStream) -> TokenStream {
    let mut ident_name = String::new();
    for token in input.into_iter() {
        match token {
            TokenTree::Ident(ident) => ident_name += &ident.to_string(),
            TokenTree::Group(group) => {
                if let Some(ident) = group.stream().into_iter().last() {
                    ident_name += &ident.to_string();
                }
            }
            _ => (),
        }
    }
    TokenStream::from(TokenTree::Ident(Ident::new(&ident_name, Span::call_site())))
}

#[proc_macro]
pub fn make_test_fn(input: TokenStream) -> TokenStream {
    let mut tokens = input.into_iter();
    let mut ts = TokenStream::new();

    // unwrap macro arguments
    let func = tokens.next().unwrap();
    let _ = tokens.next().unwrap();
    let suffix = tokens.next().unwrap();
    let _ = tokens.next().unwrap();
    let args = tokens.next().unwrap();
    let _ = tokens.next().unwrap();
    let expected = tokens.next().unwrap();

    // start with "test" attribute
    let ts_attr = TokenStream::from_iter([
        TokenTree::from(Punct::new('#', Spacing::Alone)),
        TokenTree::from(Group::new(
            Delimiter::Bracket,
            TokenStream::from(TokenTree::from(Ident::new("test", Span::call_site()))),
        )),
    ]);
    ts.extend(ts_attr);

    // append "fn" keyword
    ts.extend(iter::once(TokenTree::from(Ident::new(
        "fn",
        Span::call_site(),
    ))));

    // append function name
    let ts_name = TokenStream::from_iter([
        TokenTree::from(Ident::new("test_", Span::call_site())),
        func.clone(),
        suffix,
    ]);
    let name = build_ident(ts_name);
    ts.extend(iter::once(name));

    // add empty parameter list
    ts.extend(iter::once(TokenTree::from(Group::new(
        Delimiter::Parenthesis,
        TokenStream::new(),
    ))));

    // add function body
    let mut ts_body = TokenStream::new();
    if let TokenTree::Group(g_args) = args {
        ts_body.extend([
            TokenTree::from(Ident::new("assert_eq", Span::call_site())),
            TokenTree::from(Punct::new('!', Spacing::Alone)),
            TokenTree::from(Group::new(
                Delimiter::Parenthesis,
                TokenStream::from_iter([
                    func,
                    TokenTree::from(Group::new(
                        Delimiter::Parenthesis,
                        g_args.stream()
                    )),
                    TokenTree::from(Punct::new(',', Spacing::Alone)),
                    expected,
                ]),
            )),
            TokenTree::from(Punct::new(';', Spacing::Alone)),
        ]);
    }

    ts.extend(iter::once(TokenTree::from(Group::new(
        Delimiter::Brace,
        ts_body,
    ))));

    ts
}