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
109
110
111
112
113
114
115
116
117
118
#[cfg(test)]
extern crate arbitrary;

extern crate syn;
#[macro_use]
extern crate synstructure;
extern crate proc_macro2;

use proc_macro2::TokenStream;

decl_derive!([Arbitrary] => arbitrary_derive);

fn arbitrary_derive(s: synstructure::Structure) -> TokenStream {
    if s.variants().len() == 1 { // struct
        let con = s.variants()[0].construct(|_, _| quote! { Arbitrary::arbitrary(u)? });
        s.gen_impl(quote! {
            extern crate arbitrary;

            use arbitrary::{Arbitrary, Unstructured};

            gen impl Arbitrary for @Self {
                fn arbitrary<U: Unstructured + ?Sized>(u: &mut U) -> Result<Self, U::Error> {
                    Ok(#con)
                }
            }
        })
    } else { // enum
        let mut variant_tokens = TokenStream::new();

        for (count, variant) in s.variants().iter().enumerate() {
            let count = count as u64;
            let constructor = variant.construct(|_, _| quote! { Arbitrary::arbitrary(u)? });
            variant_tokens.extend(quote! { #count => #constructor, });
        }
        let count = s.variants().len() as u64;
        s.gen_impl(quote! {
            extern crate arbitrary;

            use arbitrary::{Arbitrary, Unstructured};

            gen impl Arbitrary for @Self {
                fn arbitrary<U: Unstructured + ?Sized>(u: &mut U) -> Result<Self, U::Error> {

                    // use multiply + shift to generate a ranged random number
                    // with slight bias
                    // see https://lemire.me/blog/2016/06/30/fast-random-shuffling
                    Ok(match (u64::from(<u32 as Arbitrary>::arbitrary(u)?) * #count) >> 32 {
                        #variant_tokens
                        _ => unreachable!()
                    })
                }
            }
        })
    }
}

#[test]
fn test_arbitrary_struct() {
    test_derive!{
        arbitrary_derive {
            #[derive(Clone)]
            struct ArbitraryTest(u8, bool);
        }
        expands to {
            #[allow(non_upper_case_globals)]
            const _DERIVE_Arbitrary_FOR_ArbitraryTest : () = {
                extern crate arbitrary;

                use arbitrary::{Arbitrary, Unstructured};

                impl Arbitrary for ArbitraryTest {
                    fn arbitrary<U: Unstructured + ?Sized>(u: & mut U) -> Result<Self, U::Error> {
                        Ok(ArbitraryTest(Arbitrary::arbitrary(u)?,
                                         Arbitrary::arbitrary(u)?, ))
                    }
                }
            };
        }
    }
}

#[test]
fn test_arbitrary_enum() {
    test_derive!{
        arbitrary_derive {
            #[derive(Clone)]
            enum ArbitraryTest {
                A,
                B(usize, u32),
                C{ b: bool, d: (u16, u16) }
            }
        }
        expands to {
            #[allow(non_upper_case_globals)]
            const _DERIVE_Arbitrary_FOR_ArbitraryTest : () = {
                extern crate arbitrary;

                use arbitrary::{Arbitrary, Unstructured};

                impl Arbitrary for ArbitraryTest {
                    fn arbitrary<U: Unstructured + ?Sized>(u: & mut U) -> Result<Self, U::Error> {
                        Ok(match (u64::from(<u32 as Arbitrary>::arbitrary(u)?) * 3u64) >> 32 {
                            0u64 => ArbitraryTest::A,
                            1u64 => ArbitraryTest::B(Arbitrary::arbitrary(u)?,
                                                       Arbitrary::arbitrary(u)?,
                                                      ),
                            2u64 => ArbitraryTest::C {
                                    b : Arbitrary::arbitrary(u)?,
                                    d : Arbitrary::arbitrary(u)?,
                                },
                            _ => unreachable!()
                        })
                    }
                }
            };
        }
    }
}