Add bitfield derive (#16)

This commit is contained in:
vagola 2021-10-21 02:36:12 +03:00 committed by GitHub
parent 05cf9c6e6b
commit 09f95625ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 179 additions and 9 deletions

View File

@ -3,7 +3,7 @@ use proc_macro2::Ident;
use std::iter::FromIterator;
use syn::punctuated::Punctuated;
use syn::{
Attribute, Data, DeriveInput, ExprLit, Fields, FieldsNamed, Lit, Meta, NestedMeta, Type,
Attribute, Data, DeriveInput, ExprLit, Field, Fields, FieldsNamed, Lit, Meta, NestedMeta, Type,
};
use syn::{Error as SynError, Variant};
use syn::{Expr, Token};
@ -36,6 +36,7 @@ pub(crate) struct FieldData<'a> {
pub(crate) enum AttributeData {
With { module: String },
MaxLength { length: usize },
Bitfield { idx: u8, position: BitfieldPosition },
Empty,
}
@ -45,6 +46,13 @@ pub(crate) enum DiscriminantType {
VarInt,
}
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum BitfieldPosition {
Start,
Intermediate,
End,
}
pub(crate) fn parse_derive_input(
input: &DeriveInput,
) -> Result<DeriveInputParseResult, DeriveInputParserError> {
@ -77,7 +85,7 @@ fn parse_discriminant_type(
attributes: &Vec<Attribute>,
) -> Result<DiscriminantType, DeriveInputParserError> {
let nested_metas = parse_attributes_nested_metas(attributes)?;
let attribute = parse_attribute(nested_metas)?;
let attribute = parse_attribute(nested_metas, None, 0)?;
match attribute {
AttributeData::With { module } if module == "var_int" => Ok(DiscriminantType::VarInt),
@ -127,13 +135,26 @@ fn parse_variant_discriminant(variant: &Variant) -> Option<usize> {
fn parse_fields(named_fields: &FieldsNamed) -> Result<Vec<FieldData>, DeriveInputParserError> {
let mut fields_data = Vec::new();
let mut current_bitfield_idx = 0;
for field in named_fields.named.iter() {
let fields: Vec<&Field> = named_fields.named.iter().collect();
for (idx, field) in fields.iter().enumerate() {
let name = field.ident.as_ref().unwrap();
let ty = &field.ty;
let nested_metas = parse_attributes_nested_metas(&field.attrs)?;
let attribute = parse_attribute(nested_metas)?;
let next_field_opt = fields.get(idx + 1);
let next_nested_metas_opt = next_field_opt
.and_then(|next_field| parse_attributes_nested_metas(&next_field.attrs).ok());
let attribute = parse_attribute(nested_metas, next_nested_metas_opt, current_bitfield_idx)?;
match attribute {
AttributeData::Bitfield { .. } => current_bitfield_idx += 1,
_ => current_bitfield_idx = 0,
}
fields_data.push(FieldData {
name,
@ -165,12 +186,23 @@ fn parse_attributes_nested_metas(
Ok(nested_metas.into_iter().flatten().collect())
}
fn parse_attribute(nested_metas: Vec<NestedMeta>) -> Result<AttributeData, DeriveInputParserError> {
let attribute_parsers: Vec<fn(&NestedMeta) -> Result<AttributeData, AttributeError>> =
fn parse_attribute(
nested_metas: Vec<NestedMeta>,
next_nested_metas_opt: Option<Vec<NestedMeta>>,
current_bitfield_idx: u8,
) -> Result<AttributeData, DeriveInputParserError> {
let simple_attribute_parsers: Vec<fn(&NestedMeta) -> Result<AttributeData, AttributeError>> =
vec![get_module_attribute, get_max_length_attribute];
for nested_meta in nested_metas.iter() {
for attribute_parser in attribute_parsers.iter() {
let bitfield_attribute =
get_bitfield_attribute(current_bitfield_idx, nested_meta, &next_nested_metas_opt);
if bitfield_attribute != AttributeData::Empty {
return Ok(bitfield_attribute);
}
for attribute_parser in simple_attribute_parsers.iter() {
let attribute = attribute_parser(nested_meta)?;
if attribute != AttributeData::Empty {
@ -211,3 +243,49 @@ fn get_max_length_attribute(nested_meta: &NestedMeta) -> Result<AttributeData, A
Ok(AttributeData::Empty)
}
fn get_bitfield_attribute(
current_bitfield_idx: u8,
nested_meta: &NestedMeta,
next_nested_metas_opt: &Option<Vec<NestedMeta>>,
) -> AttributeData {
if is_bitfield_attribute(nested_meta) {
let position = calc_bitfield_position(current_bitfield_idx, next_nested_metas_opt);
AttributeData::Bitfield {
idx: current_bitfield_idx,
position,
}
} else {
AttributeData::Empty
}
}
fn calc_bitfield_position(
current_bitfield_idx: u8,
next_nested_metas_opt: &Option<Vec<NestedMeta>>,
) -> BitfieldPosition {
fn next_has_bitfield_attribute(next_nested_metas: &Vec<NestedMeta>) -> bool {
next_nested_metas
.iter()
.any(|nested_meta| is_bitfield_attribute(nested_meta))
}
match next_nested_metas_opt {
Some(next_nested_metas) if (next_has_bitfield_attribute(&next_nested_metas)) => {
if current_bitfield_idx == 0 {
BitfieldPosition::Start
} else {
BitfieldPosition::Intermediate
}
}
_ => BitfieldPosition::End,
}
}
fn is_bitfield_attribute(nested_meta: &NestedMeta) -> bool {
match nested_meta {
NestedMeta::Meta(Meta::Path(path)) => path.is_ident("bitfield"),
_ => false,
}
}

View File

@ -1,4 +1,4 @@
use crate::parse::{AttributeData, DiscriminantType, FieldData, VariantData};
use crate::parse::{AttributeData, BitfieldPosition, DiscriminantType, FieldData, VariantData};
use proc_macro2::TokenStream as TokenStream2;
use proc_macro2::{Ident, Span};
use quote::quote;
@ -140,6 +140,7 @@ fn render_field(field: &FieldData) -> TokenStream2 {
match &field.attribute {
AttributeData::With { module } => render_with_field(name, module),
AttributeData::MaxLength { length } => render_max_length_field(name, *length as u16),
AttributeData::Bitfield { idx, position } => render_bitfield(name, *idx, position),
AttributeData::Empty => render_simple_field(name, ty),
}
}
@ -163,3 +164,22 @@ fn render_max_length_field(name: &Ident, max_length: u16) -> TokenStream2 {
let #name = crate::decoder::DecoderReadExt::read_string(reader, #max_length)?;
}
}
fn render_bitfield(name: &Ident, idx: u8, position: &BitfieldPosition) -> TokenStream2 {
let mask = 1u8 << idx;
let render_mask = quote! {
let #name = flags & #mask > 0;
};
match position {
BitfieldPosition::Start => {
quote! {
let flags = reader.read_u8()?;
#render_mask
}
}
_ => render_mask,
}
}

View File

@ -1,4 +1,4 @@
use crate::parse::{AttributeData, DiscriminantType, FieldData, VariantData};
use crate::parse::{AttributeData, BitfieldPosition, DiscriminantType, FieldData, VariantData};
use proc_macro2::TokenStream as TokenStream2;
use proc_macro2::{Ident, Span};
use quote::quote;
@ -130,6 +130,7 @@ fn render_field(field: &FieldData, with_self: bool) -> TokenStream2 {
AttributeData::MaxLength { length } => {
render_max_length_field(name, *length as u16, with_self)
}
AttributeData::Bitfield { idx, position } => render_bitfield(name, *idx, position),
AttributeData::Empty => render_simple_field(name, with_self),
}
}
@ -155,6 +156,32 @@ fn render_max_length_field(name: &Ident, max_length: u16, with_self: bool) -> To
}
}
fn render_bitfield(name: &Ident, idx: u8, position: &BitfieldPosition) -> TokenStream2 {
let mask = 1u8 << idx;
let render_mask = quote! {
if self.#name {
flags |= #mask;
}
};
match position {
BitfieldPosition::Start => quote!(
let mut flags = 0;
#render_mask
),
BitfieldPosition::Intermediate => render_mask,
BitfieldPosition::End => {
quote! {
#render_mask
writer.write_u8(flags)?;
}
}
}
}
fn get_field_final_name(name: &Ident, with_self: bool) -> TokenStream2 {
if with_self {
quote!(&self.#name)

View File

@ -12,6 +12,7 @@ use uuid::Uuid;
pub enum GameServerBoundPacket {
ServerBoundChatMessage(ServerBoundChatMessage),
ServerBoundKeepAlive(ServerBoundKeepAlive),
ServerBoundAbilities(ServerBoundAbilities),
}
pub enum GameClientBoundPacket {
@ -29,6 +30,7 @@ impl GameServerBoundPacket {
match self {
GameServerBoundPacket::ServerBoundChatMessage(_) => 0x03,
GameServerBoundPacket::ServerBoundKeepAlive(_) => 0x0F,
GameServerBoundPacket::ServerBoundAbilities(_) => 0x19,
}
}
@ -332,6 +334,20 @@ pub enum EntityActionId {
StartFlyingWithElytra,
}
#[derive(Encoder, Decoder, Debug, PartialEq)]
pub struct ServerBoundAbilities {
#[data_type(bitfield)]
pub invulnerable: bool,
#[data_type(bitfield)]
pub allow_flying: bool,
#[data_type(bitfield)]
pub flying: bool,
#[data_type(bitfield)]
pub creative_mode: bool,
pub fly_speed: f32,
pub walk_speed: f32,
}
#[cfg(test)]
mod tests {
use crate::data::chat::Payload;
@ -686,4 +702,33 @@ mod tests {
}
);
}
#[test]
fn test_serverbound_abilities_encode() {
let abilities = ServerBoundAbilities {
invulnerable: true,
flying: true,
allow_flying: false,
creative_mode: true,
fly_speed: 0.0,
walk_speed: 0.0,
};
let mut vec = Vec::new();
abilities.encode(&mut vec).unwrap();
assert_eq!(vec, [13, 0, 0, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn test_serverbound_abilities_decode() {
let vec = [13, 0, 0, 0, 0, 0, 0, 0, 0].to_vec();
let mut cursor = Cursor::new(vec);
let abilities = ServerBoundAbilities::decode(&mut cursor).unwrap();
assert!(abilities.invulnerable);
assert!(!abilities.allow_flying);
assert!(abilities.flying);
assert!(abilities.creative_mode);
}
}