summaryrefslogtreecommitdiff
path: root/bzipper_macros
diff options
context:
space:
mode:
Diffstat (limited to 'bzipper_macros')
-rw-r--r--bzipper_macros/Cargo.toml23
-rw-r--r--bzipper_macros/src/closure/mod.rs41
-rw-r--r--bzipper_macros/src/discriminant/mod.rs98
-rw-r--r--bzipper_macros/src/generic_name/mod.rs74
-rw-r--r--bzipper_macros/src/impls/deserialise_enum.rs78
-rw-r--r--bzipper_macros/src/impls/deserialise_struct.rs78
-rw-r--r--bzipper_macros/src/impls/mod.rs26
-rw-r--r--bzipper_macros/src/impls/serialise_enum.rs108
-rw-r--r--bzipper_macros/src/impls/serialise_struct.rs77
-rw-r--r--bzipper_macros/src/lib.rs102
10 files changed, 705 insertions, 0 deletions
diff --git a/bzipper_macros/Cargo.toml b/bzipper_macros/Cargo.toml
new file mode 100644
index 0000000..562251e
--- /dev/null
+++ b/bzipper_macros/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "bzipper_macros"
+edition = "2021"
+documentation = "https://docs.rs/bzipper_macros/"
+
+version.workspace = true
+authors.workspace = true
+description.workspace = true
+readme.workspace = true
+homepage.workspace = true
+repository.workspace = true
+license.workspace = true
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "1.0.86"
+quote = "1.0.36"
+syn = "2.0.72"
+
+[lints]
+workspace = true
diff --git a/bzipper_macros/src/closure/mod.rs b/bzipper_macros/src/closure/mod.rs
new file mode 100644
index 0000000..86d19d4
--- /dev/null
+++ b/bzipper_macros/src/closure/mod.rs
@@ -0,0 +1,41 @@
+// Copyright 2024 Gabriel Bjørnager Jensen.
+//
+// This file is part of bzipper.
+//
+// bzipper is free software: you can redistribute
+// it and/or modify it under the terms of the GNU
+// Lesser General Public License as published by
+// the Free Software Foundation, either version 3
+// of the License, or (at your option) any later
+// version.
+//
+// bzipper is distributed in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Less-
+// er General Public License along with bzipper. If
+// not, see <https://www.gnu.org/licenses/>.
+
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::{Ident, Token};
+
+/// A field capture list.
+///
+/// This is used for capturing fields of structures or enumeration variants.
+#[derive(Clone)]
+pub struct Capture {
+ pub ref_token: Token![ref],
+ pub ident: Ident,
+}
+
+impl ToTokens for Capture {
+ #[inline(always)]
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.ref_token.to_tokens(tokens);
+ self.ident.to_tokens(tokens);
+ }
+}
diff --git a/bzipper_macros/src/discriminant/mod.rs b/bzipper_macros/src/discriminant/mod.rs
new file mode 100644
index 0000000..21a835a
--- /dev/null
+++ b/bzipper_macros/src/discriminant/mod.rs
@@ -0,0 +1,98 @@
+// Copyright 2024 Gabriel Bjørnager Jensen.
+//
+// This file is part of bzipper.
+//
+// bzipper is free software: you can redistribute
+// it and/or modify it under the terms of the GNU
+// Lesser General Public License as published by
+// the Free Software Foundation, either version 3
+// of the License, or (at your option) any later
+// version.
+//
+// bzipper is distributed in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Less-
+// er General Public License along with bzipper. If
+// not, see <https://www.gnu.org/licenses/>.
+
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+
+/// An enumeration discriminant.
+#[derive(Clone, Copy)]
+#[repr(transparent)]
+pub struct Discriminant(u32);
+
+impl Discriminant {
+ /// Constructs a new discriminant.
+ #[inline(always)]
+ #[must_use]
+ pub const fn new(value: u32) -> Self { Self(value) }
+
+ /// Retrieves the raw discriminant value.
+ #[inline(always)]
+ #[must_use]
+ pub const fn get(self) -> u32 { self.0 }
+
+ /// Unwraps the given value as a discriminant.
+ ///
+ /// # Panics
+ ///
+ /// If the given value cannot be represented as an `u32`, this function will panic.
+ #[inline(always)]
+ #[must_use]
+ pub fn unwrap_from<T: TryInto<Self>>(value: T) -> Self {
+ value
+ .try_into()
+ .unwrap_or_else(|_| panic!("enumeration discriminants must be representable in `u32`"))
+ }
+
+ /// Unsafely unwraps the given value as a discriminant.
+ ///
+ /// This function assumes that this conversion is infallible for the given value.
+ /// If this is a false guarantee, the [`unwrap_from`](Self::unwrap_from) function should be used instead.
+ ///
+ /// # Safety
+ ///
+ /// Behaviour is undefined if the given value cannot be represented as an object of `u32`.
+ #[inline(always)]
+ #[must_use]
+ pub unsafe fn unwrap_from_unchecked<T: TryInto<Self>>(value: T) -> Self {
+ value
+ .try_into()
+ .unwrap_unchecked()
+ }
+}
+
+impl ToTokens for Discriminant {
+ #[inline(always)]
+ fn to_tokens(&self, tokens: &mut TokenStream) { self.0.to_tokens(tokens) }
+}
+
+impl From<u32> for Discriminant {
+ #[inline(always)]
+ fn from(value: u32) -> Self { Self(value) }
+}
+
+impl TryFrom<usize> for Discriminant {
+ type Error = <u32 as TryFrom<usize>>::Error;
+
+ #[inline(always)]
+ fn try_from(value: usize) -> Result<Self, Self::Error> { value.try_into().map(Self) }
+}
+
+impl From<Discriminant> for u32 {
+ #[inline(always)]
+ fn from(value: Discriminant) -> Self { value.0 }
+}
+
+impl TryFrom<Discriminant> for usize {
+ type Error = <Self as TryFrom<u32>>::Error;
+
+ #[inline(always)]
+ fn try_from(value: Discriminant) -> Result<Self, Self::Error> { value.0.try_into() }
+}
diff --git a/bzipper_macros/src/generic_name/mod.rs b/bzipper_macros/src/generic_name/mod.rs
new file mode 100644
index 0000000..a0c51f5
--- /dev/null
+++ b/bzipper_macros/src/generic_name/mod.rs
@@ -0,0 +1,74 @@
+// Copyright 2024 Gabriel Bjørnager Jensen.
+//
+// This file is part of bzipper.
+//
+// bzipper is free software: you can redistribute
+// it and/or modify it under the terms of the GNU
+// Lesser General Public License as published by
+// the Free Software Foundation, either version 3
+// of the License, or (at your option) any later
+// version.
+//
+// bzipper is distributed in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Less-
+// er General Public License along with bzipper. If
+// not, see <https://www.gnu.org/licenses/>.
+
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::{
+ GenericParam,
+ Generics,
+ Ident,
+ Lifetime,
+ Token,
+ punctuated::Punctuated,
+};
+
+/// A name of a genric.
+#[derive(Clone)]
+pub enum GenericName {
+ Const( Ident),
+ Lifetime(Lifetime),
+ Type( Ident),
+}
+
+impl GenericName {
+ /// Extracts the names of the given generics.
+ #[must_use]
+ pub fn extract_from(generics: &Generics) -> Punctuated<Self, Token![,]> {
+ let mut names = Punctuated::new();
+
+ for generic in &generics.params {
+ let name = match *generic {
+ GenericParam::Const( ref param) => Self::Const( param.ident.clone()),
+ GenericParam::Lifetime(ref param) => Self::Lifetime(param.lifetime.clone()),
+ GenericParam::Type( ref param) => Self::Type( param.ident.clone()),
+ };
+
+ names.push(name);
+ }
+
+ names
+ }
+}
+
+impl ToTokens for GenericName {
+ #[inline(always)]
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ use GenericName::*;
+
+ match *self {
+ | Const(ref ident)
+ | Type( ref ident)
+ => ident.to_tokens(tokens),
+
+ Lifetime(ref lifetime) => lifetime.to_tokens(tokens),
+ }
+ }
+}
diff --git a/bzipper_macros/src/impls/deserialise_enum.rs b/bzipper_macros/src/impls/deserialise_enum.rs
new file mode 100644
index 0000000..7bf0220
--- /dev/null
+++ b/bzipper_macros/src/impls/deserialise_enum.rs
@@ -0,0 +1,78 @@
+// Copyright 2024 Gabriel Bjørnager Jensen.
+//
+// This file is part of bzipper.
+//
+// bzipper is free software: you can redistribute
+// it and/or modify it under the terms of the GNU
+// Lesser General Public License as published by
+// the Free Software Foundation, either version 3
+// of the License, or (at your option) any later
+// version.
+//
+// bzipper is distributed in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Less-
+// er General Public License along with bzipper. If
+// not, see <https://www.gnu.org/licenses/>.
+
+use crate::Discriminant;
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{DataEnum, Fields, Token};
+use syn::punctuated::Punctuated;
+
+#[must_use]
+pub fn deserialise_enum(data: &DataEnum) -> TokenStream {
+ let mut match_arms = Punctuated::<TokenStream, Token![,]>::new();
+
+ for (index, variant) in data.variants.iter().enumerate() {
+ let variant_name = &variant.ident;
+
+ let discriminant = Discriminant::unwrap_from(index);
+
+ let block = if matches!(variant.fields, Fields::Unit) {
+ quote! { Self }
+ } else {
+ let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
+
+ for field in &variant.fields {
+ let field_ty = &field.ty;
+
+ let command = field.ident
+ .as_ref()
+ .map_or_else(
+ || quote! { stream.take::<#field_ty>()? },
+ |field_name| quote! { #field_name: stream.take::<#field_ty>()? }
+ );
+
+ chain_commands.push(command);
+ }
+
+ match variant.fields {
+ Fields::Named( ..) => quote! { Self::#variant_name { #chain_commands } },
+ Fields::Unnamed(..) => quote! { Self::#variant_name(#chain_commands) },
+ Fields::Unit => unreachable!(),
+ }
+ };
+
+ match_arms.push(quote! { #discriminant => #block });
+ }
+
+ match_arms.push(quote! { value => return Err(::bzipper::Error::InvalidDiscriminant { value }) });
+
+ quote! {
+ fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
+ ::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
+
+ let mut stream = ::bzipper::Dstream::new(data);
+
+ let value = match (stream.take::<u32>()?) { #match_arms };
+ Ok(value)
+ }
+ }
+}
diff --git a/bzipper_macros/src/impls/deserialise_struct.rs b/bzipper_macros/src/impls/deserialise_struct.rs
new file mode 100644
index 0000000..414a313
--- /dev/null
+++ b/bzipper_macros/src/impls/deserialise_struct.rs
@@ -0,0 +1,78 @@
+// Copyright 2024 Gabriel Bjørnager Jensen.
+//
+// This file is part of bzipper.
+//
+// bzipper is free software: you can redistribute
+// it and/or modify it under the terms of the GNU
+// Lesser General Public License as published by
+// the Free Software Foundation, either version 3
+// of the License, or (at your option) any later
+// version.
+//
+// bzipper is distributed in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Less-
+// er General Public License along with bzipper. If
+// not, see <https://www.gnu.org/licenses/>.
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{DataStruct, Fields, Token};
+use syn::punctuated::Punctuated;
+
+#[must_use]
+pub fn deserialise_struct(data: &DataStruct) -> TokenStream {
+ if let Fields::Named(..) = data.fields {
+ let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
+
+ for field in &data.fields {
+ let name = field.ident.as_ref().unwrap();
+ let ty = &field.ty;
+
+ chain_commands.push(quote! { #name: stream.take::<#ty>()? });
+ }
+
+ quote! {
+ fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
+ ::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
+
+ let stream = ::bzipper::Dstream::new(data);
+
+ Ok(Self { #chain_commands })
+ }
+ }
+ } else if let Fields::Unnamed(..) = data.fields {
+ let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
+
+ for field in &data.fields {
+ let ty = &field.ty;
+
+ chain_commands.push(quote! { stream.take::<#ty>()? });
+ }
+
+ quote! {
+ fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
+ ::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
+
+ let stream = ::bzipper::Dstream::new(data);
+
+ Ok(Self(#chain_commands))
+ }
+ }
+ } else {
+ // Fields::Unit
+
+ quote! {
+ #[inline(always)]
+ fn deserialise(data: &[u8]) -> ::bzipper::Result<Self> {
+ ::core::debug_assert_eq!(data.len(), <Self as ::bzipper::Serialise>::SERIALISED_SIZE);
+
+ Ok(Self)
+ }
+ }
+ }
+}
diff --git a/bzipper_macros/src/impls/mod.rs b/bzipper_macros/src/impls/mod.rs
new file mode 100644
index 0000000..d61cf90
--- /dev/null
+++ b/bzipper_macros/src/impls/mod.rs
@@ -0,0 +1,26 @@
+// Copyright 2024 Gabriel Bjørnager Jensen.
+//
+// This file is part of bzipper.
+//
+// bzipper is free software: you can redistribute
+// it and/or modify it under the terms of the GNU
+// Lesser General Public License as published by
+// the Free Software Foundation, either version 3
+// of the License, or (at your option) any later
+// version.
+//
+// bzipper is distributed in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Less-
+// er General Public License along with bzipper. If
+// not, see <https://www.gnu.org/licenses/>.
+
+use crate::use_mod;
+use_mod!(pub deserialise_enum);
+use_mod!(pub deserialise_struct);
+use_mod!(pub serialise_enum);
+use_mod!(pub serialise_struct);
diff --git a/bzipper_macros/src/impls/serialise_enum.rs b/bzipper_macros/src/impls/serialise_enum.rs
new file mode 100644
index 0000000..8f0693a
--- /dev/null
+++ b/bzipper_macros/src/impls/serialise_enum.rs
@@ -0,0 +1,108 @@
+// Copyright 2024 Gabriel Bjørnager Jensen.
+//
+// This file is part of bzipper.
+//
+// bzipper is free software: you can redistribute
+// it and/or modify it under the terms of the GNU
+// Lesser General Public License as published by
+// the Free Software Foundation, either version 3
+// of the License, or (at your option) any later
+// version.
+//
+// bzipper is distributed in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Less-
+// er General Public License along with bzipper. If
+// not, see <https://www.gnu.org/licenses/>.
+
+use crate::Capture;
+
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{DataEnum, Fields, Ident, Token};
+use syn::punctuated::Punctuated;
+
+#[must_use]
+pub fn serialise_enum(data: &DataEnum) -> TokenStream {
+ let mut sizes = Vec::new();
+
+ let mut match_arms = Punctuated::<TokenStream, Token![,]>::new();
+
+ for (index, variant) in data.variants.iter().enumerate() {
+ let mut serialised_size = Punctuated::<TokenStream, Token![+]>::new();
+
+ let name = &variant.ident;
+
+ let discriminant = u32::try_from(index)
+ .expect("enumeration discriminants must be representable in `u32`");
+
+ // Discriminant size:
+ serialised_size.push(quote! { <u32 as ::bzipper::Serialise>::SERIALISED_SIZE });
+
+ let arm = if matches!(variant.fields, Fields::Unit) {
+ quote! { Self::#name => stream.append(&#discriminant)? }
+ } else {
+ let mut captures = Punctuated::<Capture, Token![,]>::new();
+
+ let mut chain_commands = Punctuated::<TokenStream, Token![;]>::new();
+ chain_commands.push(quote! { stream.append(&#discriminant)? });
+
+ for (index, field) in variant.fields.iter().enumerate() {
+ let field_ty = &field.ty;
+
+ let field_name = field.ident
+ .as_ref()
+ .map_or_else(|| Ident::new(&format!("v{index}"), Span::call_site()), Clone::clone);
+
+ serialised_size.push(quote! { <#field_ty as ::bzipper::Serialise>::SERIALISED_SIZE });
+
+ captures.push(Capture {
+ ref_token: Token![ref](Span::call_site()),
+ ident: field_name.clone(),
+ });
+
+ chain_commands.push(quote! { stream.append(#field_name)? });
+ }
+
+ chain_commands.push_punct(Token![;](Span::call_site()));
+
+ match variant.fields {
+ Fields::Named( ..) => quote! { Self::#name { #captures } => { #chain_commands } },
+ Fields::Unnamed(..) => quote! { Self::#name(#captures) => { #chain_commands } },
+ Fields::Unit => unreachable!(),
+ }
+ };
+
+ sizes.push(serialised_size);
+ match_arms.push(arm);
+ }
+
+ let mut size_tests = Punctuated::<TokenStream, Token![else]>::new();
+
+ for size in &sizes {
+ let mut test = Punctuated::<TokenStream, Token![&&]>::new();
+
+ for other_size in &sizes { test.push(quote! { #size >= #other_size }) }
+
+ size_tests.push(quote! { if #test { #size } });
+ }
+
+ size_tests.push(quote! { { core::unreachable!(); } });
+
+ quote! {
+ const SERIALISED_SIZE: usize = const { #size_tests };
+
+ fn serialise(&self, buf: &mut [u8]) -> ::bzipper::Result<()> {
+ ::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
+
+ let mut stream = ::bzipper::Sstream::new(buf);
+
+ match (*self) { #match_arms }
+ Ok(())
+ }
+ }
+}
diff --git a/bzipper_macros/src/impls/serialise_struct.rs b/bzipper_macros/src/impls/serialise_struct.rs
new file mode 100644
index 0000000..308a6bb
--- /dev/null
+++ b/bzipper_macros/src/impls/serialise_struct.rs
@@ -0,0 +1,77 @@
+// Copyright 2024 Gabriel Bjørnager Jensen.
+//
+// This file is part of bzipper.
+//
+// bzipper is free software: you can redistribute
+// it and/or modify it under the terms of the GNU
+// Lesser General Public License as published by
+// the Free Software Foundation, either version 3
+// of the License, or (at your option) any later
+// version.
+//
+// bzipper is distributed in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Less-
+// er General Public License along with bzipper. If
+// not, see <https://www.gnu.org/licenses/>.
+
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, ToTokens};
+use syn::{
+ DataStruct,
+ Fields,
+ Index,
+ Token,
+ punctuated::Punctuated
+};
+
+#[must_use]
+pub fn serialise_struct(data: &DataStruct) -> TokenStream {
+ if matches!(data.fields, Fields::Unit) {
+ quote! {
+ const SERIALISED_SIZE: usize = 0x0;
+
+ #[inline(always)]
+ fn serialise(&self, buf: &mut [u8]) -> ::bzipper::Result<()> {
+ ::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
+
+ Ok(())
+ }
+ }
+ } else {
+ let mut serialised_size = Punctuated::<TokenStream, Token![+]>::new();
+ let mut chain_commands = Punctuated::<TokenStream, Token![;]>::new();
+
+ for (index, field) in data.fields.iter().enumerate() {
+ let ty = &field.ty;
+
+ let name = field.ident
+ .as_ref()
+ .map_or_else(|| Index::from(index).to_token_stream(), ToTokens::to_token_stream);
+
+ serialised_size.push(quote! { <#ty as ::bzipper::Serialise>::SERIALISED_SIZE });
+
+ chain_commands.push(quote! { stream.append(&self.#name)? });
+ }
+
+ chain_commands.push_punct(Token![;](Span::call_site()));
+
+ quote! {
+ const SERIALISED_SIZE: usize = #serialised_size;
+
+ fn serialise(&self, buf: &mut [u8]) -> ::bzipper::Result<()> {
+ ::core::debug_assert_eq!(buf.len(), Self::SERIALISED_SIZE);
+
+ let mut stream = ::bzipper::Sstream::new(buf);
+
+ #chain_commands
+
+ Ok(())
+ }
+ }
+ }
+}
diff --git a/bzipper_macros/src/lib.rs b/bzipper_macros/src/lib.rs
new file mode 100644
index 0000000..f7979c8
--- /dev/null
+++ b/bzipper_macros/src/lib.rs
@@ -0,0 +1,102 @@
+// Copyright 2024 Gabriel Bjørnager Jensen.
+//
+// This file is part of bzipper.
+//
+// bzipper is free software: you can redistribute
+// it and/or modify it under the terms of the GNU
+// Lesser General Public License as published by
+// the Free Software Foundation, either version 3
+// of the License, or (at your option) any later
+// version.
+//
+// bzipper is distributed in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without
+// even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Less-
+// er General Public License along with bzipper. If
+// not, see <https://www.gnu.org/licenses/>.
+
+#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg?ref_type=heads")]
+
+//! Binary (de)serialisation.
+//!
+//! This crate implements macros for the [`bzipper`](https://crates.io/crates/bzipper/) crate.
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{Data, DeriveInput, parse_macro_input};
+
+macro_rules! use_mod {
+ ($vis:vis $name:ident) => {
+ mod $name;
+ $vis use $name::*;
+ };
+}
+pub(in crate) use use_mod;
+
+use_mod!(closure);
+use_mod!(discriminant);
+use_mod!(generic_name);
+
+mod impls;
+
+#[proc_macro_derive(Deserialise)]
+pub fn derive_deserialise(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+
+ let impl_body = match input.data {
+ Data::Enum( ref data) => impls::deserialise_enum( data),
+ Data::Struct(ref data) => impls::deserialise_struct(data),
+
+ Data::Union(..) => panic!("unions cannot derive `Deserialise`"),
+ };
+
+ let type_name = &input.ident;
+
+ let generic_params = &input.generics.params;
+ let generic_where = &input.generics.where_clause;
+
+ let generic_names = GenericName::extract_from(&input.generics);
+
+ let output = quote! {
+ impl<#generic_params> ::bzipper::Deserialise for #type_name<#generic_names>
+ #generic_where {
+ #impl_body
+ }
+ };
+
+ output.into()
+}
+
+#[proc_macro_derive(Serialise)]
+pub fn derive_serialise(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+
+ let impl_body = match input.data {
+ Data::Enum( ref data) => impls::serialise_enum( data),
+ Data::Struct(ref data) => impls::serialise_struct(data),
+
+ Data::Union(..) => panic!("unions cannot derive `Serialise`"),
+ };
+
+ let type_name = &input.ident;
+
+ let generic_params = &input.generics.params;
+ let generic_where = &input.generics.where_clause;
+
+ let generic_names = GenericName::extract_from(&input.generics);
+
+ let output = quote! {
+ impl<#generic_params> ::bzipper::Serialise for #type_name<#generic_names>
+ #generic_where {
+ #impl_body
+ }
+ };
+
+ //if let Data::Enum(..) = input.data { panic!("{output}") };
+
+ output.into()
+}