test-case-derive 0.2.3

Provides #[test_case(...)] procedural macro attribute for generating parametrized test cases easily
Documentation
use prelude::*;

pub fn gen_test_case(test_fn_name: Token, token_tree: &Vec<TokenTree>) -> Tokens {
    let (name, leftover)       = given_test_case_name(token_tree);
    let (expected, leftover)   = expected_result(leftover);
    let args                   = leftover;
    let maybe_ignore_attribute = ignore_attribute(name.as_ref());
    let name                   = name.unwrap_or_else(|| generated_test_case_name(args));
    let body                   = test_case_body(test_fn_name, args, expected);

    quote! {
        #[test]
        #maybe_ignore_attribute
        fn #name() {
            #body
        }
    }
}

fn given_test_case_name(token_tree: &[TokenTree]) -> (Option<Token>, &[TokenTree]) {
    let mut iter     = token_tree.into_iter().rev();
    let last         = iter.next();
    let last_but_one = iter.next();

    match (last, last_but_one) {
        (
            Some(&TTToken(TLiteral(Str(ref case_name, _)))),
            Some(&TTToken(TModSep))
        ) =>
            (
                Some(escape_ident(case_name)),
                &token_tree[0..token_tree.len() - 2]
            ),
        _ => (None, token_tree)
    }
}

fn generated_test_case_name(args: &[TokenTree]) -> Token {
    escape_ident(args.to_string())
}

fn test_case_body(test_fn_name: Token, args: &[TokenTree], expected: &[TokenTree]) -> Tokens {
    match expected.len() {
        0 => quote! {
            #test_fn_name(#(#args)*);
        },
        _ => quote! {
            let expected = #(#expected)*;
            let actual   = #test_fn_name(#(#args)*);

            assert_eq!(expected, actual);
        }
    }
}

fn expected_result(token_tree: &[TokenTree]) -> (&[TokenTree], &[TokenTree]) {
    for i in (0..token_tree.len()).rev() {
        if &TTToken(TFatArrow) == &token_tree[i] {
            return (
                &token_tree[i+1..token_tree.len()],
                &token_tree[0..i]
            )
        }
    }

    (&[], token_tree)
}

fn ignore_attribute(given_name: Option<&Token>) -> Tokens {
    if let Some(name) = given_name {
        if quote!(#name).to_string().contains("inconclusive") {
            return quote! { #[ignore] }
        }
    }

    quote! {}
}