mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 16:44:06 +02:00
use syn_util for attribute handling
This commit is contained in:
parent
bbbce0f8cf
commit
10407f25a8
2 changed files with 85 additions and 99 deletions
|
|
@ -15,6 +15,7 @@ proc-macro = true
|
||||||
syn = { version = "0.15" }
|
syn = { version = "0.15" }
|
||||||
quote = "0.6"
|
quote = "0.6"
|
||||||
proc-macro2 = "0.4"
|
proc-macro2 = "0.4"
|
||||||
|
syn_util = {version = "0.2", git = "https://github.com/icewind1991/syn_util", branch = "lit-cast"}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bitstream_reader = { version = "0.5", path = ".." }
|
bitstream_reader = { version = "0.5", path = ".." }
|
||||||
|
|
@ -140,8 +140,9 @@ use quote::{quote, quote_spanned};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, parse_quote, parse_str, Attribute, Data, DeriveInput, Expr, Fields, Ident,
|
parse_macro_input, parse_quote, parse_str, Attribute, Data, DeriveInput, Expr, Fields, Ident,
|
||||||
Lit, LitStr, Meta, Path,
|
Lit, LitStr, Path, Variant,
|
||||||
};
|
};
|
||||||
|
use syn_util::get_attribute_value;
|
||||||
|
|
||||||
/// See the [crate documentation](index.html) for details
|
/// See the [crate documentation](index.html) for details
|
||||||
#[proc_macro_derive(
|
#[proc_macro_derive(
|
||||||
|
|
@ -151,6 +152,7 @@ use syn::{
|
||||||
pub fn derive_bitread(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_bitread(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
derive_bitread_trait(input, "BitRead".to_owned(), None)
|
derive_bitread_trait(input, "BitRead".to_owned(), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
/// See the [crate documentation](index.html) for details
|
/// See the [crate documentation](index.html) for details
|
||||||
#[proc_macro_derive(
|
#[proc_macro_derive(
|
||||||
|
|
@ -171,10 +173,7 @@ fn derive_bitread_trait(
|
||||||
|
|
||||||
let name = &input.ident;
|
let name = &input.ident;
|
||||||
|
|
||||||
let endianness = get_attr(&input.attrs, "endianness").map(|lit| match lit {
|
let endianness = get_attribute_value(&input.attrs, &["endianness"]);
|
||||||
Lit::Str(str) => str.value(),
|
|
||||||
_ => panic!("endianness attribute is required to be a string"),
|
|
||||||
});
|
|
||||||
let mut trait_generics = input.generics.clone();
|
let mut trait_generics = input.generics.clone();
|
||||||
// we need these separate generics to only add out Endianness param to the 'impl'
|
// we need these separate generics to only add out Endianness param to the 'impl'
|
||||||
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
|
@ -255,96 +254,61 @@ fn parse(data: &Data, struct_name: &Ident, attrs: &Vec<Attribute>) -> TokenStrea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Data::Enum(ref data) => {
|
Data::Enum(ref data) => {
|
||||||
let discriminant_bits = match get_attr(attrs, "discriminant_bits") {
|
let discriminant_bits: u64 = get_attribute_value(attrs, &["discriminant_bits"]).expect(
|
||||||
Some(bits_lit) => match bits_lit {
|
"'discriminant_bits' attribute is required when deriving `BinRead` for enums",
|
||||||
Lit::Int(bits) => bits.value(),
|
);
|
||||||
_ => {
|
|
||||||
panic!("'discriminant_bits' attribute is required to be an integer literal")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => panic!(
|
|
||||||
"'discriminant_bits' attribute is required when deriving `BinRead` for enums"
|
|
||||||
),
|
|
||||||
};
|
|
||||||
let discriminant_read = quote! {
|
|
||||||
let discriminant:usize = stream.read_int(#discriminant_bits as usize)?;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut last_discriminant = -1;
|
let mut last_discriminant = -1;
|
||||||
let mut discriminants = Vec::with_capacity(data.variants.len());
|
let match_arms = data.variants.iter().map(|variant| {
|
||||||
for variant in &data.variants {
|
let span = variant.span();
|
||||||
let discriminant: Option<usize> = variant
|
let variant_name = &variant.ident;
|
||||||
.discriminant
|
let read_fields = match &variant.fields {
|
||||||
.clone()
|
Fields::Unit => quote_spanned! {span=>
|
||||||
.map(|(_, expr)| match expr {
|
#struct_name::#variant_name
|
||||||
Expr::Lit(expr_lit) => expr_lit.lit,
|
},
|
||||||
_ => panic!("discriminant is required to be an integer literal"),
|
Fields::Unnamed(f) => {
|
||||||
})
|
let size = get_field_size(&variant.attrs, f.span());
|
||||||
.or_else(|| get_attr(&variant.attrs, "discriminant"))
|
match size {
|
||||||
.map(|lit| match lit {
|
Some(size) => {
|
||||||
Lit::Int(lit) => Some(lit.value() as usize),
|
quote_spanned! {span=>
|
||||||
Lit::Str(lit) => match lit.value().as_str() {
|
#struct_name::#variant_name({
|
||||||
"_" => None,
|
let _size:usize = #size;
|
||||||
_ => {
|
stream.read_sized(_size)?
|
||||||
panic!("discriminant is required to be an integer literal or \"_\"")
|
})
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => panic!("discriminant is required to be an integer literal or \"_\""),
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| Some((last_discriminant + 1) as usize));
|
|
||||||
if let Some(disc) = discriminant {
|
|
||||||
last_discriminant = disc as isize;
|
|
||||||
}
|
|
||||||
discriminants.push(discriminant)
|
|
||||||
}
|
|
||||||
let match_arms =
|
|
||||||
data.variants
|
|
||||||
.iter()
|
|
||||||
.zip(discriminants.iter())
|
|
||||||
.map(|(variant, discriminant)| {
|
|
||||||
let span = variant.span();
|
|
||||||
let variant_name = &variant.ident;
|
|
||||||
let read_fields = match &variant.fields {
|
|
||||||
Fields::Unit => quote_spanned! {span=>
|
|
||||||
#struct_name::#variant_name
|
|
||||||
},
|
|
||||||
Fields::Unnamed(f) => {
|
|
||||||
let size = get_field_size(&variant.attrs, f.span());
|
|
||||||
match size {
|
|
||||||
Some(size) => {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#struct_name::#variant_name({
|
|
||||||
let _size:usize = #size;
|
|
||||||
stream.read_sized(_size)?
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#struct_name::#variant_name(stream.read()?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
None => {
|
||||||
};
|
quote_spanned! {span=>
|
||||||
|
#struct_name::#variant_name(stream.read()?)
|
||||||
if let Some(discriminant) = discriminant {
|
}
|
||||||
quote_spanned! {span=>
|
|
||||||
#discriminant => #read_fields,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
_ => #read_fields,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let discriminant_token: TokenStream = match Discriminant::from(variant) {
|
||||||
|
Discriminant::Int(discriminant) => {
|
||||||
|
last_discriminant = discriminant as isize;
|
||||||
|
quote_spanned! { span => #discriminant }
|
||||||
|
}
|
||||||
|
Discriminant::Wildcard => quote_spanned! { span => _ },
|
||||||
|
Discriminant::Default => {
|
||||||
|
let new_discriminant = (last_discriminant + 1) as usize;
|
||||||
|
last_discriminant += 1;
|
||||||
|
quote_spanned! { span => #new_discriminant }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
quote_spanned! {span=>
|
||||||
|
#discriminant_token => #read_fields,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let span = data.enum_token.span();
|
let span = data.enum_token.span();
|
||||||
|
|
||||||
let enum_name = Lit::Str(LitStr::new(&struct_name.to_string(), struct_name.span()));
|
let enum_name = Lit::Str(LitStr::new(&struct_name.to_string(), struct_name.span()));
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
#discriminant_read
|
let discriminant:usize = stream.read_int(#discriminant_bits as usize)?;
|
||||||
Ok(match discriminant {
|
Ok(match discriminant {
|
||||||
#(#match_arms)*
|
#(#match_arms)*
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -358,7 +322,7 @@ fn parse(data: &Data, struct_name: &Ident, attrs: &Vec<Attribute>) -> TokenStrea
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_field_size(attrs: &Vec<Attribute>, span: Span) -> Option<TokenStream> {
|
fn get_field_size(attrs: &Vec<Attribute>, span: Span) -> Option<TokenStream> {
|
||||||
get_attr(attrs, "size")
|
get_attribute_value(attrs, &["size"])
|
||||||
.map(|size_lit| match size_lit {
|
.map(|size_lit| match size_lit {
|
||||||
Lit::Int(size) => {
|
Lit::Int(size) => {
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
|
|
@ -374,7 +338,7 @@ fn get_field_size(attrs: &Vec<Attribute>, span: Span) -> Option<TokenStream> {
|
||||||
_ => panic!("Unsupported value for size attribute"),
|
_ => panic!("Unsupported value for size attribute"),
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
get_attr(attrs, "size_bits").map(|size_bits_lit| {
|
get_attribute_value::<Lit>(attrs, &["size_bits"]).map(|size_bits_lit| {
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
stream.read_int::<usize>(#size_bits_lit)?
|
stream.read_int::<usize>(#size_bits_lit)?
|
||||||
}
|
}
|
||||||
|
|
@ -382,19 +346,6 @@ fn get_field_size(attrs: &Vec<Attribute>, span: Span) -> Option<TokenStream> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_attr(attrs: &Vec<Attribute>, name: &str) -> Option<Lit> {
|
|
||||||
for attr in attrs.iter() {
|
|
||||||
let meta = attr.parse_meta().unwrap();
|
|
||||||
match meta {
|
|
||||||
Meta::NameValue(ref name_value) if name_value.ident == name => {
|
|
||||||
return Some(name_value.lit.clone());
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See the [crate documentation](index.html) for details
|
/// See the [crate documentation](index.html) for details
|
||||||
#[proc_macro_derive(BitSize, attributes(size))]
|
#[proc_macro_derive(BitSize, attributes(size))]
|
||||||
pub fn derive_bitsize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_bitsize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
|
@ -468,3 +419,37 @@ fn bit_size_sum(data: &Data) -> TokenStream {
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Discriminant {
|
||||||
|
Int(usize),
|
||||||
|
Default,
|
||||||
|
Wildcard,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Lit> for Discriminant {
|
||||||
|
fn from(lit: Lit) -> Self {
|
||||||
|
match lit {
|
||||||
|
Lit::Int(lit) => Discriminant::Int(lit.value() as usize),
|
||||||
|
Lit::Str(lit) => match lit.value().as_str() {
|
||||||
|
"_" => Discriminant::Wildcard,
|
||||||
|
_ => panic!("discriminant is required to be an integer literal or \"_\""),
|
||||||
|
},
|
||||||
|
_ => panic!("discriminant is required to be an integer literal or \"_\""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Variant> for Discriminant {
|
||||||
|
fn from(variant: &Variant) -> Self {
|
||||||
|
variant
|
||||||
|
.discriminant
|
||||||
|
.as_ref()
|
||||||
|
.map(|(_, expr)| match expr {
|
||||||
|
Expr::Lit(expr_lit) => expr_lit.lit.clone(),
|
||||||
|
_ => panic!("discriminant is required to be an integer literal"),
|
||||||
|
})
|
||||||
|
.or_else(|| get_attribute_value(&variant.attrs, &["discriminant"]))
|
||||||
|
.map(Discriminant::from)
|
||||||
|
.unwrap_or(Discriminant::Default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue