use proc_macro2::Span; use quote::quote; use syn::{parse_quote, DeriveInput, Expr, Ident, Stmt}; use syn_helpers::{ derive_trait, Constructable, FieldMut, NamedOrUnnamedFieldMut, Structure, Trait, TraitItem, TypeOfSelf, }; macro_rules! token_stream_eq { ($a:expr, $b:expr) => { assert_eq!($a.to_string(), $b.to_string()) }; } #[test] fn binary_serialize_enum() { let input: DeriveInput = parse_quote! { enum X { A { a: String }, B { b: i32 } } }; let stream = derive_trait( input, Trait { name: Ident::new("BinarySerializable", Span::call_site()).into(), generic_parameters: None, items: vec![ TraitItem::new_method( Ident::new("serialize", Span::call_site()), None, TypeOfSelf::Reference, vec![parse_quote!(buf: &mut Vec)], None, |mut item| { item.map_constructable(|mut constructable| { let iterator = constructable .as_enum_variant() .map(|variant| { let idx = variant.idx as u8; parse_quote!(buf.push(#idx);) }) .into_iter() .chain(constructable.get_fields_mut().fields_iterator_mut().map( |mut field: NamedOrUnnamedFieldMut| -> Stmt { let reference = field.get_reference(); parse_quote!(crate::BinarySerializable::serialize(#reference, buf);) }, )); Ok(iterator.collect()) }) }, ), TraitItem::new_associated_function( Ident::new("deserialize", Span::call_site()), Some(vec![parse_quote!(I: Iterator)]), vec![parse_quote!(iter: &mut I)], Some(parse_quote!(Self)), |structure| { let deserialize_call: Expr = parse_quote!(crate::BinarySerializable::deserialize(iter)); match structure { Structure::Enum(r#enum) => { let indexer: Stmt = parse_quote!(let indexer = iter.next().unwrap();); let bad_case = parse_quote!( unreachable!("invalid discriminant when deserializing enum"); ); Ok(std::iter::once(indexer) .chain(r#enum.get_variants().iter().map(|variant| { let idx = variant.idx as u8; let constructor = variant .build_constructor(|_| Ok(deserialize_call.clone())) .unwrap(); parse_quote!(if indexer == #idx { return #constructor; }) })) .chain(std::iter::once(bad_case)) .collect()) } Structure::Struct(r#struct) => r#struct .build_constructor(|_| Ok(deserialize_call.clone())) .map(|expr| vec![Stmt::Expr(expr, None)]), } }, ), ], }, ); #[rustfmt::skip] let expected = quote!( #[automatically_derived] impl BinarySerializable for X { fn serialize(&self, buf: &mut Vec) { match self { X::A { a: ref _0 } => { buf.push(0u8); crate::BinarySerializable::serialize(_0, buf); } X::B { b: ref _0 } => { buf.push(1u8); crate::BinarySerializable::serialize(_0, buf); } } } fn deserialize >(iter: &mut I) -> Self { let indexer = iter.next().unwrap(); if indexer == 0u8 { return X::A { a: crate::BinarySerializable::deserialize(iter) }; } if indexer == 1u8 { return X::B { b: crate::BinarySerializable::deserialize(iter) }; } unreachable!("invalid discriminant when deserializing enum"); } } ); token_stream_eq!(stream, expected) }