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<u8>)],
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<Item = u8>)]),
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<u8>) {
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<I: Iterator<Item = u8> >(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)
}