1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
mod fmt;

use crate::fmt::format;
use quote::quote;
use syn::__private::TokenStream2;
use syn::{parse_macro_input, Data, DeriveInput, Fields};

#[proc_macro]
pub fn expr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = TokenStream2::from(input);
    let str_input = format(input.to_string());
    proc_macro::TokenStream::from(
        quote! {::shoulda::core::specifics::panic::Expression::new(#input, #str_input.to_string())},
    )
}

#[proc_macro_derive(Shoulda)]
pub fn shoulda(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = input.ident;
    let body = match input.data {
        Data::Struct(s) => match s.fields {
            Fields::Named(n) => n
                .named
                .iter()
                .map(|x| x.ident.as_ref().unwrap())
                .map(|x| {
                    quote! {
                        self.#x.should_eq::<Epsilon>(&other.#x)
                    }
                })
                .map(|x| x.to_string())
                .collect::<Vec<String>>()
                .join(" && ")
                .parse::<TokenStream2>()
                .unwrap(),
            Fields::Unnamed(u) => u
                .unnamed
                .iter()
                .enumerate()
                .map(|(x, _)| x.to_string().parse::<TokenStream2>().unwrap())
                .map(|x| {
                    quote! {
                        self.#x.should_eq::<Epsilon>(&other.#x)
                    }
                })
                .map(|x| x.to_string())
                .collect::<Vec<String>>()
                .join(" && ")
                .parse::<TokenStream2>()
                .unwrap(),
            Fields::Unit => quote! {
                true
            },
        },
        Data::Enum(e) => {
            let matches = e.variants.iter().map(|x| {
                let variant = &x.ident;
                match &x.fields {
                    Fields::Named(_) => {
                    let size = x.fields.iter().enumerate().map(|x| (x.1.ident.as_ref().unwrap().to_string(), format!("__{}",  x.0))).collect::<Vec<(String, String)>>();
                    let a_var_args = format!("{{{}}}", size.iter().map(|(a,b)|format!("{}:{}a", a,b)).collect::<Vec<String>>().join(","));
                    let b_var_args = format!("{{{}}}", size.iter().map(|(a,b)|format!("{}:{}b", a,b)).collect::<Vec<String>>().join(","));
                    let eval: String = size.iter().map(|x|format!("{0}a.should_eq::<Epsilon>({0}b)", x.1)).collect::<Vec<String>>().join(" && ");
                    format!("({name}::{variant}{a_var_args}, {name}::{variant}{b_var_args}) => {eval}, ",
                            name = name, variant = variant, a_var_args = a_var_args, b_var_args = b_var_args, eval = eval)
                        .parse::<TokenStream2>()
                        .unwrap()
                    }
                    Fields::Unnamed(_) => {
                    let size = x.fields.iter().enumerate().map(|x| format!("__{}", x.0)).collect::<Vec<String>>();
                    let a_var_args = format!("({})", size.iter().map(|x|format!("{}a", x)).collect::<Vec<String>>().join(","));
                    let b_var_args = format!("({})", size.iter().map(|x|format!("{}b", x)).collect::<Vec<String>>().join(","));
                    let eval: String = size.iter().map(|x|format!("{0}a.should_eq::<Epsilon>({0}b)", x)).collect::<Vec<String>>().join(" && ");
                    format!("({name}::{variant}{a_var_args}, {name}::{variant}{b_var_args}) => {eval}, ",
                            name = name, variant = variant, a_var_args = a_var_args, b_var_args = b_var_args, eval = eval)
                        .parse::<TokenStream2>()
                        .unwrap()
                    }
                    Fields::Unit => quote! {
                        (#name::#variant, #name::#variant) => true,
                    }
                }
            }).fold(String::new(), |acc, x| {
                acc + x.to_string().as_str()
            }).parse::<TokenStream2>().unwrap();
            quote! {
                match (self, other) {
                    #matches
                    _ => false
                }
            }
        }
        Data::Union(_) => panic!("assertable union types not supported"),
    };

    let generics = input.generics;

    let expanded = quote! {
        impl#generics ::shoulda::core::shoulda_equal::ShouldaEqual for #name#generics {
            fn should_eq<Epsilon: ::shoulda::core::epsilon_provider::EpsilonProvider>(&self, other: &Self) -> bool {
                #body
            }
        }
    };
    //panic!("{}", expanded.to_string());
    proc_macro::TokenStream::from(expanded)
}