use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{
parse_macro_input, Data, DataEnum, DeriveInput, Fields, GenericParam, Generics, Ident, Index,
LitStr, Member, Type,
};
use super::shared::{get_builder_generic_fields, get_generics, get_struct_type, StructType};
pub fn message_impl(
input: proc_macro::TokenStream,
shared_crate_name: TokenStream,
is_fragment: bool,
is_request: bool,
) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
if let Data::Enum(ref data_enum) = input.data {
return enum_message_impl(
&input,
data_enum,
shared_crate_name,
is_fragment,
is_request,
);
}
let struct_type = get_struct_type(&input);
let fields = get_fields(&input);
let (untyped_generics, typed_generics, turbofish) = get_generics(&input);
let struct_name = input.ident;
let struct_name_str = LitStr::new(
format!(
"{}{}",
&struct_name.to_string(),
&untyped_generics.to_string()
)
.as_str(),
struct_name.span(),
);
let lowercase_struct_name = Ident::new(
struct_name.to_string().to_lowercase().as_str(),
Span::call_site(),
);
let module_name = format_ident!("define_{}", lowercase_struct_name);
let builder_name = format_ident!("{}Builder", struct_name);
let builder_generic_fields = get_builder_generic_fields(&input.generics);
let clone_method = get_clone_method(&fields, &struct_type);
let relations_waiting_method = get_relations_waiting_method(&fields, &struct_type);
let relations_complete_method = get_relations_complete_method(&fields, &struct_type);
let bit_length_method = get_bit_length_method(&fields, &struct_type);
let write_method = get_write_method(&fields, &struct_type);
let builder_create_method = get_builder_create_method(&builder_name, &turbofish);
let builder_new_method = get_builder_new_method(
&typed_generics,
&builder_name,
&untyped_generics,
&input.generics,
);
let builder_box_clone_method = get_builder_box_clone_method(&input.generics);
let builder_read_method =
get_builder_read_method(&struct_name, &fields, &struct_type, &turbofish);
let is_fragment_method = get_is_fragment_method(is_fragment);
let is_request_method = get_is_request_method(is_request);
let gen = quote! {
mod #module_name {
pub use std::{any::Any, collections::HashSet};
pub use #shared_crate_name::{
Named, GlobalEntity, Message, BitWrite, LocalEntityAndGlobalEntityConverter, LocalEntityAndGlobalEntityConverterMut,
EntityProperty, MessageKind, MessageKinds, Serde, MessageBuilder, BitReader, SerdeErr, ConstBitLength, MessageContainer, RemoteEntity,
};
use super::*;
struct #builder_name #typed_generics #builder_generic_fields
#builder_new_method
impl #typed_generics MessageBuilder for #builder_name #untyped_generics {
#builder_read_method
#builder_box_clone_method
}
impl #typed_generics Message for #struct_name #untyped_generics {
fn kind(&self) -> MessageKind {
MessageKind::of::<#struct_name #untyped_generics>()
}
fn to_boxed_any(self: Box<Self>) -> Box<dyn Any> {
self
}
#is_fragment_method
#is_request_method
#bit_length_method
#builder_create_method
#relations_waiting_method
#relations_complete_method
#write_method
}
impl #typed_generics Named for #struct_name #untyped_generics {
fn name(&self) -> String {
#struct_name_str.to_string()
}
fn protocol_name() -> &'static str {
#struct_name_str
}
}
impl #typed_generics Clone for #struct_name #untyped_generics {
#clone_method
}
}
};
proc_macro::TokenStream::from(gen)
}
fn get_is_fragment_method(is_fragment: bool) -> TokenStream {
let value = {
if is_fragment {
quote! { true }
} else {
quote! { false }
}
};
quote! {
fn is_fragment(&self) -> bool {
#value
}
}
}
fn get_is_request_method(is_request: bool) -> TokenStream {
let value = {
if is_request {
quote! { true }
} else {
quote! { false }
}
};
quote! {
fn is_request(&self) -> bool {
#value
}
}
}
fn get_clone_method(fields: &[Field], struct_type: &StructType) -> TokenStream {
let mut output = quote! {};
for (index, field) in fields.iter().enumerate() {
let field_name = get_field_name(field, index, struct_type);
match field {
Field::Normal(_) | Field::EntityProperty(_) => {
let new_output_right = quote! {
#field_name: self.#field_name.clone(),
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
};
}
quote! {
fn clone(&self) -> Self {
let mut new_clone = Self {
#output
};
return new_clone;
}
}
}
fn get_relations_waiting_method(fields: &[Field], struct_type: &StructType) -> TokenStream {
let mut body = quote! {};
for (index, field) in fields.iter().enumerate() {
if let Field::EntityProperty(_) = field {
let field_name = get_field_name(field, index, struct_type);
let body_add_right = quote! {
if let Some(remote_entity) = self.#field_name.waiting_remote_entity() {
output.insert(remote_entity);
}
};
let new_body = quote! {
#body
#body_add_right
};
body = new_body;
}
}
quote! {
fn relations_waiting(&self) -> Option<HashSet<RemoteEntity>> {
let mut output = HashSet::new();
#body
if output.is_empty() {
return None;
}
return Some(output);
}
}
}
fn get_relations_complete_method(fields: &[Field], struct_type: &StructType) -> TokenStream {
let mut body = quote! {};
for (index, field) in fields.iter().enumerate() {
if let Field::EntityProperty(_) = field {
let field_name = get_field_name(field, index, struct_type);
let body_add_right = quote! {
self.#field_name.waiting_complete(converter);
};
let new_body = quote! {
#body
#body_add_right
};
body = new_body;
}
}
quote! {
fn relations_complete(&mut self, converter: &dyn LocalEntityAndGlobalEntityConverter) {
#body
}
}
}
fn get_write_method(fields: &[Field], struct_type: &StructType) -> TokenStream {
let mut field_writes = quote! {};
for (index, field) in fields.iter().enumerate() {
let field_name = get_field_name(field, index, struct_type);
let new_output_right = match field {
Field::Normal(_) => {
quote! {
self.#field_name.ser(writer);
}
}
Field::EntityProperty(_) => {
quote! {
EntityProperty::write(&self.#field_name, writer, converter);
}
}
};
let new_output_result = quote! {
#field_writes
#new_output_right
};
field_writes = new_output_result;
}
quote! {
fn write(&self, message_kinds: &MessageKinds, writer: &mut dyn BitWrite, converter: &mut dyn LocalEntityAndGlobalEntityConverterMut) {
self.kind().ser(message_kinds, writer);
#field_writes
}
}
}
fn get_bit_length_method(fields: &[Field], struct_type: &StructType) -> TokenStream {
let mut field_bit_lengths = quote! {};
for (index, field) in fields.iter().enumerate() {
let field_name = get_field_name(field, index, struct_type);
let new_output_right = match field {
Field::Normal(_) => {
quote! {
output += self.#field_name.bit_length();
}
}
Field::EntityProperty(_) => {
quote! {
output += self.#field_name.bit_length(converter);
}
}
};
let new_output_result = quote! {
#field_bit_lengths
#new_output_right
};
field_bit_lengths = new_output_result;
}
quote! {
fn bit_length(
&self,
message_kinds: &MessageKinds,
converter: &mut dyn LocalEntityAndGlobalEntityConverterMut,
) -> u32 {
let mut output = 0;
output += message_kinds.kind_bit_length();
#field_bit_lengths
output
}
}
}
pub fn get_builder_create_method(builder_name: &Ident, turbofish: &TokenStream) -> TokenStream {
let builder_new = quote! {
#builder_name #turbofish::new()
};
quote! {
fn create_builder() -> Box<dyn MessageBuilder> where Self:Sized {
Box::new(#builder_new)
}
}
}
fn get_fields(input: &DeriveInput) -> Vec<Field> {
let mut fields = Vec::new();
if let Data::Struct(data_struct) = &input.data {
match &data_struct.fields {
Fields::Named(fields_named) => {
for field in fields_named.named.iter() {
if let Some(variable_name) = &field.ident {
match &field.ty {
Type::Path(type_path) => {
if let Some(property_seg) = type_path.path.segments.first() {
let property_type = property_seg.ident.clone();
if property_type == "EntityProperty" {
fields.push(Field::entity_property(variable_name.clone()));
continue;
} else {
fields.push(Field::normal(
variable_name.clone(),
field.ty.clone(),
));
}
}
}
_ => {
fields.push(Field::normal(variable_name.clone(), field.ty.clone()));
}
}
}
}
}
Fields::Unnamed(fields_unnamed) => {
for (index, field) in fields_unnamed.unnamed.iter().enumerate() {
if let Type::Path(type_path) = &field.ty {
if let Some(property_seg) = type_path.path.segments.first() {
let property_type = property_seg.ident.clone();
let variable_name =
get_variable_name_for_unnamed_field(index, property_type.span());
if property_type == "EntityProperty" {
fields.push(Field::entity_property(variable_name));
continue;
} else {
fields.push(Field::normal(variable_name, field.ty.clone()))
}
}
}
}
}
Fields::Unit => {}
}
} else {
panic!("Can only derive Message on a struct");
}
fields
}
fn get_field_name(field: &Field, index: usize, struct_type: &StructType) -> Member {
match *struct_type {
StructType::Struct => Member::from(field.variable_name().clone()),
StructType::TupleStruct => {
let index = Index {
index: index as u32,
span: field.variable_name().span(),
};
Member::from(index)
}
_ => {
panic!("The struct should not have any fields")
}
}
}
pub fn get_builder_new_method(
typed_generics: &TokenStream,
builder_name: &Ident,
untyped_generics: &TokenStream,
input_generics: &Generics,
) -> TokenStream {
let fn_impl = if input_generics.gt_token.is_none() {
quote! { return Self; }
} else {
let mut output = quote! {};
for param in input_generics.params.iter() {
let GenericParam::Type(type_param) = param else {
panic!("Only type parameters are supported for now");
};
let field_name =
format_ident!("phantom_{}", type_param.ident.to_string().to_lowercase());
let new_output_right = quote! {
#field_name: std::marker::PhantomData,
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
Self {
#output
}
}
};
quote! {
impl #typed_generics #builder_name #untyped_generics {
pub fn new() -> Self {
#fn_impl
}
}
}
}
pub fn get_builder_read_method(
struct_name: &Ident,
fields: &[Field],
struct_type: &StructType,
turbofish: &TokenStream,
) -> TokenStream {
let mut field_names = quote! {};
for field in fields.iter() {
let field_name = field.variable_name();
let new_output_right = quote! {
#field_name
};
let new_output_result = quote! {
#field_names
#new_output_right,
};
field_names = new_output_result;
}
let mut field_reads = quote! {};
for field in fields.iter() {
let field_name = field.variable_name();
let new_output_right = match field {
Field::EntityProperty(_property) => {
quote! {
let #field_name = EntityProperty::new_read(reader, converter)?;
}
}
Field::Normal(normal_field) => {
let field_name = &normal_field.variable_name;
let field_type = &normal_field.field_type;
quote! {
let #field_name = <#field_type>::de(reader)?;
}
}
};
let new_output_result = quote! {
#field_reads
#new_output_right
};
field_reads = new_output_result;
}
let struct_build = match *struct_type {
StructType::Struct => {
quote! {
#struct_name #turbofish {
#field_names
}
}
}
StructType::TupleStruct => {
quote! {
#struct_name #turbofish (
#field_names
)
}
}
StructType::UnitStruct => {
quote! {
#struct_name
}
}
};
quote! {
fn read(&self, reader: &mut BitReader, converter: &dyn LocalEntityAndGlobalEntityConverter) -> Result<MessageContainer, SerdeErr> {
#field_reads
return Ok(MessageContainer::new(Box::new(#struct_build)));
}
}
}
pub fn get_builder_box_clone_method(input_generics: &Generics) -> TokenStream {
let fn_impl = if input_generics.gt_token.is_none() {
quote! { Self }
} else {
let mut output = quote! {};
for param in input_generics.params.iter() {
let GenericParam::Type(type_param) = param else {
panic!("Only type parameters are supported for now");
};
let field_name =
format_ident!("phantom_{}", type_param.ident.to_string().to_lowercase());
let new_output_right = quote! {
#field_name: std::marker::PhantomData,
};
let new_output_result = quote! {
#output
#new_output_right
};
output = new_output_result;
}
quote! {
Self {
#output
}
}
};
quote! {
fn box_clone(&self) -> Box<dyn MessageBuilder> {
let me = #fn_impl ;
Box::new(me)
}
}
}
const UNNAMED_FIELD_PREFIX: &str = "unnamed_field_";
fn get_variable_name_for_unnamed_field(index: usize, span: Span) -> Ident {
Ident::new(&format!("{}{}", UNNAMED_FIELD_PREFIX, index), span)
}
pub struct EntityProperty {
pub variable_name: Ident,
}
pub struct Normal {
pub variable_name: Ident,
pub field_type: Type,
}
#[allow(clippy::large_enum_variant)]
pub enum Field {
EntityProperty(EntityProperty),
Normal(Normal),
}
impl Field {
pub fn entity_property(variable_name: Ident) -> Self {
Self::EntityProperty(EntityProperty {
variable_name: variable_name.clone(),
})
}
pub fn normal(variable_name: Ident, field_type: Type) -> Self {
Self::Normal(Normal {
variable_name: variable_name.clone(),
field_type,
})
}
pub fn variable_name(&self) -> &Ident {
match self {
Self::EntityProperty(property) => &property.variable_name,
Self::Normal(field) => &field.variable_name,
}
}
}
fn bits_needed_for(variant_count: usize) -> u8 {
if variant_count <= 2 {
return 1;
}
let max_index = variant_count - 1;
let bits = usize::BITS - max_index.leading_zeros();
if bits >= 256 {
panic!("cannot encode an enum in more than 255 bits!");
}
bits as u8
}
fn is_entity_property_type(ty: &Type) -> bool {
if let Type::Path(type_path) = ty {
if let Some(seg) = type_path.path.segments.first() {
return seg.ident == "EntityProperty";
}
}
false
}
enum FieldKind {
Normal(Box<Type>),
EntityProperty,
}
enum VariantStyle {
Unit,
Named,
Unnamed,
}
struct EnumVariantField {
name: Ident,
kind: FieldKind,
}
struct EnumVariant {
name: Ident,
index: u16,
fields: Vec<EnumVariantField>,
style: VariantStyle,
}
fn get_enum_variants(data_enum: &DataEnum) -> Vec<EnumVariant> {
data_enum
.variants
.iter()
.enumerate()
.map(|(i, variant)| {
let name = variant.ident.clone();
let index = i as u16;
let (fields, style) = match &variant.fields {
Fields::Unit => (vec![], VariantStyle::Unit),
Fields::Named(named) => {
let fields = named
.named
.iter()
.map(|f| {
let fname = f.ident.clone().expect("named field has ident");
let kind = if is_entity_property_type(&f.ty) {
FieldKind::EntityProperty
} else {
FieldKind::Normal(Box::new(f.ty.clone()))
};
EnumVariantField { name: fname, kind }
})
.collect();
(fields, VariantStyle::Named)
}
Fields::Unnamed(unnamed) => {
let fields = unnamed
.unnamed
.iter()
.enumerate()
.map(|(fi, f)| {
let fname = format_ident!("f{}", fi);
let kind = if is_entity_property_type(&f.ty) {
FieldKind::EntityProperty
} else {
FieldKind::Normal(Box::new(f.ty.clone()))
};
EnumVariantField { name: fname, kind }
})
.collect();
(fields, VariantStyle::Unnamed)
}
};
EnumVariant {
name,
index,
fields,
style,
}
})
.collect()
}
fn enum_message_impl(
input: &DeriveInput,
data_enum: &DataEnum,
shared_crate_name: TokenStream,
is_fragment: bool,
is_request: bool,
) -> proc_macro::TokenStream {
let (untyped_generics, typed_generics, turbofish) = get_generics(input);
let enum_name = &input.ident;
let enum_name_str = LitStr::new(
format!(
"{}{}",
&enum_name.to_string(),
&untyped_generics.to_string()
)
.as_str(),
enum_name.span(),
);
let lowercase_enum_name = Ident::new(
enum_name.to_string().to_lowercase().as_str(),
Span::call_site(),
);
let module_name = format_ident!("define_{}", lowercase_enum_name);
let builder_name = format_ident!("{}Builder", enum_name);
let builder_generic_fields = get_builder_generic_fields(&input.generics);
let variants = get_enum_variants(data_enum);
let bits_needed: u8 = bits_needed_for(variants.len());
let write_method = get_enum_write_method(&variants, bits_needed);
let bit_length_method = get_enum_bit_length_method(&variants, bits_needed);
let clone_method = get_enum_clone_method(&variants);
let relations_waiting_method = get_enum_relations_waiting_method(&variants);
let relations_complete_method = get_enum_relations_complete_method(&variants);
let builder_read_method = get_enum_builder_read_method(
enum_name,
&variants,
bits_needed,
&untyped_generics,
);
let builder_create_method = get_builder_create_method(&builder_name, &turbofish);
let builder_new_method = get_builder_new_method(
&typed_generics,
&builder_name,
&untyped_generics,
&input.generics,
);
let builder_box_clone_method = get_builder_box_clone_method(&input.generics);
let is_fragment_method = get_is_fragment_method(is_fragment);
let is_request_method = get_is_request_method(is_request);
let gen = quote! {
mod #module_name {
pub use std::{any::Any, collections::HashSet};
pub use #shared_crate_name::{
Named, GlobalEntity, Message, BitWrite, LocalEntityAndGlobalEntityConverter,
LocalEntityAndGlobalEntityConverterMut, EntityProperty, MessageKind, MessageKinds,
Serde, MessageBuilder, BitReader, SerdeErr, ConstBitLength, MessageContainer,
RemoteEntity, UnsignedInteger,
};
use super::*;
struct #builder_name #typed_generics #builder_generic_fields
#builder_new_method
impl #typed_generics MessageBuilder for #builder_name #untyped_generics {
#builder_read_method
#builder_box_clone_method
}
impl #typed_generics Message for #enum_name #untyped_generics {
fn kind(&self) -> MessageKind {
MessageKind::of::<#enum_name #untyped_generics>()
}
fn to_boxed_any(self: Box<Self>) -> Box<dyn Any> {
self
}
#is_fragment_method
#is_request_method
#bit_length_method
#builder_create_method
#relations_waiting_method
#relations_complete_method
#write_method
}
impl #typed_generics Named for #enum_name #untyped_generics {
fn name(&self) -> String {
#enum_name_str.to_string()
}
fn protocol_name() -> &'static str {
#enum_name_str
}
}
impl #typed_generics Clone for #enum_name #untyped_generics {
#clone_method
}
}
};
proc_macro::TokenStream::from(gen)
}
fn get_enum_write_method(variants: &[EnumVariant], bits_needed: u8) -> TokenStream {
let arms = variants.iter().map(|v| {
let vname = &v.name;
let idx = v.index;
match &v.style {
VariantStyle::Unit => quote! {
Self::#vname => {
UnsignedInteger::<#bits_needed>::new(#idx).ser(writer);
}
},
VariantStyle::Named => {
let names: Vec<&Ident> = v.fields.iter().map(|f| &f.name).collect();
let writes = v.fields.iter().map(|f| {
let n = &f.name;
match &f.kind {
FieldKind::Normal(_) => quote! { #n.ser(writer); },
FieldKind::EntityProperty => {
quote! { EntityProperty::write(#n, writer, converter); }
}
}
});
quote! {
Self::#vname { #(#names),* } => {
UnsignedInteger::<#bits_needed>::new(#idx).ser(writer);
#(#writes)*
}
}
}
VariantStyle::Unnamed => {
let names: Vec<&Ident> = v.fields.iter().map(|f| &f.name).collect();
let writes = v.fields.iter().map(|f| {
let n = &f.name;
match &f.kind {
FieldKind::Normal(_) => quote! { #n.ser(writer); },
FieldKind::EntityProperty => {
quote! { EntityProperty::write(#n, writer, converter); }
}
}
});
quote! {
Self::#vname(#(#names),*) => {
UnsignedInteger::<#bits_needed>::new(#idx).ser(writer);
#(#writes)*
}
}
}
}
});
quote! {
fn write(
&self,
message_kinds: &MessageKinds,
writer: &mut dyn BitWrite,
converter: &mut dyn LocalEntityAndGlobalEntityConverterMut,
) {
self.kind().ser(message_kinds, writer);
match self {
#(#arms)*
}
}
}
}
fn get_enum_bit_length_method(variants: &[EnumVariant], bits_needed: u8) -> TokenStream {
let arms = variants.iter().map(|v| {
let vname = &v.name;
match &v.style {
VariantStyle::Unit => quote! {
Self::#vname => {
output += <UnsignedInteger::<#bits_needed> as ConstBitLength>::const_bit_length();
}
},
VariantStyle::Named => {
let names: Vec<&Ident> = v.fields.iter().map(|f| &f.name).collect();
let lengths = v.fields.iter().map(|f| {
let n = &f.name;
match &f.kind {
FieldKind::Normal(_) => quote! { output += #n.bit_length(); },
FieldKind::EntityProperty => {
quote! { output += #n.bit_length(converter); }
}
}
});
quote! {
Self::#vname { #(#names),* } => {
output += <UnsignedInteger::<#bits_needed> as ConstBitLength>::const_bit_length();
#(#lengths)*
}
}
}
VariantStyle::Unnamed => {
let names: Vec<&Ident> = v.fields.iter().map(|f| &f.name).collect();
let lengths = v.fields.iter().map(|f| {
let n = &f.name;
match &f.kind {
FieldKind::Normal(_) => quote! { output += #n.bit_length(); },
FieldKind::EntityProperty => {
quote! { output += #n.bit_length(converter); }
}
}
});
quote! {
Self::#vname(#(#names),*) => {
output += <UnsignedInteger::<#bits_needed> as ConstBitLength>::const_bit_length();
#(#lengths)*
}
}
}
}
});
quote! {
fn bit_length(
&self,
message_kinds: &MessageKinds,
converter: &mut dyn LocalEntityAndGlobalEntityConverterMut,
) -> u32 {
let mut output = 0;
output += message_kinds.kind_bit_length();
match self {
#(#arms)*
}
output
}
}
}
fn get_enum_clone_method(variants: &[EnumVariant]) -> TokenStream {
let arms = variants.iter().map(|v| {
let vname = &v.name;
match &v.style {
VariantStyle::Unit => quote! { Self::#vname => Self::#vname, },
VariantStyle::Named => {
let names: Vec<&Ident> = v.fields.iter().map(|f| &f.name).collect();
let cloned = names.iter().map(|n| quote! { #n: #n.clone(), });
quote! {
Self::#vname { #(#names),* } => Self::#vname { #(#cloned)* },
}
}
VariantStyle::Unnamed => {
let names: Vec<&Ident> = v.fields.iter().map(|f| &f.name).collect();
let cloned = names.iter().map(|n| quote! { #n.clone(), });
quote! {
Self::#vname(#(#names),*) => Self::#vname(#(#cloned)*),
}
}
}
});
quote! {
fn clone(&self) -> Self {
match self {
#(#arms)*
}
}
}
}
fn get_enum_relations_waiting_method(variants: &[EnumVariant]) -> TokenStream {
let arms = variants.iter().map(|v| {
let vname = &v.name;
let ep_fields: Vec<&Ident> = v
.fields
.iter()
.filter(|f| matches!(f.kind, FieldKind::EntityProperty))
.map(|f| &f.name)
.collect();
let collects = ep_fields.iter().map(|n| {
quote! {
if let Some(remote_entity) = #n.waiting_remote_entity() {
output.insert(remote_entity);
}
}
});
match &v.style {
VariantStyle::Unit => quote! { Self::#vname => {} },
VariantStyle::Named => {
if ep_fields.is_empty() {
quote! { Self::#vname { .. } => {} }
} else {
quote! {
Self::#vname { #(#ep_fields),*, .. } => { #(#collects)* }
}
}
}
VariantStyle::Unnamed => {
if ep_fields.is_empty() {
quote! { Self::#vname(..) => {} }
} else {
let pattern: Vec<TokenStream> = v.fields.iter().map(|f| {
let n = &f.name;
match &f.kind {
FieldKind::EntityProperty => quote! { #n },
FieldKind::Normal(_) => quote! { _ },
}
}).collect();
quote! {
Self::#vname(#(#pattern),*) => { #(#collects)* }
}
}
}
}
});
quote! {
fn relations_waiting(&self) -> Option<HashSet<RemoteEntity>> {
let mut output = HashSet::new();
match self {
#(#arms)*
}
if output.is_empty() {
return None;
}
Some(output)
}
}
}
fn get_enum_relations_complete_method(variants: &[EnumVariant]) -> TokenStream {
let arms = variants.iter().map(|v| {
let vname = &v.name;
let ep_fields: Vec<&Ident> = v
.fields
.iter()
.filter(|f| matches!(f.kind, FieldKind::EntityProperty))
.map(|f| &f.name)
.collect();
let completes = ep_fields.iter().map(|n| {
quote! { #n.waiting_complete(converter); }
});
match &v.style {
VariantStyle::Unit => quote! { Self::#vname => {} },
VariantStyle::Named => {
if ep_fields.is_empty() {
quote! { Self::#vname { .. } => {} }
} else {
quote! {
Self::#vname { #(#ep_fields),*, .. } => { #(#completes)* }
}
}
}
VariantStyle::Unnamed => {
if ep_fields.is_empty() {
quote! { Self::#vname(..) => {} }
} else {
let pattern: Vec<TokenStream> = v.fields.iter().map(|f| {
let n = &f.name;
match &f.kind {
FieldKind::EntityProperty => quote! { #n },
FieldKind::Normal(_) => quote! { _ },
}
}).collect();
quote! {
Self::#vname(#(#pattern),*) => { #(#completes)* }
}
}
}
}
});
quote! {
fn relations_complete(&mut self, converter: &dyn LocalEntityAndGlobalEntityConverter) {
match self {
#(#arms)*
}
}
}
}
fn get_enum_builder_read_method(
enum_name: &Ident,
variants: &[EnumVariant],
bits_needed: u8,
untyped_generics: &TokenStream,
) -> TokenStream {
let arms = variants.iter().map(|v| {
let vname = &v.name;
let idx = v.index;
match &v.style {
VariantStyle::Unit => quote! {
#idx => #enum_name :: #vname,
},
VariantStyle::Named => {
let reads = v.fields.iter().map(|f| {
let n = &f.name;
match &f.kind {
FieldKind::Normal(ty) => quote! { let #n = <#ty>::de(reader)?; },
FieldKind::EntityProperty => {
quote! { let #n = EntityProperty::new_read(reader, converter)?; }
}
}
});
let names: Vec<&Ident> = v.fields.iter().map(|f| &f.name).collect();
quote! {
#idx => {
#(#reads)*
#enum_name :: #vname { #(#names),* }
}
}
}
VariantStyle::Unnamed => {
let reads = v.fields.iter().map(|f| {
let n = &f.name;
match &f.kind {
FieldKind::Normal(ty) => quote! { let #n = <#ty>::de(reader)?; },
FieldKind::EntityProperty => {
quote! { let #n = EntityProperty::new_read(reader, converter)?; }
}
}
});
let names: Vec<&Ident> = v.fields.iter().map(|f| &f.name).collect();
quote! {
#idx => {
#(#reads)*
#enum_name :: #vname(#(#names),*)
}
}
}
}
});
quote! {
fn read(
&self,
reader: &mut BitReader,
converter: &dyn LocalEntityAndGlobalEntityConverter,
) -> Result<MessageContainer, SerdeErr> {
let index: UnsignedInteger<#bits_needed> = Serde::de(reader)?;
let index_u16: u16 = index.get() as u16;
let value: #enum_name #untyped_generics = match index_u16 {
#(#arms)*
_ => return Err(SerdeErr),
};
Ok(MessageContainer::new(Box::new(value)))
}
}
}