Skip to content

Commit

Permalink
Add support for borrowed data in proc macro
Browse files Browse the repository at this point in the history
  • Loading branch information
melody-rs committed May 31, 2024
1 parent e0354db commit 901144d
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 119 deletions.
2 changes: 1 addition & 1 deletion alox-48-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ proc-macro = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
syn = "2.0"
syn = { version = "2.0", features = ["printing", "derive"] }
quote = "1.0"

darling = "0.20"
Expand Down
121 changes: 56 additions & 65 deletions alox-48-derive/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,65 +4,13 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use darling::{
util::{Flag, Override},
FromDeriveInput,
};
use darling::FromDeriveInput;
use itertools::Itertools;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{spanned::Spanned, Ident, LitStr, Path, Type};

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(marshal))]
#[darling(supports(struct_any, enum_any))]
struct TypeReciever {
ident: Ident,
data: darling::ast::Data<VariantReciever, FieldReciever>,

alox_crate_path: Option<Path>,

class: Option<String>,
deny_unknown_fields: Flag,
enforce_class: Flag,
#[darling(rename = "default")]
default_fn: Option<Override<Path>>,
#[darling(rename = "from")]
from_type: Option<Type>,
#[darling(rename = "try_from")]
try_from_type: Option<Type>,
expecting: Option<String>,
}

#[derive(Debug, darling::FromField)]
#[darling(attributes(marshal))]
struct FieldReciever {
ident: Option<Ident>,
ty: Type,

rename: Option<LitStr>,

#[darling(rename = "default")]
default_fn: Option<Override<Path>>,

skip: Flag,
skip_deserializing: Flag,
use syn::{spanned::Spanned, Ident, LitStr};

#[darling(rename = "deserialize_with")]
deserialize_with_fn: Option<Path>,
#[darling(rename = "with")]
with_module: Option<Path>,
}

#[allow(dead_code)]
#[derive(Debug, darling::FromVariant)]
struct VariantReciever {
ident: Ident,
fields: darling::ast::Fields<FieldReciever>,

transparent: Flag,
class: Option<String>,
}
use super::{FieldReciever, TypeReciever, VariantReciever};

