]> git.lizzy.rs Git - generate-random.git/commitdiff
Add trait to generate random enum with fixed variant
authorLizzy Fleckenstein <eliasfleckenstein@web.de>
Fri, 10 Feb 2023 13:37:14 +0000 (14:37 +0100)
committerLizzy Fleckenstein <eliasfleckenstein@web.de>
Fri, 10 Feb 2023 14:01:05 +0000 (15:01 +0100)
derive-macro/src/handle_enum.rs
lib/src/lib.rs

index 8165ab5cdc8ae694d1afb8cf2792971adb05eb85..ecda02bc78f0b1b655f2d67fc7f71c84e311e772 100644 (file)
@@ -15,35 +15,40 @@ fn variant_weight(variant: &Variant) -> Literal {
 }
 
 pub fn generate(name: &Ident, ty: DataEnum) -> TokenStream {
-    let mut variant_weights = ty
+    let variant_weights = ty
         .variants
         .into_iter()
-        .map(|variant| (variant_weight(&variant), variant));
+        .enumerate()
+        .map(|(i, variant)| (i, variant_weight(&variant), variant));
 
     let mut arms = TokenStream::new();
+    let mut arms_variant = TokenStream::new();
+    let mut arms_variant_name = TokenStream::new();
+    let mut num_variants: usize = 0;
+
     let mut total_weight = quote! { 0 };
-    if let Some((weight, variant)) = variant_weights.next() {
+    for (index, weight, variant) in variant_weights {
         let variant_name = variant.ident;
-        let fields = generate_fields(variant.fields);
         arms.extend(quote! {
-            let end = #weight;
-            if 0 <= value && value < end {
-                return Self::#variant_name #fields
+            let start = end;
+            let end = start + #weight;
+            if start <= value && value < end {
+                return generate_random::GenerateRandomVariant::generate_random_variant(rng, #index);
             }
         });
-        total_weight = quote! { #weight };
-        for (weight, variant) in variant_weights {
-            let variant_name = variant.ident;
-            let fields = generate_fields(variant.fields);
-            arms.extend(quote! {
-                let start = end;
-                let end = start + #weight;
-                if start <= value && value < end {
-                    return Self::#variant_name #fields
-                }
-            });
-            total_weight = quote! { #total_weight + #weight };
-        }
+
+        let fields = generate_fields(variant.fields);
+        arms_variant.extend(quote! {
+            #index => Self::#variant_name #fields,
+        });
+
+        let variant_str = variant_name.to_string();
+        arms_variant_name.extend(quote! {
+            #index => #variant_str,
+        });
+
+        total_weight = quote! { #total_weight + #weight };
+        num_variants += 1;
     }
 
     quote! {
@@ -51,9 +56,30 @@ pub fn generate(name: &Ident, ty: DataEnum) -> TokenStream {
             fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self {
                 let total_weight = #total_weight;
                 let value = rng.gen_range(0..total_weight);
+                let end = 0;
                 #arms
                 unreachable!()
             }
         }
+
+        impl generate_random::GenerateRandomVariant for #name {
+            fn num_variants() -> usize {
+                #num_variants
+            }
+
+            fn variant_name(variant: usize) -> &'static str {
+                match variant {
+                    #arms_variant_name
+                    _ => "",
+                }
+            }
+
+            fn generate_random_variant<R: rand::Rng + ?Sized>(rng: &mut R, variant: usize) -> Self {
+                match variant {
+                    #arms_variant
+                    _ => generate_random::GenerateRandom::generate_random(rng),
+                }
+            }
+        }
     }
 }
index 4995eb69103bb630194eb6e395a2ade1b0e750bd..b25f91e81ee8ab41ab8720bd45ee2db779b82d49 100644 (file)
@@ -41,6 +41,22 @@ pub trait GenerateRandom {
     fn generate_random<R: rand::Rng + ?Sized>(rng: &mut R) -> Self;
 }
 
+/// Enable randomly generating values of an enum
+/// with a predefined variant
+///
+/// This trait is automatically implemented for enums
+/// by the [`macro@GenerateRandom`] macro
+pub trait GenerateRandomVariant {
+    /// Return number of variants
+    fn num_variants() -> usize;
+
+    /// Return name of variant with index
+    fn variant_name(variant: usize) -> &'static str;
+
+    /// Create a randomly generated value with a predefied variant
+    fn generate_random_variant<R: rand::Rng + ?Sized>(rng: &mut R, variant: usize) -> Self;
+}
+
 macro_rules! impl_generate_random {
        ( $( $t:ty, )+ ) => {
                $(