use std::iter::empty;
use proc_macro2::Span;
use quote::{ToTokens, format_ident, quote};
use serde::receiver::replace_receiver;
use syn::ext::IdentExt;
use syn::parse::Parse as _;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned as _;
use syn::token::Comma;
use syn::{
Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Index, Meta, Path, Token, Type,
Variant, WherePredicate, parenthesized, parse_macro_input, parse_quote,
};
mod serde;
fn try_set_attribute<T: ToTokens>(
attribute: &mut Option<T>,
value: T,
name: &'static str,
) -> Result<(), syn::Error> {
if attribute.is_none() {
*attribute = Some(value);
Ok(())
} else {
Err(syn::Error::new_spanned(
value,
format!("{name} already specified"),
))
}
}
fn field_bounded(field: &&Field) -> bool {
!field.attrs.iter().any(|attribute| {
attribute.path().is_ident("nibblecode") && {
let mut found = false;
let _ = attribute.parse_nested_meta(|meta| {
if meta.path.is_ident("recursive") {
found = true;
}
Ok(())
});
found
}
})
}
fn max_alignment<'a>(
crate_path: &Path,
types: impl Iterator<Item = &'a Type>,
) -> proc_macro2::TokenStream {
quote! { #crate_path::util::max_alignment([#(<#types as #crate_path::Serialize>::ALIGN),*]) }
}
fn impl_serialize(
crate_path: &Path,
ident: &Ident,
generics: &Generics,
archived: &Type,
alignment: proc_macro2::TokenStream,
copy_optimization: proc_macro2::TokenStream,
serialize_fn: proc_macro2::TokenStream,
size_fn: proc_macro2::TokenStream,
verify_fn: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
quote! {
#[automatically_derived]
impl #impl_generics #crate_path::Serialize for #ident #type_generics
#where_clause
{
type Archived = #archived;
const ALIGN: ::core::ptr::Alignment = #alignment;
#copy_optimization
unsafe #serialize_fn
#size_fn
#[inline]
unsafe #verify_fn
}
}
}
fn generate_where_clause<'a>(
crate_path: &Path,
generics: &mut Generics,
fields: impl Iterator<Item = &'a Field>,
derives: &[Path],
) {
let where_clause = generics.make_where_clause();
for Field { ty, .. } in fields {
where_clause
.predicates
.push(parse_quote! { #ty: #crate_path::Serialize });
if !derives.is_empty() {
where_clause.predicates.push(parse_quote! {
<#ty as #crate_path::Serialize>::Archived: #(#derives)+*
});
}
}
}
fn impl_struct<'a, T: ToTokens>(
crate_path: &Path,
ident: &Ident,
generics: &mut Generics,
archived: &Type,
fields: impl Iterator<Item = &'a Field> + Clone,
field_idents: impl Iterator<Item = T> + Clone,
derives: &[Path],
) -> proc_macro2::TokenStream {
let types = fields.clone().map(|field| &field.ty);
let types2 = types.clone();
let field_idents2 = field_idents.clone();
let field_idents3 = field_idents.clone();
let field_idents4 = field_idents.clone();
generate_where_clause(crate_path, generics, fields.clone(), derives);
impl_serialize(
crate_path,
ident,
generics,
archived,
max_alignment(crate_path, types.clone()),
quote! {
const COPY_OPTIMIZATION: bool = true #(
&& ::core::mem::offset_of!(Self, #field_idents4)
== ::core::mem::offset_of!(Self::Archived, #field_idents4)
&& <#types as #crate_path::Serialize>::COPY_OPTIMIZATION
)*;
},
quote! {
fn serialize(
&self,
out: *mut ::core::mem::MaybeUninit<Self::Archived>,
mut heap: *mut ::core::mem::MaybeUninit<u8>,
) -> usize {
let heap_start = heap;
let out = out.cast::<Self::Archived>();
unsafe {
#(
heap = heap.add(#crate_path::Serialize::serialize(
&self.#field_idents,
(&raw mut (*out).#field_idents).cast(),
heap,
));
)*
heap.offset_from_unsigned(heap_start)
}
}
},
quote! {
fn serialized_size(
&self,
mut offset: usize
) -> Result<usize, #crate_path::SerializeError> {
let offset_start = offset;
#(offset += self.#field_idents2.serialized_size(offset)?;)*
Ok(offset - offset_start)
}
},
quote! {
fn verify(
this: *const Self::Archived,
buffer_end: *const u8
) -> Result<(), #crate_path::VerifyError> {
unsafe {
#(
<#types2>::verify(
&raw const (*this).#field_idents3, buffer_end
)?;
)*
}
Ok(())
}
},
)
}
fn enum_serialize_arm<T: ToTokens>(
crate_path: &Path,
field_idents: impl Iterator<Item = Ident>,
variant_ident: &Ident,
fields: impl Iterator<Item = T>,
pattern: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
quote! {
#pattern => {
*out.cast() = #variant_ident;
#(
heap = heap.add(#crate_path::Serialize::serialize(
#field_idents,
out.cast::<u8>()
.add(::core::mem::offset_of!(Self::Archived, #variant_ident.#fields))
.cast(),
heap,
));
)*
}
}
}
fn enum_verify_arm<T: ToTokens>(
variant_ident: &Ident,
fields: &Punctuated<Field, Comma>,
field_idents: impl Iterator<Item = T>,
) -> proc_macro2::TokenStream {
let types = fields.iter().map(|field| &field.ty);
quote! {
#variant_ident => {
#(
<#types>::verify(
this.cast::<u8>()
.add(::core::mem::offset_of!(Self::Archived, #variant_ident.#field_idents))
.cast(),
buffer_end
)?;
)*
}
}
}
fn impl_compare<'a>(
token_stream: &mut proc_macro2::TokenStream,
crate_path: &Path,
compares: Option<Punctuated<Path, Comma>>,
generics: &mut Generics,
fields: impl Iterator<Item = &'a Field> + Clone,
ident: &Ident,
mut eq: impl FnMut() -> proc_macro2::TokenStream,
mut ord: impl FnMut() -> proc_macro2::TokenStream,
) {
if let Some(compares) = compares {
for compare in compares {
token_stream.extend(if compare.is_ident("PartialEq") {
let mut where_clause = generics.make_where_clause().clone();
for Field { ty, .. } in fields.clone() {
where_clause.predicates.push(
parse_quote! { <#ty as #crate_path::Serialize>::Archived: PartialEq<#ty> },
);
}
let (impl_generics, type_generics, _) = generics.split_for_impl();
let body = eq();
quote! {
impl #impl_generics PartialEq<<Self as #crate_path::Serialize>::Archived>
for #ident #type_generics
#where_clause
{
fn eq(&self, other: &<Self as #crate_path::Serialize>::Archived) -> bool {
#body
}
}
impl #impl_generics PartialEq<#ident #type_generics>
for <#ident #type_generics as #crate_path::Serialize>::Archived
#where_clause
{
fn eq(&self, other: &#ident #type_generics) -> bool {
other.eq(self)
}
}
}
} else if compare.is_ident("PartialOrd") {
let mut where_clause = generics.make_where_clause().clone();
for Field { ty, .. } in fields.clone() {
where_clause.predicates.push(
parse_quote! { <#ty as #crate_path::Serialize>::Archived: PartialOrd<#ty> },
);
}
let (impl_generics, type_generics, _) = generics.split_for_impl();
let body = ord();
quote! {
impl #impl_generics PartialOrd<<Self as #crate_path::Serialize>::Archived>
for #ident #type_generics
#where_clause
{
fn partial_cmp(
&self,
other: &<Self as #crate_path::Serialize>::Archived,
) -> Option<::core::cmp::Ordering> {
#body
}
}
impl #impl_generics PartialOrd<#ident #type_generics>
for <#ident #type_generics as #crate_path::Serialize>::Archived
#where_clause
{
fn partial_cmp(
&self,
other: &#ident #type_generics,
) -> Option<::core::cmp::Ordering> {
other.partial_cmp(self).map(::core::cmp::Ordering::reverse)
}
}
}
} else {
syn::Error::new_spanned(
compare,
"unrecognized compare argument, supported compares are PartialEq (PartialOrd \
is not supported for enums)",
)
.to_compile_error()
})
}
}
}
#[proc_macro_derive(Serialize, attributes(nibblecode))]
pub fn derive_serialize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut derive_input = parse_macro_input!(input as DeriveInput);
replace_receiver(&mut derive_input);
let DeriveInput {
attrs,
vis,
ident,
mut generics,
data,
..
} = derive_input;
let mut as_type = None;
let mut compares = None;
let mut archive_bounds = None;
let mut crate_path = None;
let mut struct_attrs = Vec::<Meta>::new();
let mut archived = None;
for attr in attrs {
if attr.path().is_ident("nibblecode")
&& let Err(error) = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("compare") {
let traits;
parenthesized!(traits in meta.input);
let traits = traits.parse_terminated(Path::parse, Token![,])?;
try_set_attribute(&mut compares, traits, "compare")
} else if meta.path.is_ident("archive_bounds") {
let bounds;
parenthesized!(bounds in meta.input);
let clauses = bounds.parse_terminated(WherePredicate::parse, Token![,])?;
try_set_attribute(&mut archive_bounds, clauses, "archive_bounds")
} else if meta.path.is_ident("archived") {
try_set_attribute(&mut archived, meta.value()?.parse()?, "archived")
} else if meta.path.is_ident("as") {
meta.input.parse::<Token![=]>()?;
try_set_attribute(&mut as_type, meta.input.parse::<Type>()?, "as")
} else if meta.path.is_ident("crate") {
if meta.input.parse::<Token![=]>().is_ok() {
let path = meta.input.parse::<Path>()?;
try_set_attribute(&mut crate_path, path, "crate")
} else if meta.input.is_empty() || meta.input.peek(Token![,]) {
try_set_attribute(&mut crate_path, parse_quote! { crate }, "crate")
} else {
Err(meta.error("expected `crate` or `crate = ...`"))
}
} else if meta.path.is_ident("derive") {
let metas;
parenthesized!(metas in meta.input);
struct_attrs.extend(
metas
.parse_terminated(Meta::parse, Token![,])?
.into_iter()
.map(|meta| parse_quote! { derive(#meta) }),
);
Ok(())
} else if meta.path.is_ident("attr") {
let metas;
parenthesized!(metas in meta.input);
struct_attrs.extend(metas.parse_terminated(Meta::parse, Token![,])?);
Ok(())
} else {
Err(meta.error("unrecognized nibblecode argument"))
}
}) {
return error.to_compile_error().into();
}
}
if as_type.is_some()
|| matches!(
data,
Data::Struct(DataStruct {
fields: Fields::Unit,
..
})
) {
if let Some(first) = struct_attrs.first() {
return syn::Error::new_spanned(
first,
"attributes may not be used with `as = ...` or unit structs; place attributes on \
the archived type instead",
)
.to_compile_error()
.into();
}
if let Some(compares) = compares {
return syn::Error::new_spanned(
compares,
"compares may not be used with `as = ...` or unit structs; place derives on the \
archived type instead",
)
.to_compile_error()
.into();
}
}
if let Some(bounds) = archive_bounds {
let where_clause = generics.make_where_clause();
where_clause.predicates.extend(bounds);
}
let type_generics = generics.split_for_impl().1;
let crate_path = crate_path.unwrap_or_else(|| parse_quote! { ::nibblecode });
let doc_string = format!("An archived [`{ident}`]");
let archived_ident = archived.unwrap_or_else(|| format_ident!("Archived{}", ident.unraw()));
let archived = as_type
.clone()
.unwrap_or_else(|| parse_quote! { #archived_ident #type_generics });
let derives = struct_attrs
.iter()
.filter(|meta| meta.path().is_ident("derive"))
.flat_map(|meta| meta.require_list())
.flat_map(|meta_list| meta_list.parse_args::<Path>())
.collect::<Vec<Path>>();
match data {
Data::Struct(data_struct) => match &data_struct.fields {
Fields::Named(fields_named) => {
let bounded_fields = fields_named.named.iter().filter(field_bounded);
let field_idents = fields_named.named.iter().map(|field| &field.ident);
let mut implementation = impl_struct(
&crate_path,
&ident,
&mut generics,
&archived,
bounded_fields.clone(),
field_idents.clone(),
&derives,
);
impl_compare(
&mut implementation,
&crate_path,
compares,
&mut generics,
bounded_fields,
&ident,
|| {
let field_idents = field_idents.clone();
quote! {
true #(&& other.#field_idents.eq(&self.#field_idents))*
}
},
|| {
let field_idents = field_idents.clone();
quote! {
#(
match other.#field_idents.partial_cmp(&self.#field_idents) {
Some(::core::cmp::Ordering::Equal) => (),
x => return x.map(::core::cmp::Ordering::reverse),
}
)*
Some(::core::cmp::Ordering::Equal)
}
},
);
if as_type.is_none() {
let archived_fields = fields_named.named.iter().map(|field| {
let Field {
vis,
ident: Some(field_ident),
colon_token,
ty,
..
} = field
else {
unreachable!()
};
let field_doc =
format!("The archived counterpart of [`{ident}::{field_ident}`]");
quote! {
#[doc = #field_doc]
#vis #field_ident #colon_token
<#ty as #crate_path::Serialize>::Archived
}
});
let where_clause = &generics.where_clause;
implementation.extend(quote! {
#[automatically_derived]
#[doc = #doc_string]
#[repr(C)]
#(#[#struct_attrs])*
#vis struct #archived_ident #generics #where_clause {
#(#archived_fields),*
}
})
}
implementation
}
Fields::Unnamed(fields_unnamed) => {
let bounded_fields = fields_unnamed.unnamed.iter().filter(field_bounded);
let indices = (0..fields_unnamed.unnamed.len()).map(Index::from);
let mut implementation = impl_struct(
&crate_path,
&ident,
&mut generics,
&archived,
bounded_fields.clone(),
indices.clone(),
&derives,
);
impl_compare(
&mut implementation,
&crate_path,
compares,
&mut generics,
bounded_fields,
&ident,
|| {
let indices = indices.clone();
quote! {
true #(&& other.#indices.eq(&self.#indices))*
}
},
|| {
let indices = indices.clone();
quote! {
#(
match other.#indices.partial_cmp(&self.#indices) {
Some(::core::cmp::Ordering::Equal) => (),
x => return x.map(::core::cmp::Ordering::reverse),
}
)*
Some(::core::cmp::Ordering::Equal)
}
},
);
if as_type.is_none() {
let archived_fields = fields_unnamed.unnamed.iter().enumerate().map(
|(i, Field { vis, ty, .. })| {
let field_doc = format!("The archived counterpart of [`{ident}::{i}`]");
quote! {
#[doc = #field_doc]
#vis <#ty as #crate_path::Serialize>::Archived
}
},
);
let where_clause = &generics.where_clause;
implementation.extend(quote! {
#[automatically_derived]
#[doc = #doc_string]
#[repr(C)]
#(#[#struct_attrs])*
#vis struct #archived_ident #generics(#(#archived_fields),*)
#where_clause;
})
}
implementation
}
Fields::Unit => {
let mut implementation = impl_serialize(
&crate_path,
&ident,
&generics,
&parse_quote! { #ident #type_generics },
quote! { ::core::ptr::Alignment::MIN },
quote! { const COPY_OPTIMIZATION: bool = true; },
quote! {
fn serialize(
&self,
_: *mut ::core::mem::MaybeUninit<Self::Archived>,
_: *mut ::core::mem::MaybeUninit<u8>,
) -> usize {
0
}
},
quote! {
fn serialized_size(
&self,
_: usize
) -> Result<usize, #crate_path::SerializeError> {
Ok(0)
}
},
quote! {
fn verify(
_: *const Self::Archived,
_: *const u8
) -> Result<(), #crate_path::VerifyError> {
Ok(())
}
},
);
impl_compare(
&mut implementation,
&crate_path,
compares,
&mut generics,
empty(),
&ident,
|| quote! { true },
|| quote! { Some(::core::cmp::Ordering::Equal) },
);
implementation
}
},
Data::Enum(data_enum) => {
if 256 < data_enum.variants.len() {
return syn::Error::new(
ident.span(),
"enums with more than 256 variants cannot derive Archive",
)
.to_compile_error()
.into();
}
let archived_variant_tags = data_enum.variants.iter().map(
|Variant {
ident,
discriminant,
..
}| {
if let Some((eq, expr)) = discriminant {
quote! { #ident #eq #expr }
} else {
ident.to_token_stream()
}
},
);
let fields = data_enum
.variants
.iter()
.flat_map(|variant| &variant.fields);
let bounded_fields = fields.clone().filter(field_bounded);
let types = bounded_fields.clone().map(|field| &field.ty);
let serialize_arms =
data_enum
.variants
.iter()
.map(|Variant { ident, fields, .. }| match fields {
Fields::Named(fields_named) => {
let field_idents = fields_named
.named
.iter()
.map(|field| field.ident.as_ref().unwrap().clone());
enum_serialize_arm(
&crate_path,
field_idents.clone(),
ident,
field_idents.clone(),
quote! { Self::#ident { #(#field_idents),* } },
)
}
Fields::Unnamed(fields_unnamed) => {
let field_idents = (0..fields_unnamed.unnamed.len())
.map(|i| format_ident!("self_{i}"));
enum_serialize_arm(
&crate_path,
field_idents.clone(),
ident,
(0..fields_unnamed.unnamed.len()).map(Index::from),
quote! { Self::#ident(#(#field_idents),*) },
)
}
Fields::Unit => quote! { Self::#ident => *out.cast() = #ident },
});
let size_arms = data_enum.variants.iter().filter_map(
|Variant { ident, fields, .. }| match fields {
Fields::Named(fields_named) => {
let field_idents = fields_named.named.iter().map(|field| &field.ident);
let field_idents2 = field_idents.clone();
Some(quote! {
Self::#ident { #(#field_idents),* } => {
#(offset += #field_idents2.serialized_size(offset)?;)*
}
})
}
Fields::Unnamed(fields_unnamed) => {
let field_idents =
(0..fields_unnamed.unnamed.len()).map(|i| format_ident!("self_{i}"));
let field_idents2 = field_idents.clone();
Some(quote! {
Self::#ident(#(#field_idents),*) => {
#(offset += #field_idents2.serialized_size(offset)?;)*
}
})
}
Fields::Unit => None,
},
);
let variant_idents = data_enum.variants.iter().map(|variant| &variant.ident);
let verify_arms = data_enum
.variants
.iter()
.map(|Variant { ident, fields, .. }| match fields {
Fields::Named(fields_named) => enum_verify_arm(
ident,
&fields_named.named,
fields_named.named.iter().map(|field| &field.ident),
),
Fields::Unnamed(fields_unnamed) => enum_verify_arm(
ident,
&fields_unnamed.unnamed,
(0..fields_unnamed.unnamed.len()).map(Index::from),
),
Fields::Unit => quote! { #ident => () },
});
generate_where_clause(&crate_path, &mut generics, bounded_fields.clone(), &derives);
let mut implementation = impl_serialize(
&crate_path,
&ident,
&generics,
&archived,
max_alignment(&crate_path, types),
proc_macro2::TokenStream::new(),
quote! {
fn serialize(
&self,
out: *mut ::core::mem::MaybeUninit<Self::Archived>,
mut heap: *mut ::core::mem::MaybeUninit<u8>,
) -> usize {
let heap_start = heap;
unsafe {
match self {
#(#serialize_arms,)*
_ => ()
}
heap.offset_from_unsigned(heap_start)
}
}
},
quote! {
fn serialized_size(
&self,
mut offset: usize
) -> Result<usize, #crate_path::SerializeError> {
let offset_start = offset;
match self {
#(#size_arms,)*
_ => ()
}
Ok(offset - offset_start)
}
},
quote! {
fn verify(
this: *const Self::Archived,
buffer_end: *const u8
) -> Result<(), #crate_path::VerifyError> {
unsafe {
let tag = *this.cast::<u8>();
match tag {
#(#verify_arms,)*
_ => return Err(
#crate_path::VerifyError::InvalidEnumDiscriminantError {
enum_name: ::core::stringify!(Self),
invalid_discriminant: tag,
}
)
}
}
Ok(())
}
},
);
impl_compare(
&mut implementation,
&crate_path,
compares,
&mut generics,
bounded_fields,
&ident,
|| {
let variant_impls = data_enum.variants.iter().map(|v| {
let variant = &v.ident;
let (self_fields, other_fields) = v
.fields
.iter()
.enumerate()
.map(|(i, f)| {
(
Ident::new(&format!("self_{i}"), f.span()),
Ident::new(&format!("other_{i}"), f.span()),
)
})
.unzip::<_, _, Vec<_>, Vec<_>>();
match &v.fields {
Fields::Named(fields) => {
let field_names =
fields.named.iter().map(|f| &f.ident).collect::<Vec<_>>();
quote! {
#ident::#variant {
#(#field_names: #self_fields),*
} => match other {
#archived_ident::#variant {
#(#field_names: #other_fields),*
} => true #(&& #other_fields.eq(#self_fields))*,
#[allow(unreachable_patterns)]
_ => false,
}
}
}
Fields::Unnamed(_) => {
quote! {
#ident::#variant(#(#self_fields),*) => match other {
#archived_ident::#variant(#(#other_fields),*) => {
true #(&& #other_fields.eq(#self_fields))*
}
#[allow(unreachable_patterns)]
_ => false,
}
}
}
Fields::Unit => quote! {
#ident::#variant => match other {
#archived_ident::#variant => true,
#[allow(unreachable_patterns)]
_ => false,
}
},
}
});
quote! { match self { #(#variant_impls)* } }
},
|| {
let self_disc = data_enum.variants.iter().map(|v| {
let variant = &v.ident;
match v.fields {
Fields::Named(_) => quote! {
#ident::#variant { .. } => ArchivedTag::#variant
},
Fields::Unnamed(_) => quote! {
#ident::#variant ( .. ) => ArchivedTag::#variant
},
Fields::Unit => quote! {
#ident::#variant => ArchivedTag::#variant
},
}
});
let other_disc = data_enum.variants.iter().map(|v| {
let variant = &v.ident;
match v.fields {
Fields::Named(_) => quote! {
#archived_ident::#variant { .. } => ArchivedTag::#variant
},
Fields::Unnamed(_) => quote! {
#archived_ident::#variant ( .. ) => ArchivedTag::#variant
},
Fields::Unit => quote! {
#archived_ident::#variant => ArchivedTag::#variant
},
}
});
let variant_impls = data_enum.variants.iter().map(|v| {
let variant = &v.ident;
let (self_fields, other_fields) = v
.fields
.iter()
.enumerate()
.map(|(i, f)| {
(
Ident::new(&format!("self_{i}"), f.span()),
Ident::new(&format!("other_{i}"), f.span()),
)
})
.unzip::<_, _, Vec<_>, Vec<_>>();
match &v.fields {
Fields::Named(fields) => {
let field_names =
fields.named.iter().map(|f| &f.ident).collect::<Vec<_>>();
quote! {
(
#ident::#variant {
#(#field_names: #self_fields),*
},
#archived_ident::#variant {
#(#field_names: #other_fields),*
}
) => {
#(
match #other_fields.partial_cmp(#self_fields) {
Some(::core::cmp::Ordering::Equal) => (),
cmp => return cmp.map(
::core::cmp::Ordering::reverse
),
}
)*
Some(::core::cmp::Ordering::Equal)
}
}
}
Fields::Unnamed(_) => {
quote! {
(
#ident::#variant(#(#self_fields),*),
#archived_ident::#variant(#(#other_fields),*)
) => {
#(
match #other_fields.partial_cmp(#self_fields) {
Some(::core::cmp::Ordering::Equal) => (),
cmp => return cmp.map(
::core::cmp::Ordering::reverse
),
}
)*
Some(::core::cmp::Ordering::Equal)
}
}
}
Fields::Unit => quote! {
(#ident::#variant, #archived_ident::#variant) => {
Some(::core::cmp::Ordering::Equal)
}
},
}
});
quote! {
match (self, other) {
#(#variant_impls,)*
_ => match self { #(#self_disc),* }
.partial_cmp(&match other { #(#other_disc),* })
}
}
},
);
let mut implementation = quote! {
#[allow(non_upper_case_globals)]
const _: () = {
#[derive(PartialEq, PartialOrd)]
#[repr(u8)]
enum ArchivedTag {
#(#archived_variant_tags),*
}
#(const #variant_idents: u8 = ArchivedTag::#variant_idents as u8;)*
#implementation
};
};
if as_type.is_none() {
let archived_variants = data_enum.variants.iter().map(
|Variant {
ident: variant_ident,
fields,
discriminant,
..
}| {
let variant_doc =
format!("The archived counterpart of [`{ident}::{variant_ident}`]");
let (eq, expr) = if let Some((eq, expr)) = discriminant {
(Some(eq), Some(expr))
} else {
(None, None)
};
match fields {
Fields::Named(fields_named) => {
let variant_fields = fields_named.named.iter().map(
|Field {
vis,
ident,
colon_token,
ty,
..
}| {
quote! {
#vis #ident #colon_token
<#ty as #crate_path::Serialize>::Archived
}
},
);
quote! {
#[doc = #variant_doc]
#[allow(dead_code)]
#variant_ident { #(#variant_fields),* } #eq #expr
}
}
Fields::Unnamed(fields_unnamed) => {
let visibilities =
fields_unnamed.unnamed.iter().map(|field| &field.vis);
let types = fields_unnamed.unnamed.iter().map(|field| &field.ty);
quote! {
#[doc = #variant_doc]
#[allow(dead_code)]
#variant_ident(
#(#visibilities
<#types as #crate_path::Serialize>::Archived),*
) #eq #expr
}
}
Fields::Unit => quote! {
#[doc = #variant_doc]
#[allow(dead_code)]
#variant_ident #eq #expr
},
}
},
);
let where_clause = &generics.where_clause;
implementation.extend(quote! {
#[automatically_derived]
#[doc = #doc_string]
#[repr(u8)]
#(#[#struct_attrs])*
#vis enum #archived_ident #generics #where_clause {
#(#archived_variants),*
}
});
}
implementation
}
Data::Union(_) => {
return syn::Error::new(Span::call_site(), "Serialize cannot be derived for unions")
.to_compile_error()
.into();
}
}
.into()
}