Skip to content

Commit

Permalink
Generics are now supported in Macros
Browse files Browse the repository at this point in the history
  • Loading branch information
wyatt-herkamp committed Oct 13, 2023
1 parent 2562066 commit 230f0f3
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 98 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.3 (Unreleased)
- Generics are now supported in Macros
## 0.2.2 (2023-10-13)
- Fixed Unresolved path for `core::any`

Expand Down
32 changes: 32 additions & 0 deletions digestible/tests/generics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use digestible::Digestible;

#[derive(Digestible)]
#[digestible(hash = LittleEndian)]
pub struct MyStructGenerics<T> {
pub id: u32,
pub t: T,
}
#[derive(Digestible)]
pub struct MyStructGenericsAlreadyRequired<T: Digestible> {
pub id: u32,
pub t: T,
}

#[test]
pub fn hash_test() {
let test = MyStructGenerics {
id: 0,
t: "Test".to_string(),
};
let mut default_hasher = DefaultHasher::new();
test.hash(&mut default_hasher);
}


#[derive(Digestible)]
pub enum MyEnum<T> {
A(T),
B(u32),
}
24 changes: 11 additions & 13 deletions macros/src/container_attrs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use syn::parse::{Parse, ParseStream};

use syn::{parse_quote, Path};
use syn::{Attribute, parse_quote, Path};

