From 15c5980fbb720567cb8ccbb394a5d954253baa21 Mon Sep 17 00:00:00 2001 From: Nghia Date: Fri, 30 Aug 2024 20:14:18 +0000 Subject: [PATCH] add more lifetime/generics support for o2o (#17) * add more lifetime support for o2o * add support for template parameters in into * add example reference with lifetime * add new lifetime for from_ref * add new lifetime o2o for ref convert * update test for reference with lifetime --- README.md | 105 +++++++++++++++++++++++++++++++++++++++++ o2o-impl/src/expand.rs | 93 +++++++++++++++++++++++++----------- 2 files changed, 171 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 42bc9c3..be36c84 100644 --- a/README.md +++ b/README.md @@ -1443,6 +1443,75 @@ struct EntityDto{ ``` +### Reference with lifetime + +```rust +use o2o::o2o; + +pub struct Entity { + pub some_a: String, + pub some_b: String, +} + +#[derive(o2o)] +#[from_ref(Entity)] +pub struct EntityDto<'a, 'b> { + #[from(~.as_str())] + pub some_a: &'a str, + #[from(~.as_str())] + pub some_b: &'b str, +} +``` +
+ View generated code + + ``` rust ignore + impl<'a, 'b, 'o2o> ::core::convert::From<&'o2o Entity> for EntityDto<'a, 'b> + where + 'o2o: 'a + 'b, + { + fn from(value: &'o2o Entity) -> EntityDto<'a, 'b> { + EntityDto { + some_a: value.some_a.as_str(), + some_b: value.some_b.as_str(), + } + } + } + ``` +
+ +### Lifetime both ways + +```rust +use o2o::o2o; + +struct Entity<'a> { + some_str: &'a str, +} + +#[derive(o2o)] +#[map_owned(Entity<'a>)] +struct EntityDto<'a> { + some_str: &'a str, +} +``` +
+ View generated code + + ``` rust ignore + impl<'a> ::core::convert::From> for EntityDto<'a> { + fn from(value: Entity<'a>) -> EntityDto<'a> { + EntityDto { some_str: value.some_str } + } + } + impl<'a> ::core::convert::Into> for EntityDto<'a> { + fn into(self) -> Entity<'a> { + Entity { some_str: self.some_str } + } + } + ``` +
+ ### Generics ``` rust @@ -1483,6 +1552,42 @@ struct EntityDto { ``` +### Generics both ways + +```rust +use o2o::o2o; + +struct Entity { + something: T, +} + +#[derive(o2o)] +#[map_owned(Entity)] +struct EntityDto { + something: T, +} +``` +
+ View generated code + + ``` rust ignore + impl ::core::convert::From> for EntityDto { + fn from(value: Entity) -> EntityDto { + EntityDto { + something: value.something, + } + } + } + impl ::core::convert::Into> for EntityDto { + fn into(self) -> Entity { + Entity { + something: self.something, + } + } + } + ``` +
+ ### Where clauses ``` rust diff --git a/o2o-impl/src/expand.rs b/o2o-impl/src/expand.rs index bd93fd4..9343c43 100644 --- a/o2o-impl/src/expand.rs +++ b/o2o-impl/src/expand.rs @@ -5,7 +5,7 @@ use crate::{ attr::{ApplicableAttr, ChildData, ChildPath, DataTypeAttrs, GhostData, GhostIdent, InitData, Kind, TraitAttrCore, TypeHint}, validate::validate }; use proc_macro2::{TokenStream, Span}; -use syn::{punctuated::Punctuated, Data, DeriveInput, Error, Index, Member, Result, Token}; +use syn::{parse2, parse_quote, punctuated::Punctuated, Data, DeriveInput, Error, Index, Member, Result, Token, Type}; use quote::{format_ident, quote, ToTokens}; pub fn derive(node: &DeriveInput) -> Result { @@ -333,9 +333,15 @@ fn struct_main_code_block(input: &Struct, ctx: &ImplContext) -> TokenStream { quote!(#dst #struct_init_block) }, Kind::OwnedInto | Kind::RefInto => { - let dst = if ctx.struct_attr.ty.nameless_tuple || ctx.has_post_init { TokenStream::new() } else { ctx.dst_ty.clone() }; + let dst = if ctx.struct_attr.ty.nameless_tuple || ctx.has_post_init { + TokenStream::new() + } else if let Ok(Type::Path(path)) = parse2::(ctx.dst_ty.clone()) { + path.path.segments.first().unwrap().ident.to_token_stream() + } else { + ctx.dst_ty.clone() + }; quote!(#dst #struct_init_block) - }, + } Kind::OwnedIntoExisting | Kind::RefIntoExisting => struct_init_block, } } @@ -1052,31 +1058,64 @@ struct QuoteTraitParams<'a> { pub dst: &'a TokenStream, pub src: &'a TokenStream, pub gens: TokenStream, + pub gens_impl: TokenStream, pub where_clause: Option, pub r: Option, } fn get_quote_trait_params<'a>(input: &DataType, ctx: &'a ImplContext) -> QuoteTraitParams<'a> { - QuoteTraitParams { - attr: ctx.struct_attr.attribute.as_ref(), - impl_attr: ctx.struct_attr.impl_attribute.as_ref(), - inner_attr: ctx.struct_attr.inner_attribute.as_ref(), - dst: ctx.dst_ty, - src: ctx.src_ty, - gens: input.get_generics().to_token_stream(), - where_clause: input.get_attrs().where_attr(&ctx.struct_attr.ty).map(|x| { - let where_clause = x.where_clause.to_token_stream(); - quote!(where #where_clause) - }), - r: ctx.kind.is_ref().then_some(quote!(&)) + // If there is at least one lifetime in generics,we add a new lifetime `'o2o` and add a bound `'o2o: 'a + 'b`. + let generics = input.get_generics(); + let (gens_impl, where_clause, r) = if ctx.kind.is_ref() && generics.lifetimes().next().is_some() { + let lifetimes: Vec<_> = generics + .lifetimes() + .map(|params| params.lifetime.clone()) + .collect(); + + let mut generics = generics.clone(); + generics.params.push(parse_quote!('o2o)); + + let mut where_clause = input + .get_attrs() + .where_attr(&ctx.struct_attr.ty) + .map(|x| x.where_clause.clone()) + .unwrap_or_default(); + where_clause.push(parse_quote!('o2o: #( #lifetimes )+*)); + + ( + generics.to_token_stream(), + Some(quote!(where #where_clause)), + Some(quote!(&'o2o)), + ) + } else { + ( + input.get_generics().to_token_stream(), + input.get_attrs().where_attr(&ctx.struct_attr.ty).map(|x| { + let where_clause = x.where_clause.to_token_stream(); + quote!(where #where_clause) + }), + ctx.kind.is_ref().then_some(quote!(&)), + ) + }; + + QuoteTraitParams { + attr: ctx.struct_attr.attribute.as_ref(), + impl_attr: ctx.struct_attr.impl_attribute.as_ref(), + inner_attr: ctx.struct_attr.inner_attribute.as_ref(), + dst: ctx.dst_ty, + src: ctx.src_ty, + gens: generics.to_token_stream(), + gens_impl, + where_clause, + r, } } fn quote_from_trait(input: &DataType, ctx: &ImplContext, pre_init: Option, init: TokenStream) -> TokenStream { - let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, where_clause, r } = get_quote_trait_params(input, ctx); + let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, gens_impl, where_clause, r } = get_quote_trait_params(input, ctx); quote! { #impl_attr - impl #gens ::core::convert::From<#r #src> for #dst #gens #where_clause { + impl #gens_impl ::core::convert::From<#r #src> for #dst #gens #where_clause { #attr fn from(value: #r #src) -> #dst #gens { #inner_attr @@ -1088,11 +1127,11 @@ fn quote_from_trait(input: &DataType, ctx: &ImplContext, pre_init: Option, init: TokenStream) -> TokenStream { - let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, where_clause, r } = get_quote_trait_params(input, ctx); + let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, gens_impl, where_clause, r } = get_quote_trait_params(input, ctx); let err_ty = &ctx.struct_attr.err_ty.as_ref().unwrap().path; quote! { #impl_attr - impl #gens ::core::convert::TryFrom<#r #src> for #dst #gens #where_clause { + impl #gens_impl ::core::convert::TryFrom<#r #src> for #dst #gens #where_clause { type Error = #err_ty; #attr fn try_from(value: #r #src) -> Result<#dst #gens, #err_ty> { @@ -1105,7 +1144,7 @@ fn quote_try_from_trait(input: &DataType, ctx: &ImplContext, pre_init: Option, init: TokenStream, post_init: Option) -> TokenStream { - let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, where_clause, r } = get_quote_trait_params(input, ctx); + let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, gens_impl, where_clause, r } = get_quote_trait_params(input, ctx); let body = match post_init { Some(post_init) => quote! { @@ -1122,7 +1161,7 @@ fn quote_into_trait(input: &DataType, ctx: &ImplContext, pre_init: Option for #r #src #gens #where_clause { + impl #gens_impl ::core::convert::Into<#dst> for #r #src #gens #where_clause { #attr fn into(self) -> #dst { #inner_attr @@ -1133,7 +1172,7 @@ fn quote_into_trait(input: &DataType, ctx: &ImplContext, pre_init: Option, init: TokenStream, post_init: Option) -> TokenStream { - let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, where_clause, r } = get_quote_trait_params(input, ctx); + let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, gens_impl, where_clause, r } = get_quote_trait_params(input, ctx); let err_ty = &ctx.struct_attr.err_ty.as_ref().unwrap().path; let body = match post_init { @@ -1151,7 +1190,7 @@ fn quote_try_into_trait(input: &DataType, ctx: &ImplContext, pre_init: Option for #r #src #gens #where_clause { + impl #gens_impl ::core::convert::TryInto<#dst> for #r #src #gens #where_clause { type Error = #err_ty; #attr fn try_into(self) -> Result<#dst, #err_ty> { @@ -1163,10 +1202,10 @@ fn quote_try_into_trait(input: &DataType, ctx: &ImplContext, pre_init: Option, init: TokenStream, post_init: Option) -> TokenStream { - let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, where_clause, r } = get_quote_trait_params(input, ctx); + let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, gens_impl, where_clause, r } = get_quote_trait_params(input, ctx); quote! { #impl_attr - impl #gens o2o::traits::IntoExisting<#dst> for #r #src #gens #where_clause { + impl #gens_impl o2o::traits::IntoExisting<#dst> for #r #src #gens #where_clause { #attr fn into_existing(self, other: &mut #dst) { #inner_attr @@ -1179,11 +1218,11 @@ fn quote_into_existing_trait(input: &DataType, ctx: &ImplContext, pre_init: Opti } fn quote_try_into_existing_trait(input: &DataType, ctx: &ImplContext, pre_init: Option, init: TokenStream, post_init: Option) -> TokenStream { - let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, where_clause, r } = get_quote_trait_params(input, ctx); + let QuoteTraitParams { attr, impl_attr, inner_attr, dst, src, gens, gens_impl, where_clause, r } = get_quote_trait_params(input, ctx); let err_ty = &ctx.struct_attr.err_ty.as_ref().unwrap().path; quote! { #impl_attr - impl #gens o2o::traits::TryIntoExisting<#dst> for #r #src #gens #where_clause { + impl #gens_impl o2o::traits::TryIntoExisting<#dst> for #r #src #gens #where_clause { type Error = #err_ty; #attr fn try_into_existing(self, other: &mut #dst) -> Result<(), #err_ty> {