pub fn derive_inner(input: &syn::DeriveInput) -> TokenStream {
let reciever = match TypeReciever::from_derive_input(input) {
Expand Down Expand Up @@ -96,6 +44,16 @@ pub fn derive_inner(input: &syn::DeriveInput) -> TokenStream {
}

fn parse_reciever(reciever: &TypeReciever) -> TokenStream {
if reciever
.generics
.lifetimes()
.any(|l| l.lifetime.ident == "de")
{
return quote! {
compile_error!("Cannot use 'de as a lifetime in the Deserialize derive macro")
};
}

let ty = &reciever.ident;

if reciever.try_from_type.is_some() && reciever.from_type.is_some() {
Expand Down Expand Up @@ -136,6 +94,7 @@ fn parse_reciever(reciever: &TypeReciever) -> TokenStream {
}
}

#[allow(clippy::too_many_lines)]
fn parse_struct(
reciever: &TypeReciever,
fields: &darling::ast::Fields<FieldReciever>,
Expand All @@ -152,6 +111,19 @@ fn parse_struct(
}

let ty = reciever.ident.clone();
let ty_lifetimes = reciever.generics.lifetimes().map(|l| &l.lifetime);
let ty_lifetimes = quote! { <#( #ty_lifetimes ),*> };
let visitor_lifetimes = reciever.generics.lifetimes().map(|l| &l.lifetime);
let visitor_lifetimes = quote! { <'de, #( #visitor_lifetimes ),*> };

let lifetimes_iter = reciever.generics.lifetimes().map(|l| &l.lifetime);
let de_lifetime = quote! { 'de: #( #lifetimes_iter )+* };
// i have no idea why we need to specify this here but rust gets *really* unhappy if we don't
let lifetimes_iter = reciever.generics.lifetimes().cloned().map(|mut l| {
l.bounds.push(syn::Lifetime::new("'de", l.span()));
l
});
let impl_lifetimes = quote! { <#de_lifetime, #( #lifetimes_iter ),*> };

let (field_const, field_lets, field_match, instantiate_fields): ParseUnpack = fields
.iter()
Expand Down Expand Up @@ -195,7 +167,7 @@ fn parse_struct(

quote! {
#[automatically_derived]
impl<'de> Deserialize<'de> for #ty {
impl #impl_lifetimes Deserialize<'de> for #ty #ty_lifetimes {
fn deserialize<D>(deserializer: D) -> Result<Self, DeError>
where
D: DeserializerTrait<'de>
Expand All @@ -204,10 +176,13 @@ fn parse_struct(
#( #field_const ),*
];

struct __Visitor;
struct __Visitor #impl_lifetimes {
_marker: std::marker::PhantomData<#ty #ty_lifetimes >,
_phantom: std::marker::PhantomData<&'de ()>,
}

impl<'de> Visitor<'de> for __Visitor {
type Value = #ty;
impl #impl_lifetimes Visitor<'de> for __Visitor #visitor_lifetimes {
type Value = #ty #ty_lifetimes;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(#expecting_lit)
Expand Down Expand Up @@ -236,14 +211,27 @@ fn parse_struct(
}
}

deserializer.deserialize(__Visitor)
deserializer.deserialize(__Visitor { _marker: std::marker::PhantomData, _phantom: std::marker::PhantomData })
}
}
}
}

fn parse_newtype_struct(reciever: &TypeReciever) -> TokenStream {
let ty = reciever.ident.clone();
let ty_lifetimes = reciever.generics.lifetimes().map(|l| &l.lifetime);
let ty_lifetimes = quote! { <#( #ty_lifetimes ),*> };
let visitor_lifetimes = reciever.generics.lifetimes().map(|l| &l.lifetime);
let visitor_lifetimes = quote! { <'de, #( #visitor_lifetimes ),*> };

let lifetimes_iter = reciever.generics.lifetimes().map(|l| &l.lifetime);
let de_lifetime = quote! { 'de: #( #lifetimes_iter )+* };
// i have no idea why we need to specify this here but rust gets *really* unhappy if we don't
let lifetimes_iter = reciever.generics.lifetimes().cloned().map(|mut l| {
l.bounds.push(syn::Lifetime::new("'de", l.span()));
l
});
let impl_lifetimes = quote! { <#de_lifetime, #( #lifetimes_iter ),*> };

let classname = reciever.class.clone().unwrap_or_else(|| ty.to_string());
let enforce_class = if reciever.enforce_class.is_present() {
Expand All @@ -265,16 +253,19 @@ fn parse_newtype_struct(reciever: &TypeReciever) -> TokenStream {

quote! {
#[automatically_derived]
impl<'de> Deserialize<'de> for #ty {
impl #impl_lifetimes Deserialize<'de> for #ty #ty_lifetimes {
fn deserialize<D>(deserializer: D) -> Result<Self, DeError>
where
D: DeserializerTrait<'de>
{

struct __Visitor;
struct __Visitor #impl_lifetimes {
_marker: std::marker::PhantomData<#ty #ty_lifetimes >,
_phantom: std::marker::PhantomData<&'de ()>,
}

impl<'de> Visitor<'de> for __Visitor {
type Value = #ty;
impl #impl_lifetimes Visitor<'de> for __Visitor #visitor_lifetimes {
type Value = #ty #ty_lifetimes;

fn visit_user_class<D>(self, class: &'de Sym, deserializer: D) -> Result<Self::Value, DeError>
where
Expand All @@ -290,7 +281,7 @@ fn parse_newtype_struct(reciever: &TypeReciever) -> TokenStream {
}
}

deserializer.deserialize(__Visitor)
deserializer.deserialize(__Visitor { _marker: std::marker::PhantomData, _phantom: std::marker::PhantomData })
}
}
}
Expand Down
70 changes: 70 additions & 0 deletions alox-48-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,76 @@ mod de;
mod ser;
mod util;

use darling::{
util::{Flag, Override},
FromDeriveInput,
};
use syn::{Ident, LitStr, Path, Type};

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(marshal))]
#[darling(supports(struct_any, enum_any))]
struct TypeReciever {
ident: Ident,
data: darling::ast::Data<VariantReciever, FieldReciever>,

generics: syn::Generics,

alox_crate_path: Option<Path>,

class: Option<String>,

deny_unknown_fields: Flag,
enforce_class: Flag,

#[darling(rename = "default")]
default_fn: Option<Override<Path>>,
#[darling(rename = "from")]
from_type: Option<Type>,
#[darling(rename = "into")]
into_type: Option<Type>,
#[darling(rename = "try_from")]
try_from_type: Option<Type>,
#[darling(rename = "try_into")]
try_into_type: Option<Type>,

expecting: Option<String>,
}

#[derive(Debug, darling::FromField)]
#[darling(attributes(marshal))]
struct FieldReciever {
ident: Option<Ident>,
ty: Type,

rename: Option<LitStr>,

#[darling(rename = "default")]
default_fn: Option<Override<Path>>,

skip: Flag,
skip_serializing: Flag,
skip_deserializing: Flag,
byte_string: Flag,

#[darling(rename = "deserialize_with")]
deserialize_with_fn: Option<Path>,
#[darling(rename = "serialize_with")]
serialize_with_fn: Option<Path>,
#[darling(rename = "with")]
with_module: Option<Path>,
}

#[allow(dead_code)]
#[derive(Debug, darling::FromVariant)]
struct VariantReciever {
ident: Ident,
fields: darling::ast::Fields<FieldReciever>,

transparent: Flag,
class: Option<String>,
}

#[proc_macro_derive(Deserialize, attributes(marshal))]
pub fn derive_deserialize(item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as DeriveInput);
Expand Down
63 changes: 17 additions & 46 deletions alox-48-derive/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,12 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use darling::{util::Flag, FromDeriveInput};
use darling::FromDeriveInput;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{spanned::Spanned, Ident, LitInt, LitStr, Path, Type};

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(marshal))]
#[darling(supports(struct_any, enum_any))]
struct TypeReciever {
ident: Ident,
data: darling::ast::Data<VariantReciever, FieldReciever>,

alox_crate_path: Option<Path>,

class: Option<String>,
#[darling(rename = "into")]
into_type: Option<Type>,
#[darling(rename = "try_into")]
try_into_type: Option<Type>,
}

#[derive(Debug, darling::FromField)]
#[darling(attributes(marshal))]
struct FieldReciever {
ident: Option<Ident>,
ty: Type,
use syn::{spanned::Spanned, Ident, LitInt, LitStr};

rename: Option<LitStr>,

skip: Flag,
skip_serializing: Flag,

#[darling(rename = "serialize_with")]
serialize_with_fn: Option<Path>,
#[darling(rename = "with")]
with_module: Option<Path>,
}

#[allow(dead_code)]
#[derive(Debug, darling::FromVariant)]
struct VariantReciever {
ident: Ident,
fields: darling::ast::Fields<FieldReciever>,

transparent: Flag,
class: Option<String>,
}
use super::{FieldReciever, TypeReciever, VariantReciever};

pub fn derive_inner(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
let reciever = match TypeReciever::from_derive_input(input) {
Expand Down Expand Up @@ -136,6 +95,9 @@ fn parse_struct(
}

let ty = reciever.ident.clone();
let impl_lifetimes = reciever.generics.lifetimes();
let ty_lifetimes = reciever.generics.lifetimes().map(|l| &l.lifetime);

let classname = reciever.class.clone().unwrap_or_else(|| ty.to_string());

let field_impls = fields.iter().map(parse_field);
Expand All @@ -144,7 +106,7 @@ fn parse_struct(

quote! {
#[automatically_derived]
impl Serialize for #ty {
impl < #( #impl_lifetimes ),* > Serialize for #ty < #( #ty_lifetimes ),* > {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, SerError>
where S: SerializerTrait
{
Expand All @@ -158,11 +120,14 @@ fn parse_struct(

fn parse_newtype_struct(reciever: &TypeReciever) -> TokenStream {
let ty = reciever.ident.clone();
let impl_lifetimes = reciever.generics.lifetimes();
let ty_lifetimes = reciever.generics.lifetimes().map(|l| &l.lifetime);

let classname = reciever.class.clone().unwrap_or_else(|| ty.to_string());

quote! {
#[automatically_derived]
impl Serialize for #ty {
impl < #( #impl_lifetimes ),* > Serialize for #ty < #( #ty_lifetimes ),* > {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, SerError>
where S: SerializerTrait
{
Expand Down Expand Up @@ -213,6 +178,12 @@ fn parse_field(field: &FieldReciever) -> ParseResult {
serialize_ivars.serialize_entry(&field, &__SerializeField(&self.#field_ident))?;
}
}
} else if field.byte_string.is_present() {
quote! {
let field = Sym::new(#serialize_str).to_ivar();
let ty = _alox_48::SerializeByteString(self.#field_ident.as_ref());
serialize_ivars.serialize_entry(&field, &ty)?;
}
} else {
quote! {
let field = Sym::new(#serialize_str).to_ivar();
Expand Down
Loading

0 comments on commit 901144d

Please sign in to comment.