Implement serialization for untagged unnamed variants
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use syn::{Attribute, Error, Field, Fields, Lit, Meta, NestedMeta, Result, Variant, DataEnum};
|
||||
use crate::TagType;
|
||||
use syn::{Attribute, DataEnum, Error, Field, Fields, Lit, Meta, NestedMeta, Result, Variant};
|
||||
|
||||
pub(crate) fn tag_type(attrs: &[Attribute], enumeration: &DataEnum) -> Result<TagType> {
|
||||
let mut tag_type = None;
|
||||
|
||||
17
src/lib.rs
17
src/lib.rs
@@ -1,12 +1,10 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
mod ser;
|
||||
pub(crate) mod attr;
|
||||
mod ser;
|
||||
|
||||
use syn::{
|
||||
parse_macro_input, DeriveInput, Error, Data,
|
||||
};
|
||||
use std::convert::From;
|
||||
use syn::{parse_macro_input, Data, DeriveInput, Error};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TagType {
|
||||
@@ -20,8 +18,13 @@ pub fn derive_serialize(tokens: proc_macro::TokenStream) -> proc_macro::TokenStr
|
||||
let input = parse_macro_input!(tokens as DeriveInput);
|
||||
let en = match &input.data {
|
||||
Data::Enum(en) => en,
|
||||
_ => return Error::new_spanned(input, "Serialize_enum can only be applied to enums")
|
||||
.to_compile_error().into()
|
||||
_ => {
|
||||
return Error::new_spanned(input, "Serialize_enum can only be applied to enums")
|
||||
.to_compile_error()
|
||||
.into()
|
||||
}
|
||||
};
|
||||
ser::derive(&input, &en).unwrap_or_else(|err| err.to_compile_error()).into()
|
||||
ser::derive(&input, &en)
|
||||
.unwrap_or_else(|err| err.to_compile_error())
|
||||
.into()
|
||||
}
|
||||
|
||||
163
src/ser.rs
163
src/ser.rs
@@ -1,10 +1,8 @@
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
use syn::{
|
||||
DeriveInput, Result, DataEnum, Error, Fields, FieldsNamed,
|
||||
};
|
||||
use quote::quote;
|
||||
use crate::TagType;
|
||||
use crate::attr;
|
||||
use crate::TagType;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{DataEnum, DeriveInput, Ident, Error, Fields, FieldsNamed, FieldsUnnamed, Result};
|
||||
|
||||
pub fn derive(input: &DeriveInput, enumeration: &DataEnum) -> Result<TokenStream> {
|
||||
if input.generics.lt_token.is_some() || input.generics.where_clause.is_some() {
|
||||
@@ -15,11 +13,13 @@ pub fn derive(input: &DeriveInput, enumeration: &DataEnum) -> Result<TokenStream
|
||||
}
|
||||
let ident = &input.ident;
|
||||
let tag_type = attr::tag_type(&input.attrs, &enumeration)?;
|
||||
let names = enumeration.variants
|
||||
let names = enumeration
|
||||
.variants
|
||||
.iter()
|
||||
.map(attr::name_of_variant)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let begin = enumeration.variants
|
||||
let begin = enumeration
|
||||
.variants
|
||||
.iter()
|
||||
.zip(names.iter())
|
||||
.map(|(variant, name)| {
|
||||
@@ -33,18 +33,32 @@ pub fn derive(input: &DeriveInput, enumeration: &DataEnum) -> Result<TokenStream
|
||||
}
|
||||
Fields::Named(fields) => {
|
||||
let implementation = serialize_named(&fields, name, &tag_type)?;
|
||||
let field_ident = fields.named.iter().map(|field| &field.ident).collect::<Vec<_>>();
|
||||
let field_ident = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| &field.ident)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote!{
|
||||
#ident::#var_ident{#(#field_ident),*} => {
|
||||
#implementation
|
||||
}
|
||||
}
|
||||
},
|
||||
Fields::Unnamed(fields) => quote!{_ => unimplemented!(),},
|
||||
}
|
||||
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(fields, &field_ident, name, &tag_type)?;
|
||||
quote!{
|
||||
#ident::#var_ident(#(#field_ident),*) => {
|
||||
#implementation
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
}).collect::<Result<Vec<_>>>()?;
|
||||
|
||||
Ok(quote!{
|
||||
const _: () = {
|
||||
@@ -87,10 +101,26 @@ fn serialize_unit(variant_name: &str, tag_type: &TagType) -> Result<TokenStream>
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_named(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<_>>();
|
||||
fn serialize_named(
|
||||
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<_>>();
|
||||
Ok(if let TagType::External = tag_type {
|
||||
quote!{
|
||||
use miniserde::Serialize;
|
||||
@@ -125,12 +155,15 @@ fn serialize_named(fields: &FieldsNamed, variant_name: &str, tag_type: &TagType)
|
||||
}
|
||||
} else {
|
||||
let (start, tag_arm) = if let TagType::Internal(ref tag) = &tag_type {
|
||||
(0usize, quote!{
|
||||
0 => miniserde::export::Some((
|
||||
miniserde::export::Cow::Borrowed(#tag),
|
||||
&#variant_name,
|
||||
)),
|
||||
})
|
||||
(
|
||||
0usize,
|
||||
quote!{
|
||||
0 => miniserde::export::Some((
|
||||
miniserde::export::Cow::Borrowed(#tag),
|
||||
&#variant_name,
|
||||
)),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(1, quote!())
|
||||
};
|
||||
@@ -165,3 +198,87 @@ fn serialize_named(fields: &FieldsNamed, variant_name: &str, tag_type: &TagType)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_unnamed(
|
||||
fields: &FieldsUnnamed,
|
||||
field_ident: &Vec<Ident>,
|
||||
variant_name: &str,
|
||||
tag_type: &TagType,
|
||||
) -> Result<TokenStream> {
|
||||
let field_type = fields
|
||||
.unnamed
|
||||
.iter()
|
||||
.map(|field| &field.ty)
|
||||
.collect::<Vec<_>>();
|
||||
Ok(if let TagType::External = tag_type {
|
||||
quote!{
|
||||
use miniserde::Serialize;
|
||||
#[derive(Serialize)]
|
||||
struct __AsStruct<'__b> (#(&'__b #field_type),*);
|
||||
|
||||
struct __SuperMap<'__b> {
|
||||
data: __AsStruct<'__b>,
|
||||
state: miniserde::export::usize,
|
||||
}
|
||||
|
||||
impl<'__a> miniserde::ser::Map for __SuperMap<'__a> {
|
||||
fn next(&mut self) -> miniserde::export::Option<(miniserde::export::Cow<miniserde::export::str>, &dyn miniserde::Serialize)> {
|
||||
let __state = self.state;
|
||||
self.state = __state + 1;
|
||||
match __state {
|
||||
0 => miniserde::export::Some((
|
||||
miniserde::export::Cow::Borrowed(#variant_name),
|
||||
&self.data,
|
||||
)),
|
||||
_ => miniserde::export::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
miniserde::ser::Fragment::Map(miniserde::export::Box::new(__SuperMap {
|
||||
data: __AsStruct { #(#field_ident),* },
|
||||
state: 0,
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
let (start, tag_arm) = if let TagType::Internal(ref tag) = &tag_type {
|
||||
(
|
||||
0usize,
|
||||
quote!{
|
||||
0 => miniserde::export::Some((
|
||||
miniserde::export::Cow::Borrowed(#tag),
|
||||
&#variant_name,
|
||||
)),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(1, quote!())
|
||||
};
|
||||
let index = 1usize..;
|
||||
quote!{
|
||||
struct __Seq<'__a> {
|
||||
#(#field_ident: &'__a #field_type),*,
|
||||
state: miniserde::export::usize,
|
||||
}
|
||||
|
||||
impl<'__a> miniserde::ser::Seq for __Seq<'__a> {
|
||||
fn next(&mut self) -> miniserde::export::Option<&dyn miniserde::Serialize> {
|
||||
let __state = self.state;
|
||||
self.state = __state + 1;
|
||||
match __state {
|
||||
#tag_arm
|
||||
#(#index => {
|
||||
miniserde::export::Some(self.#field_ident)
|
||||
})*,
|
||||
_ => miniserde::export::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
miniserde::ser::Fragment::Seq(miniserde::export::Box::new(__Seq {
|
||||
#(#field_ident),*,
|
||||
state: #start,
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ fn test_internal() {
|
||||
A,
|
||||
#[serde(rename = "renamedB")]
|
||||
B,
|
||||
C{x: i32},
|
||||
C {
|
||||
x: i32,
|
||||
},
|
||||
}
|
||||
use Internal::*;
|
||||
let example = [A, B, C{x: 2}];
|
||||
let example = [A, B, C { x: 2 }];
|
||||
let actual = json::to_string(&example[..]);
|
||||
let expected = r#"[{"type":"A"},{"type":"renamedB"},{"type":"C","x":2}]"#;
|
||||
assert_eq!(actual, expected);
|
||||
@@ -25,10 +27,12 @@ fn test_external() {
|
||||
A,
|
||||
#[serde(rename = "renamedB")]
|
||||
B,
|
||||
C{x: i32},
|
||||
C {
|
||||
x: i32,
|
||||
},
|
||||
}
|
||||
use External::*;
|
||||
let example = [A, B, C{x: 2}];
|
||||
let example = [A, B, C { x: 2 }];
|
||||
let actual = json::to_string(&example[..]);
|
||||
let expected = r#"["A","renamedB",{"C":{"x":2}}]"#;
|
||||
assert_eq!(actual, expected);
|
||||
@@ -41,12 +45,14 @@ fn test_untagged() {
|
||||
enum Untagged {
|
||||
A,
|
||||
#[serde(rename = "renamedB")]
|
||||
B,
|
||||
C{x: i32},
|
||||
B(i32, String),
|
||||
C {
|
||||
x: i32,
|
||||
},
|
||||
}
|
||||
use Untagged::*;
|
||||
let example = [A, B, C{x: 2}];
|
||||
let example = [A, B(42, "everything".to_string()), C { x: 2 }];
|
||||
let actual = json::to_string(&example[..]);
|
||||
let expected = r#"["A","renamedB",{"x":2}]"#;
|
||||
let expected = r#"["A",[42,"everything"],{"x":2}]"#;
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user