Add support for lifetimes

This commit is contained in:
Sosthène Guédon
2023-11-24 13:55:53 +01:00
committed by sosthene-nitrokey
parent dd33ec9572
commit 056374115f
2 changed files with 48 additions and 8 deletions
+35 -7
View File
@@ -30,6 +30,8 @@ extern crate proc_macro;
mod parse;
use std::iter;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::parse_macro_input;
@@ -85,9 +87,10 @@ pub fn derive_serialize(input: TokenStream) -> TokenStream {
let ident = input.ident;
let num_fields = count_serialized_fields(&input.fields);
let serialize_fields = serialize_fields(&input.fields, input.attrs.offset);
let (_, lifetimes) = lifetimes(&input.lifetimes);
TokenStream::from(quote! {
impl serde::Serialize for #ident {
impl<#(#lifetimes),*> serde::Serialize for #ident<#(#lifetimes),*> {
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: serde::Serializer
@@ -167,6 +170,21 @@ fn all_fields(fields: &[parse::Field]) -> Vec<proc_macro2::TokenStream> {
.collect()
}
fn lifetimes(
lifetimes: &[syn::Lifetime],
) -> (proc_macro2::TokenStream, Vec<proc_macro2::TokenStream>) {
let lifetimes: Vec<_> = lifetimes
.into_iter()
.map(|l| {
quote! {#l}
})
.collect();
let de_lifetime = quote! {
'de: #(#lifetimes)+*
};
(de_lifetime, lifetimes)
}
#[proc_macro_derive(DeserializeIndexed, attributes(serde, serde_indexed))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as Input);
@@ -175,6 +193,9 @@ pub fn derive_deserialize(input: TokenStream) -> TokenStream {
let unwrap_expected_fields = unwrap_expected_fields(&input.fields);
let match_fields = match_fields(&input.fields, input.attrs.offset);
let all_fields = all_fields(&input.fields);
let (de_lifetime, lifetimes) = lifetimes(&input.lifetimes);
let impl_lifetimes = iter::once(&de_lifetime).chain(&lifetimes);
let impl_lifetimes2 = impl_lifetimes.clone();
let the_loop = if !input.fields.is_empty() {
// NB: In the previous "none_fields", we use the actual struct's
@@ -195,22 +216,29 @@ pub fn derive_deserialize(input: TokenStream) -> TokenStream {
quote! {}
};
let phantom_datas_ty = lifetimes
.iter()
.map(|l| quote!(core::marker::PhantomData<&#l ()>));
let phantom_datas_values = lifetimes
.iter()
.map(|_l| quote!(core::marker::PhantomData::default()));
TokenStream::from(quote! {
impl<'de> serde::Deserialize<'de> for #ident {
impl<#(#impl_lifetimes),*> serde::Deserialize<'de> for #ident<#(#lifetimes),*> {
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct IndexedVisitor;
struct IndexedVisitor<#(#lifetimes),*>(#(#phantom_datas_ty),*);
impl<'de> serde::de::Visitor<'de> for IndexedVisitor {
type Value = #ident;
impl<#(#impl_lifetimes2),*> serde::de::Visitor<'de> for IndexedVisitor<#(#lifetimes),*> {
type Value = #ident<#(#lifetimes),*>;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str(stringify!(#ident))
}
fn visit_map<V>(self, mut map: V) -> core::result::Result<#ident, V::Error>
fn visit_map<V>(self, mut map: V) -> core::result::Result<Self::Value, V::Error>
where
V: serde::de::MapAccess<'de>,
{
@@ -224,7 +252,7 @@ pub fn derive_deserialize(input: TokenStream) -> TokenStream {
}
}
deserializer.deserialize_map(IndexedVisitor {})
deserializer.deserialize_map(IndexedVisitor(#(#phantom_datas_values),*))
}
}
})
+13 -1
View File
@@ -1,11 +1,12 @@
use proc_macro2::Span;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{Data, DeriveInput, Fields, Ident, Token};
use syn::{Data, DeriveInput, Fields, Ident, Lifetime, Token};
pub struct Input {
pub ident: Ident,
pub attrs: StructAttrs,
pub fields: Vec<Field>,
pub lifetimes: Vec<Lifetime>,
}
#[derive(Default)]
@@ -68,6 +69,14 @@ fn parse_attrs(attrs: &Vec<syn::Attribute>) -> Result<StructAttrs> {
Ok(struct_attrs)
}
fn lifetimes(generics: &syn::Generics) -> Vec<Lifetime> {
generics
.lifetimes()
.into_iter()
.map(|l| l.lifetime.clone())
.collect()
}
impl Parse for Input {
fn parse(input: ParseStream) -> Result<Self> {
let call_site = Span::call_site();
@@ -91,12 +100,15 @@ impl Parse for Input {
let fields = fields_from_ast(&syn_fields.named);
let lifetimes = lifetimes(&derive_input.generics);
//serde::internals::ast calls `fields_from_ast(cx, &fields.named, attrs, container_default)`
Ok(Input {
ident: derive_input.ident,
attrs,
fields,
lifetimes,
})
}
}