283 lines
10 KiB
Rust
283 lines
10 KiB
Rust
use crate::attr;
|
|
use crate::bound;
|
|
use crate::TagType;
|
|
use proc_macro2::{Span, TokenStream};
|
|
use quote::quote;
|
|
use syn::{parse_quote, DataEnum, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Ident, Result};
|
|
|
|
pub fn derive(input: &DeriveInput, enumeration: &DataEnum) -> Result<TokenStream> {
|
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
|
let ident = &input.ident;
|
|
let tag_type = attr::tag_type(&input.attrs, enumeration)?;
|
|
let names = enumeration
|
|
.variants
|
|
.iter()
|
|
.map(attr::name_of_variant)
|
|
.collect::<Result<Vec<_>>>()?;
|
|
let begin = enumeration
|
|
.variants
|
|
.iter()
|
|
.zip(names.iter())
|
|
.map(|(variant, name)| {
|
|
let var_ident = &variant.ident;
|
|
Ok(match &variant.fields {
|
|
Fields::Unit => {
|
|
let implementation = serialize_unit(name, &tag_type)?;
|
|
quote! {
|
|
#ident::#var_ident => {#implementation}
|
|
}
|
|
}
|
|
Fields::Named(fields) => {
|
|
let implementation = serialize_named(input, fields, name, &tag_type)?;
|
|
let field_ident = fields
|
|
.named
|
|
.iter()
|
|
.map(|field| &field.ident)
|
|
.collect::<Vec<_>>();
|
|
|
|
quote! {
|
|
#ident::#var_ident{#(#field_ident),*} => {
|
|
#implementation
|
|
}
|
|
}
|
|
}
|
|
Fields::Unnamed(fields) => {
|
|
let field_ident = (0..fields.unnamed.len())
|
|
.map(|i| format!("f{}", i))
|
|
.map(|id| Ident::new(&id, Span::call_site()))
|
|
.collect::<Vec<_>>();
|
|
let implementation =
|
|
serialize_unnamed(input, fields, &field_ident, name, &tag_type)?;
|
|
quote! {
|
|
#ident::#var_ident(#(#field_ident),*) => {
|
|
#implementation
|
|
}
|
|
}
|
|
}
|
|
})
|
|
})
|
|
.collect::<Result<Vec<_>>>()?;
|
|
|
|
Ok(quote! {
|
|
const _: () = {
|
|
impl #impl_generics miniserde::Serialize for #ident #ty_generics #where_clause {
|
|
fn begin(&self) -> miniserde::ser::Fragment {
|
|
match self {
|
|
#(#begin)*
|
|
}
|
|
}
|
|
}
|
|
};
|
|
})
|
|
}
|
|
|
|
fn serialize_unit(variant_name: &str, tag_type: &TagType) -> Result<TokenStream> {
|
|
Ok(if let TagType::Internal(tag) = &tag_type {
|
|
quote! {
|
|
struct __Map {
|
|
state: usize,
|
|
}
|
|
|
|
impl miniserde::ser::Map for __Map {
|
|
fn next(&mut self) -> std::prelude::v1::Option<(std::borrow::Cow<core::primitive::str>, &dyn miniserde::Serialize)> {
|
|
let __state = self.state;
|
|
self.state = __state + 1;
|
|
match __state {
|
|
0 => std::prelude::v1::Some((
|
|
std::borrow::Cow::Borrowed(#tag),
|
|
&#variant_name,
|
|
)),
|
|
_ => std::prelude::v1::None,
|
|
}
|
|
}
|
|
}
|
|
|
|
miniserde::ser::Fragment::Map(std::prelude::v1::Box::new(__Map {state: 0}))
|
|
}
|
|
} else {
|
|
quote! {miniserde::ser::Fragment::Str(std::borrow::Cow::Borrowed(#variant_name))}
|
|
})
|
|
}
|
|
|
|
fn serialize_named(
|
|
input: &DeriveInput,
|
|
fields: &FieldsNamed,
|
|
variant_name: &str,
|
|
tag_type: &TagType,
|
|
) -> Result<TokenStream> {
|
|
let field_ident = fields
|
|
.named
|
|
.iter()
|
|
.map(|field| &field.ident)
|
|
.collect::<Vec<_>>();
|
|
let field_name = fields
|
|
.named
|
|
.iter()
|
|
.map(attr::name_of_field)
|
|
.collect::<Result<Vec<_>>>()?;
|
|
let field_type = fields
|
|
.named
|
|
.iter()
|
|
.map(|field| &field.ty)
|
|
.collect::<Vec<_>>();
|
|
let (_, _, where_clause) = input.generics.split_for_impl();
|
|
let wrapper_generics = bound::with_lifetime_bound(&input.generics, "'__b");
|
|
let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
|
|
let bound = parse_quote!(miniserde::Serialize);
|
|
let bounded_where_clause = bound::where_clause_with_bound(&input.generics, bound);
|
|
let cow = quote!(std::borrow::Cow);
|
|
let some = quote!(std::prelude::v1::Some);
|
|
if let TagType::External = tag_type {
|
|
Ok(quote! {
|
|
use miniserde::Serialize;
|
|
#[derive(Serialize)]
|
|
struct __AsStruct #wrapper_impl_generics #where_clause {
|
|
#(#field_ident: &'__b #field_type,)*
|
|
}
|
|
|
|
struct __SuperMap #wrapper_impl_generics #where_clause {
|
|
data: __AsStruct #wrapper_ty_generics,
|
|
state: usize,
|
|
}
|
|
|
|
impl #wrapper_impl_generics miniserde::ser::Map for __SuperMap #wrapper_ty_generics #bounded_where_clause {
|
|
fn next(&mut self) -> std::prelude::v1::Option<(#cow<core::primitive::str>, &dyn miniserde::Serialize)> {
|
|
let __state = self.state;
|
|
self.state = __state + 1;
|
|
match __state {
|
|
0 => #some((#cow::Borrowed(#variant_name), &self.data)),
|
|
_ => std::prelude::v1::None,
|
|
}
|
|
}
|
|
}
|
|
|
|
miniserde::ser::Fragment::Map(std::prelude::v1::Box::new(__SuperMap {
|
|
data: __AsStruct { #(#field_ident),* },
|
|
state: 0,
|
|
}))
|
|
})
|
|
} else {
|
|
let (start, tag_arm) = if let TagType::Internal(ref tag) = &tag_type {
|
|
(
|
|
0,
|
|
quote! {0 => #some((#cow::Borrowed(#tag), &#variant_name)),},
|
|
)
|
|
} else {
|
|
(1usize, quote!())
|
|
};
|
|
let index = 1usize..;
|
|
Ok(quote! {
|
|
struct __Map #wrapper_impl_generics {
|
|
#(#field_ident: &'__b #field_type),*,
|
|
state: usize,
|
|
}
|
|
|
|
impl #wrapper_impl_generics miniserde::ser::Map for __Map #wrapper_ty_generics #where_clause {
|
|
fn next(&mut self) -> std::prelude::v1::Option<(#cow<core::primitive::str>, &dyn miniserde::Serialize)> {
|
|
let __state = self.state;
|
|
self.state = __state + 1;
|
|
match __state {
|
|
#tag_arm
|
|
#(#index => {
|
|
#some((
|
|
#cow::Borrowed(#field_name),
|
|
self.#field_ident,
|
|
))
|
|
})*,
|
|
_ => std::prelude::v1::None,
|
|
}
|
|
}
|
|
}
|
|
|
|
miniserde::ser::Fragment::Map(std::prelude::v1::Box::new(__Map {
|
|
#(#field_ident),*,
|
|
state: #start,
|
|
}))
|
|
})
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::cognitive_complexity)]
|
|
fn serialize_unnamed(
|
|
input: &DeriveInput,
|
|
fields: &FieldsUnnamed,
|
|
field_ident: &[Ident],
|
|
variant_name: &str,
|
|
tag_type: &TagType,
|
|
) -> Result<TokenStream> {
|
|
let field_type = fields
|
|
.unnamed
|
|
.iter()
|
|
.map(|field| &field.ty)
|
|
.collect::<Vec<_>>();
|
|
let (_, _, where_clause) = input.generics.split_for_impl();
|
|
let wrapper_generics = bound::with_lifetime_bound(&input.generics, "'__b");
|
|
let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
|
|
let bound = parse_quote!(miniserde::Serialize);
|
|
let bounded_where_clause = bound::where_clause_with_bound(&input.generics, bound);
|
|
let index = 0usize..;
|
|
let ex = quote!(std::prelude::v1);
|
|
let seq = if field_ident.len() == 1 {
|
|
quote! { #(#field_ident.begin())* }
|
|
} else {
|
|
quote! {
|
|
struct __Seq #wrapper_impl_generics #where_clause {
|
|
#(#field_ident: &'__b #field_type),*,
|
|
state: usize,
|
|
}
|
|
|
|
impl #wrapper_impl_generics miniserde::ser::Seq for __Seq #wrapper_ty_generics #bounded_where_clause {
|
|
fn next(&mut self) -> #ex::Option<&dyn miniserde::Serialize> {
|
|
let __state = self.state;
|
|
self.state = __state + 1;
|
|
match __state {
|
|
#(#index => #ex::Some(self.#field_ident)),*,
|
|
_ => #ex::None,
|
|
}
|
|
}
|
|
}
|
|
|
|
miniserde::ser::Fragment::Seq(#ex::Box::new(__Seq {
|
|
#(#field_ident),*,
|
|
state: 0,
|
|
}))
|
|
}
|
|
};
|
|
Ok(if let TagType::External = tag_type {
|
|
quote! {
|
|
struct __AsStruct #wrapper_impl_generics (#(&'__b #field_type),*) #where_clause;
|
|
|
|
impl #wrapper_impl_generics miniserde::Serialize for __AsStruct #wrapper_ty_generics #bounded_where_clause {
|
|
fn begin(&self) -> miniserde::ser::Fragment {
|
|
let __AsStruct(#(#field_ident),*) = self;
|
|
#seq
|
|
}
|
|
}
|
|
|
|
struct __SuperMap #wrapper_impl_generics #where_clause {
|
|
data: __AsStruct #wrapper_ty_generics,
|
|
state: bool,
|
|
}
|
|
|
|
impl #wrapper_impl_generics miniserde::ser::Map for __SuperMap #wrapper_ty_generics #bounded_where_clause {
|
|
fn next(&mut self) -> std::prelude::v1::Option<(std::borrow::Cow<core::primitive::str>, &dyn miniserde::Serialize)> {
|
|
if self.state {
|
|
return std::prelude::v1::None;
|
|
}
|
|
self.state = true;
|
|
#ex::Some((std::borrow::Cow::Borrowed(#variant_name), &self.data))
|
|
}
|
|
}
|
|
|
|
miniserde::ser::Fragment::Map(#ex::Box::new(__SuperMap {
|
|
data: __AsStruct ( #(#field_ident),* ),
|
|
state: false,
|
|
}))
|
|
}
|
|
} else {
|
|
quote! {
|
|
#seq
|
|
}
|
|
})
|
|
}
|