#[derive(Debug)]
pub enum TypeHeader {
Expand Down Expand Up @@ -77,16 +77,14 @@ impl Parse for ContainerAttrs {
Ok(attr)
}
}
macro_rules! get_container_attrs {
($input:ident) => {
$input
.attrs
.iter()
.find(|v| v.path().is_ident("digestible"))
.map(|v| v.parse_args::<ContainerAttrs>())
.transpose()?
.unwrap_or_default()
};
pub fn get_container_attrs(attrs: &[Attribute])-> syn::Result<ContainerAttrs> {
let attrs = attrs
.iter()
.find(|v| v.path().is_ident("digestible"))
.map(|v| v.parse_args::<ContainerAttrs>())
.transpose()?
.unwrap_or_default();
Ok(attrs)
}
use crate::paths::byte_order_impl_path;
pub(crate) use get_container_attrs;

use crate::utils::byte_order_impl_path;
49 changes: 27 additions & 22 deletions macros/src/expand_enum.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::paths::{digest_writer, digestible_path, private_path};
use crate::container_attrs::{get_container_attrs, ContainerAttrs, TypeHeader};
use crate::utils::{digest_writer, digestible_path, private_path};
use crate::container_attrs::{get_container_attrs, TypeHeader};
use crate::fields::Field;
use crate::shared;
use crate::{ utils};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{DeriveInput, Path};
Expand Down Expand Up @@ -43,28 +43,27 @@ impl<'a> Variant<'a> {
pub fn catch_block(&self, enum_name: &Ident) -> TokenStream {
let ident = &self.ident;
let writer = self.writer;
let endian = self.endian;
let fields: Vec<_> = self.fields.iter().map(|v| &v.ident).collect();
let fn_name = format_ident!("digest_{}", self.ident);
match self.enum_type {
EnumType::Unit => {
quote! {
#enum_name::#ident => {
#fn_name::<#endian, W>(#writer);
#fn_name(#writer);
}
}
}
EnumType::Tuple => {
quote! {
#enum_name::#ident(#(#fields),*) => {
#fn_name::<#endian, W>(#writer, #(#fields),*);
#fn_name(#writer, #(#fields),*);
}
}
}
EnumType::Struct => {
quote! {
#enum_name::#ident{#(#fields),*} => {
#fn_name::<#endian, W>(#writer, #(#fields),*);
#fn_name(#writer, #(#fields),*);
}
}
}
Expand All @@ -89,26 +88,32 @@ impl ToTokens for Variant<'_> {
let digest_writer = digest_writer();
let ident = &self.ident;
let writer = self.writer;
let endian = self.endian;
let byte_order_path = crate::paths::byte_order_path();
let result = quote! {
fn #fn_name<#endian: #byte_order_path, W: #digest_writer>(
#writer: &mut W,
#(#fields_def),*
) {
let #fn_name = |#writer: &mut W, #(#fields_def),*| {
#digest_writer::write(writer, stringify!(#ident).as_bytes());
#(#fields)*
}
};
};
tokens.extend(result);
}
}
pub(crate) fn expand(derive_input: DeriveInput) -> Result<TokenStream> {
let name = &derive_input.ident;
let container_attrs = get_container_attrs!(derive_input);
let syn::Data::Enum(as_enum) = derive_input.data else {
unreachable!("digestible can only be derived for enums (expand_enum.rs)")

let DeriveInput {
attrs,
ident,
mut generics,
data,..
} = derive_input;
let syn::Data::Enum(as_enum) = data else {
//Checked before
unsafe{
std::hint::unreachable_unchecked();
}
};
utils::add_digestible_trait(&mut generics);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let container_attrs = get_container_attrs(&attrs)?;

let writer = format_ident!("writer");
let order = format_ident!("B");
Expand All @@ -132,11 +137,11 @@ pub(crate) fn expand(derive_input: DeriveInput) -> Result<TokenStream> {
let variant = Variant::new(variant, &order, &writer)?;
variants.push(variant);
}
let catch_block: Vec<_> = variants.iter().map(|v| v.catch_block(name)).collect();
let catch_block: Vec<_> = variants.iter().map(|v| v.catch_block(&ident)).collect();
let digestible = digestible_path();
let byte_order_path = crate::paths::byte_order_path();
let byte_order_path = crate::utils::byte_order_path();
let impl_hash = if let Some(impl_hash) = container_attrs.impl_hash {
shared::impl_hash(name, impl_hash)
utils::impl_hash(&ident, impl_hash, &impl_generics, &ty_generics, &where_clause)
} else {
quote! {}
};
Expand All @@ -145,7 +150,7 @@ pub(crate) fn expand(derive_input: DeriveInput) -> Result<TokenStream> {
#[allow(unused_extern_crates, clippy::useless_attribute)]
extern crate digestible as _digestible;
#[automatically_derived]
impl #digestible for #name {
impl #impl_generics #digestible for #ident #ty_generics #where_clause {
#[allow(non_snake_case)]
fn digest<#order: #byte_order_path, W: _digestible::DigestWriter>(
&self,
Expand Down
26 changes: 16 additions & 10 deletions macros/src/expand_struct.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
use crate::paths::{digest_writer, digestible_path, private_path};
use crate::container_attrs::{get_container_attrs, ContainerAttrs, TypeHeader};
use crate::utils::{digest_writer, digestible_path, private_path};
use crate::container_attrs::{get_container_attrs, TypeHeader};
use crate::fields::Field;
use crate::shared;
use crate::{utils};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{DeriveInput, Path};
use syn::{Fields, Result};

pub(crate) fn expand(derive_input: DeriveInput) -> Result<TokenStream> {
let name = &derive_input.ident;
let container_attrs = get_container_attrs!(derive_input);
let syn::Data::Struct(as_struct) = derive_input.data else {
unreachable!("digestible can only be derived for enums (expand_struct.rs)")
let DeriveInput{ attrs, ident, mut generics, data,.. } = derive_input;
utils::add_digestible_trait(&mut generics);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

let container_attrs = get_container_attrs(&attrs)?;
let syn::Data::Struct(as_struct) = data else {
// This is checked before
unsafe{
std::hint::unreachable_unchecked();
}
};

let mut fields = Vec::with_capacity(as_struct.fields.len());
Expand Down Expand Up @@ -53,9 +59,9 @@ pub(crate) fn expand(derive_input: DeriveInput) -> Result<TokenStream> {
todo!("type_id")
}
};
let byte_order_path = crate::paths::byte_order_path();
let byte_order_path = utils::byte_order_path();
let impl_hash = if let Some(impl_hash) = container_attrs.impl_hash {
shared::impl_hash(name, impl_hash)
utils::impl_hash(&ident, impl_hash, &impl_generics, &ty_generics, &where_clause)
} else {
quote! {}
};
Expand All @@ -66,7 +72,7 @@ pub(crate) fn expand(derive_input: DeriveInput) -> Result<TokenStream> {
#[allow(unused_extern_crates, clippy::useless_attribute)]
extern crate digestible as _digestible;
#[automatically_derived]
impl #digestible for #name {
impl #impl_generics #digestible for #ident #ty_generics #where_clause {
fn digest<#order: #byte_order_path, W: _digestible::DigestWriter>(
&self,
writer: &mut W,
Expand Down
2 changes: 1 addition & 1 deletion macros/src/fields.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::paths::{digest_with_path, digestible_path};
use crate::utils::{digest_with_path, digestible_path};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::parse::{Parse, ParseStream};
Expand Down
3 changes: 1 addition & 2 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
mod paths;
mod utils;
mod container_attrs;
mod expand_enum;
mod expand_struct;
mod fields;
mod shared;

use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
Expand Down
30 changes: 0 additions & 30 deletions macros/src/paths.rs

This file was deleted.

20 changes: 0 additions & 20 deletions macros/src/shared.rs

This file was deleted.

61 changes: 61 additions & 0 deletions macros/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use proc_macro2::{Ident, TokenStream};
use syn::{GenericParam, Generics, ImplGenerics, parse_quote, Path, TypeGenerics, WhereClause};

pub fn digestible_path() -> Path {
parse_quote!(_digestible::Digestible)
}
pub fn digest_writer() -> Path {
parse_quote!(_digestible::DigestWriter)
}
pub fn digest_with_path(path: Path) -> Path {
parse_quote!(_digestible::digest_with::#path)
}
pub fn digester_using_hasher() -> Path {
parse_quote!(_digestible::hash_digester::DigesterUsingHasher)
}

pub fn byte_order_path() -> Path {
parse_quote!(_digestible::byteorder::ByteOrder)
}
pub fn byte_order_impl_path(ident: Ident) -> Path {
parse_quote!(_digestible::byteorder::#ident)
}

macro_rules! private_path {
// `()` indicates that the macro takes no argument.
($key:ident) => {
syn::parse_quote!(_digestible::_private::$key)
};
}
pub(crate) use private_path;

pub fn add_digestible_trait(generics: &mut Generics) {
if generics.params.is_empty() {
return;
}
for param in &mut generics.params {
if let GenericParam::Type(ty) = param {
ty.bounds.push(parse_quote!(_digestible::Digestible));
}
}
}

use quote::quote;

/// Implements `Hash` for the container.
/// Using Digestible
pub fn impl_hash(container: &Ident, endian_path: Path, impl_generics: &ImplGenerics, ty_generics: &TypeGenerics, where_clause: &Option<&WhereClause>) -> TokenStream {
let digester_using_hasher = digester_using_hasher();
let digestible_path = digestible_path();
let hash: Path = private_path!(Hash);
let hasher: Path = private_path!(Hasher);
quote! {
#[automatically_derived]
impl #impl_generics #hash for #container #ty_generics #where_clause {
fn hash<H: #hasher>(&self, state: &mut H) {
let mut digester = #digester_using_hasher(state);
<Self as #digestible_path>::digest::<#endian_path, _>(self,&mut digester);
}
}
}
}

0 comments on commit 230f0f3

Please sign in to comment.