-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
583 additions
and
569 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! {}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! {}) | ||
} | ||
} |
Oops, something went wrong.