Skip to content

Commit

Permalink
Clean proc macro
Browse files Browse the repository at this point in the history
  • Loading branch information
nmeylan committed Mar 24, 2024
1 parent 087aeff commit dce939c
Show file tree
Hide file tree
Showing 9 changed files with 583 additions and 569 deletions.
67 changes: 67 additions & 0 deletions lib/enum_macro/src/enum_with_eq.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::Data::Enum;
use syn::{DeriveInput, parse_macro_input};
use syn::__private::TokenStream2;
use crate::helper::{get_number_value, is_numeric};

pub fn with_eq(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let enum_name = &input.ident;

if let Enum(enum_data) = &input.data {
let arms = enum_data.variants.iter().map(|variant| {
let variant_name = variant.ident.clone();
match &variant.fields {
syn::Fields::Unnamed(fields) => {
let variant_offset = if let Some(_) = variant.attrs.iter().find(|attr| {
attr.path().is_ident("value_comparison_offset")
}) {
get_number_value(variant, "value_comparison_offset").unwrap_or(0)
} else {
0
};
let mut args1 = fields.unnamed.iter().map(|_| quote! {_}).collect::<Vec<TokenStream2>>();
let mut args2 = fields.unnamed.iter().map(|_| quote! {_}).collect::<Vec<TokenStream2>>();
let mut should_deref = false;
if args1.len() > 1 {
args1[variant_offset] = quote! {variant1};
args2[variant_offset] = quote! {variant2};
should_deref = is_numeric(&fields.unnamed[variant_offset]);
}
// let field_types = fields.unnamed.iter().map(|field| &field.ty);
if fields.unnamed.len() > 1 {
if should_deref {
quote! {(#enum_name::#variant_name(#(#args1,)*), #enum_name::#variant_name(#(#args2,)*)) => *variant1 == *variant2, }
} else {
quote! {(#enum_name::#variant_name(#(#args1,)*), #enum_name::#variant_name(#(#args2,)*)) => variant1 == variant2, }
}
} else {
quote! {(#enum_name::#variant_name(#(#args1)*), #enum_name::#variant_name(#(#args2)*)) => true, }
}
}
syn::Fields::Unit => {
quote! {
(#enum_name::#variant_name, #enum_name::#variant_name) => true,
}
}
syn::Fields::Named(_) => {
// Handling named fields if necessary
panic!("Named fields are not supported in this macro")
}
}
});
TokenStream::from(quote! {
impl PartialEq for #enum_name {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
#(#arms)*
_ => false
}
}
}
})
} else {
TokenStream::from(quote! {})
}
}
86 changes: 86 additions & 0 deletions lib/enum_macro/src/enum_with_mask.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@

macro_rules! with_mask {
($function:ident, $trait:ident, $macro:ident, $type:ty, $max_bits:expr) => {

#[proc_macro_derive($macro, attributes(mask_value, mask_all))]
pub fn $function(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let enum_name = &input.ident;
fn count_number_of_1_bits(value: $type) -> usize {
let mut k: usize = 0;
// count number of 1 bits in value
loop {
if k > $max_bits {
break;
}
if value & (1 << k) != 0 {
return k;
}
k += 1;
}
0
}
let res = if let Enum(enum_data) = &input.data {
let mut i: usize = 0;
let from_value_match_arms = enum_data.variants.iter().enumerate().map(|(_, variant)| {
let variant_name = variant.ident.clone();
let maybe_value = crate::helper::get_number_value::<$type>(variant, "mask_value");
let is_all_value = crate::helper::is_all_value(variant, "mask_all");
let j = if let Some(value) = maybe_value {
i = count_number_of_1_bits(value);
value
} else if is_all_value {
<$type>::MAX
} else {
1 << i
};

let res = quote! {
#j => #enum_name::#variant_name,
};
i += 1;
res
});
let mut i: usize = 0;
let value_match_arms = enum_data.variants.iter().enumerate().map(|(_, variant)| {
let variant_name = variant.ident.clone();
let maybe_value = crate::helper::get_number_value::<$type>(variant, "mask_value");
let is_all_value = crate::helper::is_all_value(variant, "mask_all");
let j = if let Some(value) = maybe_value {
i = count_number_of_1_bits(value);
value
} else if is_all_value {
<$type>::MAX
} else {
1 << i
};
let res = quote! {
#enum_name::#variant_name => #j,
};
i += 1;
res
});
quote! {
impl $trait for #enum_name {
fn from_flag(value: $type) -> Self {
match value {
#(#from_value_match_arms)*
_ => panic!("Can't create enum_macro #enum_name for value {}", value)
}
}

fn as_flag(&self) -> $type {
match self {
#(#value_match_arms)*
_ => panic!("Value can't be found for enum_macro #enum_name")
}
}
}
}
} else {
quote! {}
};
TokenStream::from(res)
}
}
}
79 changes: 79 additions & 0 deletions lib/enum_macro/src/enum_with_number_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::Data::Enum;
use syn::{DeriveInput, parse_macro_input};
use crate::helper::get_number_value;

pub fn with_number_value(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let enum_name = &input.ident;

let res = if let Enum(enum_data) = &input.data {
let mut j: usize = 1;
let from_value_match_arms = enum_data.variants.iter().enumerate().map(|(_, variant)| {
let variant_name = variant.ident.clone();
let maybe_value = get_number_value::<usize>(variant, "value");
if let Some(value) = maybe_value {
j = value;
}
let res = quote! {
#j => #enum_name::#variant_name,
};
j += 1;
res
});
let mut j: usize = 1;
let try_from_value_match_arms =
enum_data.variants.iter().enumerate().map(|(_, variant)| {
let variant_name = variant.ident.clone();
let maybe_value = get_number_value::<usize>(variant, "value");
if let Some(value) = maybe_value {
j = value;
}
let res = quote! {
#j => Ok(#enum_name::#variant_name),
};
j += 1;
res
});
let mut j: usize = 1;
let value_match_arms = enum_data.variants.iter().enumerate().map(|(_, variant)| {
let variant_name = variant.ident.clone();
let maybe_value = get_number_value::<usize>(variant, "value");
if let Some(value) = maybe_value {
j = value;
}
let res = quote! {
#enum_name::#variant_name => #j,
};
j += 1;
res
});
quote! {
impl EnumWithNumberValue for #enum_name {
fn from_value(value: usize) -> Self {
match value {
#(#from_value_match_arms)*
_ => panic!("Can't create enum_macro #enum_name for value {}", value)
}
}
fn try_from_value(value: usize) -> Result<Self, String> {
match value {
#(#try_from_value_match_arms)*
_ => panic!("Can't create enum_macro #enum_name for value {}", value)
}
}

fn value(&self) -> usize {
match self {
#(#value_match_arms)*
_ => panic!("Value can't be found for enum_macro #enum_name")
}
}
}
}
} else {
quote! {}
};
TokenStream::from(res)
}
131 changes: 131 additions & 0 deletions lib/enum_macro/src/enum_with_stackable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use syn::__private::{Span, TokenStream2};
use syn::Data::Enum;
use syn::{DeriveInput, parse_macro_input};
use crate::helper::{field_type, get_number_value, is_numeric};

pub fn stackable_enum(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let enum_name = &input.ident;

if let Enum(enum_data) = &input.data {
let get_value_sum_arms = enum_data.variants.iter().filter(|variant| matches!(&variant.fields, syn::Fields::Unnamed(..))).map(|variant| {
let variant_name = variant.ident.clone();
if let syn::Fields::Unnamed(fields) = &variant.fields {
let value_offset = if let Some(_) = variant.attrs.iter().find(|attr| {
attr.path().is_ident("value_offset")
}) {
get_number_value(variant, "value_offset").unwrap_or(1)
} else {
if fields.unnamed.len() == 1 {
0
} else {
1
}
};
if is_numeric(&fields.unnamed[value_offset]) {
let mut args1 = fields.unnamed.iter().map(|_| quote! {_}).collect::<Vec<TokenStream2>>();
args1[value_offset] = quote! {val};
quote! {#enum_name::#variant_name(#(#args1,)*) => Some(*val as f32), }
} else {
quote! {}
}
} else {
panic!("patterns `Fields::Named(_)` and `Fields::Unit` not covered")
}
});
let get_value_sum_return_arms = enum_data.variants.iter().map(|variant| {
let variant_name = variant.ident.clone();
if let syn::Fields::Unnamed(fields) = &variant.fields {
let value_offset = if let Some(_) = variant.attrs.iter().find(|attr| {
attr.path().is_ident("value_offset")
}) {
get_number_value(variant, "value_offset").unwrap_or(1)
} else {
if fields.unnamed.len() == 1 {
0
} else {
1
}
};
if is_numeric(&fields.unnamed[value_offset]) {
let mut args1 = fields.unnamed.iter().enumerate().map(|(i, _)| {
let v = Ident::new(format!("value{}", i).as_str(), Span::call_site());
quote! {#v}
}).collect::<Vec<TokenStream2>>();
let mut args2 = args1.clone();
let val_type = Ident::new(field_type(&fields.unnamed[value_offset]).unwrap().as_str(), Span::call_site());
args1[value_offset] = quote! {_};
args2[value_offset] = quote! {&val as #val_type};
quote! {#enum_name::#variant_name(#(#args1,)*) => #enum_name::#variant_name(#(*#args2,)*), }
} else {
let args1 = fields.unnamed.iter().enumerate().map(|(i, _)| {
let v = Ident::new(format!("value{}", i).as_str(), Span::call_site());
quote! {#v}
}).collect::<Vec<TokenStream2>>();
quote! {#enum_name::#variant_name(#(#args1,)*) => #enum_name::#variant_name(#(*#args1,)*), }
}

} else {
quote! {#enum_name::#variant_name => #enum_name::#variant_name, }
}
});
let get_enum_value = enum_data.variants.iter().filter(|variant| matches!(&variant.fields, syn::Fields::Unnamed(..))).map(|variant| {
let variant_name = variant.ident.clone();
if let syn::Fields::Unnamed(fields) = &variant.fields {
let value_offset = if let Some(_) = variant.attrs.iter().find(|attr| {
attr.path().is_ident("value_offset")
}) {
get_number_value(variant, "value_offset").unwrap_or(1)
} else {
if fields.unnamed.len() == 1 {
0
} else {
1
}
};
if is_numeric(&fields.unnamed[value_offset]) {
let mut args1 = fields.unnamed.iter().map(|_| {
quote! {_}
}).collect::<Vec<TokenStream2>>();
args1[value_offset] = quote! {val};
quote! {#enum_name::#variant_name(#(#args1,)*) => *val as f32, }
} else {
quote!{}
}

} else {
panic!("patterns `Fields::Named(_)` and `Fields::Unit` not covered")
}
});
TokenStream::from(quote! {
impl EnumStackable<#enum_name> for #enum_name {
fn get_value_sum(single_enum: &#enum_name, enums: &Vec<#enum_name>) -> #enum_name {
let val: f32 = enums.into_iter().filter_map(|e|
if e == single_enum {
match e {
#(#get_value_sum_arms)*
_ => None
}
} else {
None
}
).sum();
match single_enum {
#(#get_value_sum_return_arms)*
}
}
fn get_enum_value<'a>(single_enum: &#enum_name, enums: &'a Vec<&#enum_name>) -> Option<f32> {
Self::get_enum(single_enum, enums).map(|b| match b {
#(#get_enum_value)*
_ => 0.0
})
}
}
})
} else {
TokenStream::from(quote! {})
}
}
Loading

0 comments on commit dce939c

Please sign in to comment.