mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 16:44:06 +02:00
rewrite derive macro
This commit is contained in:
parent
0701318120
commit
3852f09dd5
21 changed files with 1548 additions and 840 deletions
135
bitbuffer_derive/src/params/enum.rs
Normal file
135
bitbuffer_derive/src/params/enum.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use crate::params::parse_attrs;
|
||||
use crate::params::variant::VariantParam;
|
||||
use merge::Merge;
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::quote;
|
||||
use structmeta::StructMeta;
|
||||
use syn::{Attribute, DataEnum, Error, LitInt, Result};
|
||||
|
||||
#[derive(Default, StructMeta, Merge, Debug)]
|
||||
struct EnumAttrs {
|
||||
discriminant_bits: Option<LitInt>,
|
||||
}
|
||||
|
||||
pub struct EnumParam {
|
||||
pub span: Span,
|
||||
pub ident: Ident,
|
||||
pub variants: Vec<VariantParam>,
|
||||
pub discriminant_bits: usize,
|
||||
}
|
||||
|
||||
impl EnumParam {
|
||||
pub fn size_can_be_predicted(&self) -> bool {
|
||||
self.variants
|
||||
.iter()
|
||||
.all(|field| field.size_can_be_predicted())
|
||||
}
|
||||
|
||||
pub fn parse(
|
||||
data: &DataEnum,
|
||||
ident: Ident,
|
||||
attrs: &[Attribute],
|
||||
span: Span,
|
||||
) -> Result<EnumParam> {
|
||||
let attrs: EnumAttrs = parse_attrs(attrs)?;
|
||||
let variants = data
|
||||
.variants
|
||||
.iter()
|
||||
.map(VariantParam::parse)
|
||||
.collect::<Result<Vec<VariantParam>>>()?;
|
||||
let discriminant_bits = attrs
|
||||
.discriminant_bits
|
||||
.ok_or_else(|| {
|
||||
Error::new(
|
||||
span.clone(),
|
||||
"'discriminant_bits' attribute is required when deriving `BinRead` for enums",
|
||||
)
|
||||
})?
|
||||
.base10_parse()?;
|
||||
|
||||
Ok(EnumParam {
|
||||
span,
|
||||
ident,
|
||||
variants,
|
||||
discriminant_bits,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
pub fn read_discriminant_tokens(&self) -> impl Iterator<Item = TokenStream> + '_ {
|
||||
ReadDiscriminantTokenIter {
|
||||
last: -1,
|
||||
variants: self.variants.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_discriminant_tokens(&self) -> impl Iterator<Item = TokenStream> + '_ {
|
||||
WriteDiscriminantTokenIter {
|
||||
last: -1,
|
||||
max: self.max_discriminant(),
|
||||
variants: self.variants.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_discriminant(&self) -> usize {
|
||||
let mut last_discriminant = -1;
|
||||
|
||||
self.variants
|
||||
.iter()
|
||||
.map(|variant| variant.discriminant.max_value(&mut last_discriminant))
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn discriminant_repr(&self) -> TokenStream {
|
||||
if self.discriminant_bits <= 8 {
|
||||
quote!(u8)
|
||||
} else if self.discriminant_bits <= 16 {
|
||||
quote!(u16)
|
||||
} else if self.discriminant_bits <= 32 {
|
||||
quote!(u32)
|
||||
} else {
|
||||
quote!(u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadDiscriminantTokenIter<'a> {
|
||||
last: isize,
|
||||
variants: std::slice::Iter<'a, VariantParam>,
|
||||
}
|
||||
|
||||
impl Iterator for ReadDiscriminantTokenIter<'_> {
|
||||
type Item = TokenStream;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let variant = self.variants.next()?;
|
||||
Some(
|
||||
variant
|
||||
.discriminant
|
||||
.read_token(&mut self.last, variant.span()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WriteDiscriminantTokenIter<'a> {
|
||||
last: isize,
|
||||
max: usize,
|
||||
variants: std::slice::Iter<'a, VariantParam>,
|
||||
}
|
||||
|
||||
impl Iterator for WriteDiscriminantTokenIter<'_> {
|
||||
type Item = TokenStream;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let variant = self.variants.next()?;
|
||||
Some(
|
||||
variant
|
||||
.discriminant
|
||||
.write_token(&mut self.last, self.max, variant.span()),
|
||||
)
|
||||
}
|
||||
}
|
||||
81
bitbuffer_derive/src/params/field.rs
Normal file
81
bitbuffer_derive/src/params/field.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
use crate::params::{parse_attrs, Alignment, Size};
|
||||
use merge::Merge;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use structmeta::StructMeta;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Expr, Field, Index, LitInt, Member, Result, Type};
|
||||
|
||||
#[derive(Default, StructMeta, Merge)]
|
||||
struct FieldAttrs {
|
||||
size: Option<Expr>,
|
||||
size_bits: Option<LitInt>,
|
||||
#[merge(strategy = merge::bool::overwrite_false)]
|
||||
align: bool,
|
||||
}
|
||||
|
||||
pub struct FieldParam {
|
||||
pub span: Span,
|
||||
pub field_name: Option<Ident>,
|
||||
pub size: Option<Size>,
|
||||
pub align: Alignment,
|
||||
pub ty: Type,
|
||||
}
|
||||
|
||||
impl FieldParam {
|
||||
/// Whether the size of the field can be determined without having to read further bits
|
||||
pub fn size_can_be_predicted(&self) -> bool {
|
||||
if self.align == Alignment::Auto {
|
||||
return false;
|
||||
}
|
||||
match &self.size {
|
||||
Some(size) => size.is_const(),
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(input: &Field) -> Result<FieldParam> {
|
||||
let attrs: FieldAttrs = parse_attrs(&input.attrs)?;
|
||||
let field_name = input.ident.clone();
|
||||
let align = attrs.align.into();
|
||||
let size = Size::from_attrs(attrs.size, attrs.size_bits, input.span())?;
|
||||
let ty = input.ty.clone();
|
||||
|
||||
Ok(FieldParam {
|
||||
span: input.span(),
|
||||
field_name,
|
||||
size,
|
||||
align,
|
||||
ty,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
pub fn member(&self, index: u32) -> Member {
|
||||
match self.field_name.as_ref() {
|
||||
Some(name) => Member::Named(name.clone()),
|
||||
None => Member::Unnamed(Index {
|
||||
index,
|
||||
span: self.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_int(&self) -> bool {
|
||||
if let Type::Path(path) = &self.ty {
|
||||
if let Some(ident) = path.path.get_ident() {
|
||||
let name = ident.to_string();
|
||||
matches!(
|
||||
name.as_str(),
|
||||
"u8" | "u16" | "u32" | "u64" | "usize" | "i8" | "i16" | "i32" | "i64" | "isize"
|
||||
)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
305
bitbuffer_derive/src/params/mod.rs
Normal file
305
bitbuffer_derive/src/params/mod.rs
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
mod r#enum;
|
||||
mod field;
|
||||
mod r#struct;
|
||||
mod variant;
|
||||
|
||||
pub use crate::params::field::FieldParam;
|
||||
pub use crate::params::r#enum::EnumParam;
|
||||
pub use crate::params::r#struct::StructParam;
|
||||
pub use crate::params::variant::{VariantBody, VariantBodyType, VariantParam};
|
||||
use crate::{err, DeriveParams};
|
||||
use merge::Merge;
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
|
||||
use std::any::type_name;
|
||||
use std::fmt::Debug;
|
||||
use structmeta::StructMeta;
|
||||
use syn::__private::{bool, IntoSpans};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::token::Paren;
|
||||
use syn::{
|
||||
parse_quote, parse_str, Attribute, Data, DeriveInput, Expr, ExprLit, ExprPath, GenericParam,
|
||||
Generics, ImplGenerics, Lifetime, Lit, LitBool, LitInt, LitStr, MacroDelimiter, Meta, MetaList,
|
||||
Result, TypeGenerics, WhereClause,
|
||||
};
|
||||
|
||||
pub enum Size {
|
||||
Expression(Expr, Span),
|
||||
Bits(usize, Span),
|
||||
}
|
||||
|
||||
impl Size {
|
||||
pub fn is_const(&self) -> bool {
|
||||
match self {
|
||||
Size::Expression(
|
||||
Expr::Lit(ExprLit {
|
||||
lit: Lit::Int(_), ..
|
||||
}),
|
||||
_,
|
||||
) => true,
|
||||
Size::Expression(Expr::Path(ExprPath { path, .. }), _) => path.is_ident("input_size"),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_attrs(
|
||||
size: Option<Expr>,
|
||||
size_bits: Option<LitInt>,
|
||||
span: Span,
|
||||
) -> Result<Option<Self>> {
|
||||
Ok(match (size, size_bits) {
|
||||
(
|
||||
Some(Expr::Lit(ExprLit {
|
||||
lit: Lit::Str(field),
|
||||
..
|
||||
})),
|
||||
None,
|
||||
) => Some(Size::Expression(parse_str(&field.value())?, span)),
|
||||
(Some(size), None) => Some(Size::Expression(size, span)),
|
||||
(None, Some(bits)) => Some(Size::Bits(bits.base10_parse()?, span)),
|
||||
(Some(_), Some(_)) => err("#[size] and #[size_bits] are mutually exclusive", span)?,
|
||||
(None, None) => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Size {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
Size::Expression(expr, span) => {
|
||||
let span = span.clone();
|
||||
tokens.append_all(quote_spanned! {span => {
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
let __size = (#expr) as usize;
|
||||
__size
|
||||
}
|
||||
});
|
||||
}
|
||||
Size::Bits(bits, span) => {
|
||||
let span = span.clone();
|
||||
tokens.append_all(quote_spanned! {span => {
|
||||
__stream.read_int::<usize>(#bits)?
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialOrd, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Alignment {
|
||||
#[default]
|
||||
None,
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl Alignment {
|
||||
pub fn write(&self) -> TokenStream {
|
||||
match self {
|
||||
Alignment::Auto => quote! {
|
||||
__stream.align();
|
||||
},
|
||||
Alignment::None => quote!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Alignment {
|
||||
fn from(value: bool) -> Self {
|
||||
match value {
|
||||
true => Alignment::Auto,
|
||||
false => Alignment::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Alignment {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(LitBool::parse(input)?.value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Merge for Alignment {
|
||||
fn merge(&mut self, other: Self) {
|
||||
if other == Alignment::Auto {
|
||||
*self = Alignment::Auto
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Alignment {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
Alignment::Auto => tokens.append_all(quote! {
|
||||
__stream.align()?;
|
||||
}),
|
||||
Alignment::None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, StructMeta, Merge, Debug)]
|
||||
struct InputAttrs {
|
||||
endianness: Option<LitStr>,
|
||||
#[merge(strategy = merge::bool::overwrite_false)]
|
||||
align: bool,
|
||||
}
|
||||
|
||||
pub struct InputParams {
|
||||
pub ident: Ident,
|
||||
pub span: Span,
|
||||
endianness: Option<String>,
|
||||
pub align: Alignment,
|
||||
pub generics: Generics,
|
||||
pub generics_with_endianness: Generics,
|
||||
pub inner: InputInnerParams,
|
||||
pub lifetime: Lifetime,
|
||||
}
|
||||
|
||||
pub enum InputInnerParams {
|
||||
Struct(StructParam),
|
||||
Enum(EnumParam),
|
||||
}
|
||||
|
||||
impl DeriveParams for InputParams {
|
||||
fn parse(input: &DeriveInput) -> Result<Self> {
|
||||
let attrs: InputAttrs = parse_attrs(&input.attrs)?;
|
||||
let inner = match &input.data {
|
||||
Data::Struct(data) => InputInnerParams::Struct(StructParam::parse(
|
||||
data,
|
||||
input.ident.clone(),
|
||||
&input.attrs,
|
||||
input.span(),
|
||||
)?),
|
||||
Data::Enum(data) => InputInnerParams::Enum(EnumParam::parse(
|
||||
data,
|
||||
input.ident.clone(),
|
||||
&input.attrs,
|
||||
input.span(),
|
||||
)?),
|
||||
_ => return err("Only structs and enums are supported", input.span()),
|
||||
};
|
||||
|
||||
let endianness = attrs.endianness.map(|lit| lit.value());
|
||||
let align = attrs.align.into();
|
||||
|
||||
let generics = input.generics.clone();
|
||||
let mut generics_with_endianness = generics.clone();
|
||||
let mut lifetimes = input
|
||||
.generics
|
||||
.params
|
||||
.iter()
|
||||
.filter_map(|param| match param {
|
||||
GenericParam::Lifetime(lifetime) => Some(lifetime),
|
||||
_ => None,
|
||||
});
|
||||
let lifetime = match (lifetimes.next(), lifetimes.next()) {
|
||||
(_, Some(_)) => {
|
||||
return err("Only a single lifetime generic is supported", input.span())
|
||||
}
|
||||
(Some(param), None) => param.lifetime.clone(),
|
||||
(None, None) => {
|
||||
let lifetime = Lifetime::new("'a", input.span());
|
||||
generics_with_endianness
|
||||
.params
|
||||
.push(GenericParam::Lifetime(parse_str("'a").unwrap()));
|
||||
lifetime
|
||||
}
|
||||
};
|
||||
|
||||
if endianness.is_none() {
|
||||
generics_with_endianness
|
||||
.params
|
||||
.push(parse_quote!(_E: ::bitbuffer::Endianness));
|
||||
}
|
||||
|
||||
Ok(InputParams {
|
||||
ident: input.ident.clone(),
|
||||
span: input.span(),
|
||||
endianness,
|
||||
align,
|
||||
generics,
|
||||
generics_with_endianness,
|
||||
lifetime,
|
||||
inner,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl InputParams {
|
||||
#[allow(dead_code)]
|
||||
pub fn size_can_be_predicted(&self) -> bool {
|
||||
match &self.inner {
|
||||
InputInnerParams::Struct(inner) => inner.size_can_be_predicted(),
|
||||
InputInnerParams::Enum(inner) => inner.size_can_be_predicted(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generics_for_impl<'a>(
|
||||
&'a self,
|
||||
) -> (ImplGenerics<'a>, TypeGenerics<'a>, Option<&'a WhereClause>) {
|
||||
// we need these separate generics to only add out Endianness param to the 'impl'
|
||||
let (_, ty_generics, where_clause) = self.generics.split_for_impl();
|
||||
let (impl_generics, _, _) = self.generics_with_endianness.split_for_impl();
|
||||
|
||||
(impl_generics, ty_generics, where_clause)
|
||||
}
|
||||
|
||||
pub fn endianness(&self) -> Ident {
|
||||
Ident::new(
|
||||
self.endianness.as_deref().unwrap_or_else(|| "_E"),
|
||||
self.span,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const BARE_ATTRS: &'static [&'static str] = &[
|
||||
"size",
|
||||
"size_bits",
|
||||
"discriminant_bits",
|
||||
"discriminant",
|
||||
"endianness",
|
||||
"align",
|
||||
];
|
||||
|
||||
fn parse_attrs<T: Parse + Default + Merge>(attrs: &[Attribute]) -> Result<T> {
|
||||
let mut result = T::default();
|
||||
for attr in attrs {
|
||||
let parsed = if BARE_ATTRS
|
||||
.iter()
|
||||
.any(|name| attr.meta.path().is_ident(name))
|
||||
{
|
||||
let wrapped_meta = Meta::List(MetaList {
|
||||
path: parse_str("bitbuffer").unwrap(),
|
||||
delimiter: MacroDelimiter::Paren(Paren {
|
||||
span: attr.span().into_spans(),
|
||||
}),
|
||||
tokens: attr.meta.clone().into_token_stream(),
|
||||
});
|
||||
let wrapped = Attribute {
|
||||
pound_token: attr.pound_token,
|
||||
style: attr.style.clone(),
|
||||
bracket_token: attr.bracket_token.clone(),
|
||||
meta: wrapped_meta,
|
||||
};
|
||||
wrapped.parse_args()
|
||||
} else {
|
||||
attr.parse_args()
|
||||
};
|
||||
match parsed {
|
||||
Ok(parsed) => {
|
||||
result.merge(parsed);
|
||||
}
|
||||
Err(e) => {
|
||||
// since we first parse our attrs as InputAttrs, and then the same attrs as either an Struct or EnumAttrs
|
||||
// when doing the first pass we expect a bunch of extra parameters
|
||||
let is_first_pass = type_name::<T>() == type_name::<InputAttrs>();
|
||||
if !e.to_string().starts_with("cannot find parameter") && !is_first_pass {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
44
bitbuffer_derive/src/params/struct.rs
Normal file
44
bitbuffer_derive/src/params/struct.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use crate::params::field::FieldParam;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use syn::{Attribute, DataStruct, Fields, Result};
|
||||
|
||||
pub struct StructParam {
|
||||
pub span: Span,
|
||||
pub ident: Ident,
|
||||
pub fields: Vec<FieldParam>,
|
||||
pub is_unit: bool,
|
||||
}
|
||||
|
||||
impl StructParam {
|
||||
pub fn size_can_be_predicted(&self) -> bool {
|
||||
self.fields
|
||||
.iter()
|
||||
.all(|field| field.size_can_be_predicted())
|
||||
}
|
||||
|
||||
pub fn parse(
|
||||
data: &DataStruct,
|
||||
ident: Ident,
|
||||
_attrs: &[Attribute],
|
||||
span: Span,
|
||||
) -> Result<StructParam> {
|
||||
let fields = data
|
||||
.fields
|
||||
.iter()
|
||||
.map(FieldParam::parse)
|
||||
.collect::<Result<Vec<FieldParam>>>()?;
|
||||
|
||||
let is_unit = matches!(data.fields, Fields::Unit);
|
||||
|
||||
Ok(StructParam {
|
||||
span,
|
||||
ident,
|
||||
fields,
|
||||
is_unit,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
143
bitbuffer_derive/src/params/variant.rs
Normal file
143
bitbuffer_derive/src/params/variant.rs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
use crate::discriminant::Discriminant;
|
||||
use crate::err;
|
||||
use crate::params::field::FieldParam;
|
||||
use crate::params::{parse_attrs, Alignment, Size};
|
||||
use merge::Merge;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use std::convert::TryFrom;
|
||||
use structmeta::StructMeta;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Expr, ExprLit, Fields, Lit, LitInt, Result, Variant};
|
||||
|
||||
#[derive(Default, StructMeta, Merge)]
|
||||
struct VariantAttrs {
|
||||
size: Option<Expr>,
|
||||
size_bits: Option<LitInt>,
|
||||
#[merge(strategy = merge::bool::overwrite_false)]
|
||||
align: bool,
|
||||
discriminant: Option<Lit>,
|
||||
}
|
||||
|
||||
pub struct VariantParam {
|
||||
pub span: Span,
|
||||
pub variant_name: Ident,
|
||||
pub body: VariantBody,
|
||||
pub discriminant: Discriminant,
|
||||
}
|
||||
|
||||
pub enum VariantBodyType {
|
||||
Unit,
|
||||
Unnamed,
|
||||
Named,
|
||||
}
|
||||
|
||||
pub enum VariantBody {
|
||||
Unit,
|
||||
Fields(Vec<FieldParam>),
|
||||
}
|
||||
|
||||
impl VariantBody {
|
||||
pub fn body_type(&self) -> VariantBodyType {
|
||||
match self {
|
||||
VariantBody::Unit => VariantBodyType::Unit,
|
||||
VariantBody::Fields(fields) => {
|
||||
let named = fields.iter().any(|f| f.field_name.is_some());
|
||||
if named {
|
||||
VariantBodyType::Named
|
||||
} else {
|
||||
VariantBodyType::Unnamed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VariantParam {
|
||||
/// Whether the size of the variant can be determined without having to read further bits
|
||||
pub fn size_can_be_predicted(&self) -> bool {
|
||||
match &self.body {
|
||||
VariantBody::Fields(fields) => fields.iter().all(|field| field.size_can_be_predicted()),
|
||||
VariantBody::Unit => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(input: &Variant) -> Result<VariantParam> {
|
||||
let attrs: VariantAttrs = parse_attrs(&input.attrs)?;
|
||||
let variant_name = input.ident.clone();
|
||||
let align = attrs.align.into();
|
||||
let size = Size::from_attrs(attrs.size, attrs.size_bits, input.span())?;
|
||||
|
||||
if attrs.discriminant.is_some() && input.discriminant.is_some() {
|
||||
err(
|
||||
"variant has both discriminant and discriminant attribute set",
|
||||
input.span(),
|
||||
)?;
|
||||
}
|
||||
|
||||
let discriminant = attrs
|
||||
.discriminant
|
||||
.map(|lit| {
|
||||
Expr::Lit(ExprLit {
|
||||
attrs: Vec::new(),
|
||||
lit,
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
input
|
||||
.discriminant
|
||||
.clone()
|
||||
.map(|(_, discriminant)| discriminant)
|
||||
})
|
||||
.map(Discriminant::try_from)
|
||||
.transpose()?
|
||||
.unwrap_or(Discriminant::Default);
|
||||
|
||||
let body = if matches!(input.fields, Fields::Unit) {
|
||||
if align == Alignment::Auto {
|
||||
err(
|
||||
"'align' attribute is not allowed on unit variants",
|
||||
input.span(),
|
||||
)?;
|
||||
}
|
||||
if size.is_some() {
|
||||
err(
|
||||
"'size' attribute is not allowed on unit variants",
|
||||
input.span(),
|
||||
)?;
|
||||
}
|
||||
VariantBody::Unit
|
||||
} else {
|
||||
let mut fields = input
|
||||
.fields
|
||||
.iter()
|
||||
.map(FieldParam::parse)
|
||||
.collect::<Result<Vec<FieldParam>>>()?;
|
||||
|
||||
// align and size attributes on the variant go to the first field
|
||||
match (fields.first_mut(), align) {
|
||||
(Some(field), Alignment::Auto) => {
|
||||
field.align = align;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
match (fields.first_mut(), size) {
|
||||
(Some(field), Some(size)) => {
|
||||
field.size = Some(size);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
VariantBody::Fields(fields)
|
||||
};
|
||||
|
||||
Ok(VariantParam {
|
||||
span: input.span(),
|
||||
variant_name,
|
||||
discriminant,
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue