use crate::parse_enum::EnumMeta;
use quote::quote;
use std::str::FromStr;
use syn::DeriveInput;
pub fn generate_enum_write(
parsed: EnumMeta,
input: &DeriveInput,
gen_sized_impl: bool,
) -> proc_macro2::TokenStream {
let name = input.ident.clone();
let repr_type = parsed.repr_type;
let size_bytes = match repr_type.to_string().as_str() {
"u8" | "i8" => 1usize,
"u16" | "i16" => 2,
"u32" | "i32" => 4,
"u64" | "i64" => 8,
invalid => unreachable!("Invalid repr {}", invalid),
};
let pack = if parsed.catch_all.is_some() {
let match_arms = parsed.variants.clone().into_iter().map(|variant| {
let value =
proc_macro2::TokenStream::from_str(&variant.discriminant.to_string()).unwrap();
let variant_name = variant.name;
if variant.catch_all {
quote! {
#name::#variant_name (value) => { *value }
}
} else {
quote! {
#name::#variant_name => { #value }
}
}
});
quote! {
let value: #repr_type = match self {
#(#match_arms),*
};
buf.copy_from_slice(&value.to_le_bytes());
}
} else {
quote! {
buf.copy_from_slice(&(*self as #repr_type).to_le_bytes());
}
};
let sized_impl = if gen_sized_impl {
quote! {
impl ::ethercrab_wire::EtherCrabWireSized for #name {
const PACKED_LEN: usize = #size_bytes;
type Buffer = [u8; #size_bytes];
fn buffer() -> Self::Buffer {
[0u8; #size_bytes]
}
}
}
} else {
quote! {}
};
quote! {
impl ::ethercrab_wire::EtherCrabWireWrite for #name {
fn pack_to_slice_unchecked<'buf>(&self, buf: &'buf mut [u8]) -> &'buf [u8] {
let mut buf = &mut buf[0..#size_bytes];
unsafe {
buf.as_mut_ptr().write_bytes(0u8, #size_bytes);
}
#pack
buf
}
fn packed_len(&self) -> usize {
#size_bytes
}
}
#sized_impl
impl ::ethercrab_wire::EtherCrabWireWriteSized for #name {
fn pack(&self) -> Self::Buffer {
let mut buf = [0u8; #size_bytes];
<Self as ::ethercrab_wire::EtherCrabWireWrite>::pack_to_slice_unchecked(self, &mut buf);
buf
}
}
}
}
pub fn generate_enum_read(parsed: EnumMeta, input: &DeriveInput) -> proc_macro2::TokenStream {
let name = input.ident.clone();
let repr_type = parsed.repr_type;
let size_bytes = match repr_type.to_string().as_str() {
"u8" | "i8" => 1usize,
"u16" | "i16" => 2,
"u32" | "i32" => 4,
"u64" | "i64" => 8,
invalid => unreachable!("Invalid repr {}", invalid),
};
let primitive_variants = parsed
.variants
.clone()
.into_iter()
.filter(|variant| !variant.catch_all);
let result_match_arms = primitive_variants.clone().map(|variant| {
let value = proc_macro2::TokenStream::from_str(&variant.discriminant.to_string()).unwrap();
let variant_name = variant.name;
quote! {
#value => { Ok(Self::#variant_name) }
}
});
let fallthrough = if let Some(ref catch_all_variant) = parsed.catch_all {
let catch_all = catch_all_variant.name.clone();
quote! {
other => Ok(Self::#catch_all(other))
}
} else if let Some(ref default_variant) = parsed.default_variant {
let default = default_variant.name.clone();
quote! {
_other => Ok(Self::#default)
}
} else {
quote! {
_other => { Err(::ethercrab_wire::WireError::InvalidValue) }
}
};
let match_arms = primitive_variants.clone().map(|variant| {
let value = proc_macro2::TokenStream::from_str(&variant.discriminant.to_string()).unwrap();
let variant_name = variant.name;
quote! {
#value => { Self::#variant_name }
}
});
let from_primitive_impl = if let Some(catch_all_variant) = &parsed.catch_all {
let catch_all = catch_all_variant.name.clone();
let match_arms = match_arms.clone();
quote! {
impl From<#repr_type> for #name {
fn from(value: #repr_type) -> Self {
match value {
#(#match_arms),*
other => Self::#catch_all(other)
}
}
}
}
} else if let Some(default_variant) = parsed.default_variant {
let default = default_variant.name;
let match_arms = match_arms.clone();
quote! {
impl From<#repr_type> for #name {
fn from(value: #repr_type) -> Self {
match value {
#(#match_arms),*
other => Self::#default
}
}
}
}
} else {
let match_arms = result_match_arms.clone();
quote! {
impl TryFrom<#repr_type> for #name {
type Error = ::ethercrab_wire::WireError;
fn try_from(value: #repr_type) -> Result<Self, Self::Error> {
match value {
#(#match_arms),*
_other => Err(::ethercrab_wire::WireError::InvalidValue)
}
}
}
}
};
let into_primitive_impl = if parsed.catch_all.is_some() {
let match_arms_from = parsed.variants.clone().into_iter().map(|variant| {
let value =
proc_macro2::TokenStream::from_str(&variant.discriminant.to_string()).unwrap();
let variant_name = variant.name;
if variant.catch_all {
quote! {
#name::#variant_name (value) => { value }
}
} else {
quote! {
#name::#variant_name => { #value }
}
}
});
quote! {
impl From<#name> for #repr_type {
fn from(value: #name) -> Self {
match value {
#(#match_arms_from),*
}
}
}
}
} else {
quote! {
impl From<#name> for #repr_type {
fn from(value: #name) -> Self {
value as #repr_type
}
}
}
};
quote! {
impl ::ethercrab_wire::EtherCrabWireRead for #name {
fn unpack_from_slice(buf: &[u8]) -> Result<Self, ::ethercrab_wire::WireError> {
let raw = buf.first_chunk::<#size_bytes>().map(|chunk| {
#repr_type::from_le_bytes(*chunk)
}).ok_or(::ethercrab_wire::WireError::ReadBufferTooShort)?;
match raw {
#(#result_match_arms),*
#fallthrough,
}
}
}
impl ::ethercrab_wire::EtherCrabWireSized for #name {
const PACKED_LEN: usize = #size_bytes;
type Buffer = [u8; #size_bytes];
fn buffer() -> Self::Buffer {
[0u8; #size_bytes]
}
}
#from_primitive_impl
#into_primitive_impl
}
}