Skip to content

Commit

Permalink
done Decode for enum
Browse files Browse the repository at this point in the history
  • Loading branch information
0123456789-jpg committed Mar 23, 2024
1 parent 12aad86 commit 5023c93
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 74 deletions.
4 changes: 2 additions & 2 deletions crates/util/edcode-derive-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod tests {
#[test]
#[allow(dead_code)]
fn derive_enum() {
#[derive(Encode)]
#[derive(Encode, Decode, PartialEq, Eq)]
#[repr(u8)]
enum Topics {
Pearl = 15,
Expand All @@ -18,6 +18,6 @@ mod tests {

let mut buf = BytesMut::new();
assert!(Topics::Someone.encode(&mut buf).is_ok());
assert!(<u8 as Decode>::decode(buf).is_ok_and(|x| x == 36));
assert!(Topics::decode(buf).is_ok_and(|x| x == Topics::Someone));
}
}
203 changes: 132 additions & 71 deletions crates/util/edcode-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
//! Proc-macros for deriving [`rimecraft_edcode`] traits.
//! Proc-macros for deriving `rimecraft_edcode` traits.
//!
//! __You shouldn't use this crate directly__, use `rimecraft_edcode` crate
//! with `derive` feature flag instead.

use proc_macro::TokenStream;
use proc_macro2::TokenTree;
use quote::quote;
use syn::{
parse_macro_input, spanned::Spanned, Data, DeriveInput, Error, Expr, Fields, Ident, Meta,
parse_macro_input, spanned::Spanned, Attribute, Data, DataEnum, DeriveInput, Error, Expr,
Fields, Ident, Meta,
};

macro_rules! unsupported_object {
Expand Down Expand Up @@ -37,7 +41,82 @@ macro_rules! repr_required {
};
}

/// Derive [`rimecraft_edcode::Encode`] to objects.
/// Common parsing code for deriving to `enum`.
fn parse_derive_enum(
ident: Ident,
attrs: Vec<Attribute>,
data: DataEnum,
) -> Result<(Ident, Ident, Vec<Ident>, Vec<Expr>), TokenStream> {
let mut enum_idents: Vec<Ident> = vec![];
let mut enum_vals: Vec<Expr> = vec![];
for var in data.variants.into_iter() {
if let Fields::Unit = var.fields {
enum_idents.push(var.ident.clone());
} else {
return Err(Error::new(var.fields.span(), fields_disallowed!())
.into_compile_error()
.into());
}
let has_disc_err = !var.discriminant.clone().is_some_and(|(_, e)| {
enum_vals.push(e);
true
});
if has_disc_err {
return Err(Error::new(var.span(), discriminant_required!())
.into_compile_error()
.into());
}
}
let mut repr_type: Option<Ident> = None;
for attr in attrs {
if let Meta::List(meta) = attr.meta {
let is_repr = meta
.path
.require_ident()
.is_ok_and(|id| id == &Ident::new("repr", id.span()));
if is_repr {
let mut iter = meta.tokens.into_iter().peekable();
let span = iter.peek().span();
macro_rules! ident_helper {
($span:expr => $( $ty:ident ),*) => {
vec![
$( Ident::new(stringify!($ty), $span) ),*
]
};
}
let supported = iter.next().is_some_and(|x| {
if let TokenTree::Ident(id) = x {
if ident_helper!(span => u8, u16, u32, u64, i8, i16, i32, i64).contains(&id)
{
repr_type = Some(id);
true
} else {
false
}
} else {
false
}
});
if !supported {
return Err(Error::new(span, unsupported_repr!())
.into_compile_error()
.into());
}
}
}
}
let repr_type = match repr_type {
None => {
return Err(Error::new(ident.span(), repr_required!())
.into_compile_error()
.into())
}
Some(x) => x,
};
Ok((ident, repr_type, enum_idents, enum_vals))
}

/// Derive `rimecraft_edcode::Encode` to objects.
///
/// # Enum
///
Expand All @@ -51,74 +130,11 @@ pub fn derive_encode(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match input.data {
Data::Enum(data) => {
let ident = input.ident;
let mut enum_idents: Vec<Ident> = vec![];
let mut enum_vals: Vec<Expr> = vec![];
for var in data.variants.into_iter() {
if let Fields::Unit = var.fields {
enum_idents.push(var.ident.clone());
} else {
return Error::new(var.fields.span(), fields_disallowed!())
.into_compile_error()
.into();
}
let has_disc_err = !var.discriminant.clone().is_some_and(|(_, e)| {
enum_vals.push(e);
true
});
if has_disc_err {
return Error::new(var.span(), discriminant_required!())
.into_compile_error()
.into();
}
}
let mut repr_type: Option<Ident> = None;
for attr in input.attrs {
if let Meta::List(meta) = attr.meta {
let is_repr = meta
.path
.require_ident()
.is_ok_and(|id| id == &Ident::new("repr", id.span()));
if is_repr {
let mut iter = meta.tokens.into_iter().peekable();
let span = iter.peek().span();
macro_rules! ident_helper {
($span:expr => $( $ty:ident ),*) => {
vec![
$( Ident::new(stringify!($ty), $span) ),*
]
};
}
let supported = iter.next().is_some_and(|x| {
if let TokenTree::Ident(id) = x {
if ident_helper!(span => u8, u16, u32, u64, i8, i16, i32, i64)
.contains(&id)
{
repr_type = Some(id);
true
} else {
false
}
} else {
false
}
});
if !supported {
return Error::new(span, unsupported_repr!())
.into_compile_error()
.into();
}
}
}
}
let repr_type = match repr_type {
None => {
return Error::new(ident.span(), repr_required!())
.into_compile_error()
.into()
}
Some(x) => x,
};
let (ident, repr_type, enum_idents, enum_vals) =
match parse_derive_enum(input.ident, input.attrs, data) {
Ok(x) => x,
Err(err) => return err,
};
let expanded = quote! {
impl ::rimecraft_edcode::Encode for #ident {
fn encode<B>(&self, mut buf: B) -> Result<(), std::io::Error>
Expand Down Expand Up @@ -149,3 +165,48 @@ pub fn derive_encode(input: TokenStream) -> TokenStream {
.into(),
}
}

/// Derive `rimecraft_edcode::Decode` to objects.
#[proc_macro_derive(Decode)]
pub fn derive_decode(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match input.data {
Data::Enum(data) => {
let (ident, repr_type, enum_idents, enum_vals) =
match parse_derive_enum(input.ident, input.attrs, data) {
Ok(x) => x,
Err(err) => {
return err;
}
};
let expanded = quote! {
impl ::rimecraft_edcode::Decode for #ident {
fn decode<B>(mut buf: B) -> Result<Self, std::io::Error>
where
B: ::rimecraft_edcode::bytes::Buf,
{
let x:#repr_type = ::rimecraft_edcode::Decode::decode(&mut buf)?;
let var = match x {
#( #enum_vals => Self::#enum_idents, )*
unknown => return Err(std::io::Error::other(format!("unknown variant {}", unknown))),
};
Ok(var)
}
}
};
expanded.into()
}
Data::Struct(data) => Error::new(
data.struct_token.span,
unsupported_object!("Decode", "struct"),
)
.to_compile_error()
.into(),
Data::Union(data) => Error::new(
data.union_token.span,
unsupported_object!("Decode", "union"),
)
.into_compile_error()
.into(),
}
}
2 changes: 1 addition & 1 deletion crates/util/edcode/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::io;
pub use bytes;

#[cfg(feature = "derive")]
pub use rimecraft_edcode_derive::Encode;
pub use rimecraft_edcode_derive::{Decode, Encode};

/// Describes types that can be encoded into a packet buffer.
pub trait Encode {
Expand Down

0 comments on commit 5023c93

Please sign in to comment.