From 956a787dd71d161ac504972e9030cee99dbe0440 Mon Sep 17 00:00:00 2001 From: Artem Romanenia Date: Thu, 21 Mar 2024 17:34:03 +0300 Subject: [PATCH 1/3] Enums implementation v2 --- o2o-impl/src/ast.rs | 173 +++++++- o2o-impl/src/attr.rs | 15 +- o2o-impl/src/expand.rs | 391 +++++++++++++----- o2o-impl/src/validate.rs | 41 +- .../27_enum_bare_top_level_attr_tests.rs | 61 +++ .../28_enum_bare_top_level_attr_tests.rs | 30 ++ o2o-tests/tests/29_enums_playground.rs | 124 ++++++ 7 files changed, 693 insertions(+), 142 deletions(-) create mode 100644 o2o-tests/tests/27_enum_bare_top_level_attr_tests.rs create mode 100644 o2o-tests/tests/28_enum_bare_top_level_attr_tests.rs create mode 100644 o2o-tests/tests/29_enums_playground.rs diff --git a/o2o-impl/src/ast.rs b/o2o-impl/src/ast.rs index 5dfa5cd..ed19609 100644 --- a/o2o-impl/src/ast.rs +++ b/o2o-impl/src/ast.rs @@ -1,7 +1,9 @@ use crate::attr::{self}; use crate::attr::{FieldAttrs, StructAttrs}; +use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::{DataStruct, DeriveInput, Fields, Generics, Ident, Index, Member, Result}; +use syn::token::Comma; +use syn::{Attribute, DataEnum, DataStruct, DeriveInput, Fields, Generics, Ident, Index, Member, Result}; pub(crate) struct Struct<'a> { pub attrs: StructAttrs, @@ -11,12 +13,6 @@ pub(crate) struct Struct<'a> { pub named_fields: bool, } -pub(crate) struct Field { - pub attrs: FieldAttrs, - pub idx: usize, - pub member: Member, -} - impl<'a> Struct<'a> { pub fn from_syn(node: &'a DeriveInput, data: &'a DataStruct) -> Result { let (attrs, bark) = attr::get_struct_attrs(&node.attrs)?; @@ -31,6 +27,13 @@ impl<'a> Struct<'a> { } } +#[derive(Clone)] +pub(crate) struct Field { + pub attrs: FieldAttrs, + pub idx: usize, + pub member: Member, +} + impl<'a> Field { fn multiple_from_syn(fields: &'a Fields, bark: bool) -> Result> { let mut attrs_to_repeat = None; @@ -62,7 +65,7 @@ impl<'a> Field { fn from_syn(i: usize, node: &'a syn::Field, bark: bool) -> Result { Ok(Field { - attrs: attr::get_field_attrs(node, bark)?, + attrs: attr::get_field_attrs(SynDataTypeMember::Field(node), bark)?, idx: i, member: node.ident.clone().map(Member::Named).unwrap_or_else(|| { Member::Unnamed(Index { @@ -73,3 +76,157 @@ impl<'a> Field { }) } } + +pub(crate) struct Enum<'a> { + pub attrs: StructAttrs, + pub ident: &'a Ident, + pub generics: &'a Generics, + pub variants: Vec +} + +impl<'a> Enum<'a> { + pub fn from_syn(node: &'a DeriveInput, data: &'a DataEnum) -> Result { + let (attrs, bark) = attr::get_struct_attrs(&node.attrs)?; + let variants = Variant::multiple_from_syn(&data.variants, bark)?; + Ok(Enum { + attrs, + ident: &node.ident, + generics: &node.generics, + variants + }) + } +} + +pub(crate) struct Variant { + pub attrs: FieldAttrs, + pub ident: Ident, + pub idx: usize, + pub fields: Vec, + pub named_fields: bool, +} + +impl<'a> Variant { + fn multiple_from_syn(variants: &'a Punctuated, bark: bool) -> Result> { + let mut attrs_to_repeat = None; + + variants + .iter() + .enumerate() + .map(|(i, variant)| { + let mut field = Variant::from_syn(i, variant, bark)?; + + if field.attrs.stop_repeat { + attrs_to_repeat = None; + } + + if field.attrs.repeat.is_some() { + if attrs_to_repeat.is_some() && !field.attrs.stop_repeat { + panic!("Previous #[repeat] instruction must be terminated with #[stop_repeat]") + } + + attrs_to_repeat = Some(field.attrs.clone()); + } else if let Some(attrs_to_repeat) = &attrs_to_repeat { + field.attrs.merge(attrs_to_repeat.clone()); + } + + Ok(field) + }) + .collect() + } + + fn from_syn(i: usize, variant: &'a syn::Variant, bark: bool) -> Result { + let fields = Field::multiple_from_syn(&variant.fields, bark)?; + Ok(Variant { + attrs: attr::get_field_attrs(SynDataTypeMember::Variant(&variant), bark)?, + ident: variant.ident.clone(), + idx: i, + fields, + named_fields: matches!(&variant.fields, Fields::Named(_)), + }) + } +} + +pub(crate) enum DataType<'a> { + Struct(&'a Struct<'a>), + Enum(&'a Enum<'a>) +} + +impl<'a> DataType<'a> { + pub fn get_ident(&'a self) -> &Ident { + match self { + DataType::Struct(s) => &s.ident, + DataType::Enum(e) => &e.ident + } + } + + pub fn get_attrs(&'a self) -> &'a StructAttrs { + match self { + DataType::Struct(s) => &s.attrs, + DataType::Enum(e) => &e.attrs + } + } + + pub fn get_members(&'a self) -> Vec { + match self { + DataType::Struct(s) => s.fields.iter().map(|x| DataTypeMember::Field(x)).collect(), + DataType::Enum(e) => e.variants.iter().map(|x| DataTypeMember::Variant(x)).collect() + } + } + + pub fn get_generics(&'a self) -> &'a Generics { + match self { + DataType::Struct(s) => &s.generics, + DataType::Enum(e) => &e.generics + } + } + + // pub fn get_field(&'a self, ty: &TypePath) -> Option<&Field> { + // match self { + // DataType::Struct(s) => s.fields.iter().find(|f| + // f.attrs.field_attr_core(&Kind::OwnedInto, ty) + // .filter(|&g| g.wrapper).is_some() + // ), + // DataType::Enum(e) => None + // } + // } + + // pub fn get_field_with_action(&'a self, ty: &TypePath) -> Option<(&Field, &Option)> { + // match self { + // DataType::Struct(s) => s.fields.iter().find_map(|f| + // f.attrs.field_attr_core(&Kind::RefInto, ty) + // .filter(|&g| g.wrapper) + // .map(|x| (f, &x.action)) + // ), + // DataType::Enum(e) => None + // } + // } +} + +pub(crate) enum SynDataTypeMember<'a> { + Field(&'a syn::Field), + Variant(&'a syn::Variant) +} + +impl<'a> SynDataTypeMember<'a> { + pub fn get_attrs(&'a self) -> &'a Vec { + match self { + SynDataTypeMember::Field(f) => &f.attrs, + SynDataTypeMember::Variant(v) => &v.attrs + } + } +} + +#[derive(Clone, Copy)] +pub(crate) enum DataTypeMember<'a> { + Field(&'a Field), + Variant(&'a Variant) +} + +impl<'a> DataTypeMember<'a> { + pub fn get_attrs(&'a self) -> &'a FieldAttrs { + match self { + DataTypeMember::Field(f) => &f.attrs, + DataTypeMember::Variant(v) => &v.attrs + } + } +} \ No newline at end of file diff --git a/o2o-impl/src/attr.rs b/o2o-impl/src/attr.rs index 65044c7..b2e7489 100644 --- a/o2o-impl/src/attr.rs +++ b/o2o-impl/src/attr.rs @@ -10,6 +10,7 @@ use syn::spanned::Spanned; use syn::token::{Brace, Paren}; use syn::{Attribute, Ident, Result, Token, Member, parenthesized, braced, WherePredicate, Error}; +use crate::ast::SynDataTypeMember; use crate::kw; struct OptionalParenthesizedTokenStream { @@ -317,11 +318,13 @@ pub(crate) enum StructKindHint { Unspecified = 2 } +#[derive(Clone)] pub(crate) struct StructAttr { pub attr: StructAttrCore, pub applicable_to: ApplicableTo, } +#[derive(Clone)] pub(crate) struct StructAttrCore { pub ty: TypePath, pub struct_kind_hint: StructKindHint, @@ -369,6 +372,7 @@ impl Parse for StructAttrCore { } } +#[derive(Clone)] pub(crate) struct InitData { pub ident: Ident, _colon: Token![:], @@ -659,9 +663,9 @@ pub(crate) fn get_struct_attrs(input: &[Attribute]) -> Result<(StructAttrs, bool Ok((StructAttrs {attrs, ghost_attrs, where_attrs, children_attrs }, bark)) } -pub(crate) fn get_field_attrs(input: &syn::Field, bark: bool) -> Result { +pub(crate) fn get_field_attrs(input: SynDataTypeMember, bark: bool) -> Result { let mut instrs: Vec = vec![]; - for x in input.attrs.iter() { + for x in input.get_attrs().iter() { if x.path.is_ident("doc"){ continue; } else if x.path.is_ident("o2o") { @@ -692,7 +696,12 @@ pub(crate) fn get_field_attrs(input: &syn::Field, bark: bool) -> Result child_attrs.push(attr), MemberInstruction::Ghost(attr) => ghost_attrs.push(attr), MemberInstruction::Parent(attr) => parent_attrs.push(attr), - MemberInstruction::As(attr) => add_as_type_attrs(input, attr, &mut attrs), + MemberInstruction::As(attr) => { + match input { + SynDataTypeMember::Field(f) => add_as_type_attrs(f, attr, &mut attrs), + SynDataTypeMember::Variant(_) => panic!("weird") + }; + }, MemberInstruction::Repeat(repeat_for) => repeat = Some(repeat_for), MemberInstruction::StopRepeat => stop_repeat = true, MemberInstruction::Unrecognized => () diff --git a/o2o-impl/src/expand.rs b/o2o-impl/src/expand.rs index a199bc1..2e3644a 100644 --- a/o2o-impl/src/expand.rs +++ b/o2o-impl/src/expand.rs @@ -1,83 +1,95 @@ use std::{slice::Iter, iter::Peekable, collections::HashSet}; use crate::{ - ast::{Field, Struct}, - attr::{ApplicableAttr, ChildData, ChildPath, GhostData, InitData, Kind, StructAttrCore, StructKindHint}, validate::validate + ast::{DataType, DataTypeMember, Enum, Field, Struct, Variant}, + attr::{ApplicableAttr, ChildData, ChildPath, GhostData, InitData, Kind, StructAttrCore, StructAttrs, StructKindHint}, validate::validate }; use proc_macro2::{TokenStream, Span}; -use syn::{DeriveInput, Result, Error, Data, Token, Member, Index, punctuated::Punctuated}; -use quote::{quote, ToTokens}; +use syn::{punctuated::Punctuated, Data, DataStruct, DeriveInput, Error, Generics, Index, Member, Result, Token}; +use quote::{format_ident, quote, ToTokens}; pub fn derive(node: &DeriveInput) -> Result { match &node.data { Data::Struct(data) => { let input = Struct::from_syn(node, data)?; + let input = DataType::Struct(&input); validate(&input)?; Ok(struct_impl(input)) }, + Data::Enum(data) => { + let input = Enum::from_syn(node, data)?; + let input = DataType::Enum(&input); + validate(&input)?; + //Ok(TokenStream::new()) + Ok(struct_impl(input)) + }, _ => Err(Error::new_spanned( node, - "#[derive(o2o)] only supports structs.", + "#[derive(o2o)] only supports structs and enums.", )) } } +#[derive(Clone)] struct ImplContext<'a> { - input: &'a Struct<'a>, + input: &'a DataType<'a>, struct_attr: &'a StructAttrCore, kind: Kind, dst_ty: &'a TokenStream, src_ty: &'a TokenStream, - has_post_init: bool + has_post_init: bool, + destructured_src: bool, } -fn struct_impl(input: Struct) -> TokenStream { - let ty = input.ident.to_token_stream(); +fn struct_impl(input: DataType) -> TokenStream { + let ty = input.get_ident().to_token_stream(); + let attrs = input.get_attrs(); - let from_owned_impls = input.attrs.iter_for_kind(&Kind::FromOwned).map(|struct_attr| { - let ctx = ImplContext{ + let from_owned_impls = attrs.iter_for_kind(&Kind::FromOwned).map(|struct_attr| { + let ctx = ImplContext { input: &input, struct_attr, kind: Kind::FromOwned, dst_ty: &ty, src_ty: &struct_attr.ty.path, has_post_init: false, + destructured_src: false }; let pre_init = struct_pre_init(&ctx, &struct_attr.init_data); if let Some(quick_return) = &struct_attr.quick_return { return quote_from_trait(&ctx, pre_init, quote_action(quick_return, None, &ctx)) } - let init = struct_init_block(&ctx); - quote_from_trait(&ctx, pre_init, quote!(#ty #init)) + quote_from_trait(&ctx, pre_init, main_code_block(&ctx)) }); - let from_ref_impls = input.attrs.iter_for_kind(&Kind::FromRef).map(|struct_attr| { - let ctx = ImplContext{ + let from_ref_impls = attrs.iter_for_kind(&Kind::FromRef).map(|struct_attr| { + let ctx = ImplContext { input: &input, struct_attr, kind: Kind::FromRef, dst_ty: &ty, src_ty: &struct_attr.ty.path, has_post_init: false, + destructured_src: false }; let pre_init = struct_pre_init(&ctx, &struct_attr.init_data); if let Some(quick_return) = &struct_attr.quick_return { return quote_from_trait(&ctx, pre_init, quote_action(quick_return, None, &ctx)) } - let init = struct_init_block(&ctx); - quote_from_trait(&ctx, pre_init, quote!(#ty #init)) + quote_from_trait(&ctx, pre_init, main_code_block(&ctx)) }); - let owned_into_impls = input.attrs.iter_for_kind(&Kind::OwnedInto).map(|struct_attr| { - let mut ctx = ImplContext{ + let owned_into_impls = attrs.iter_for_kind(&Kind::OwnedInto).map(|struct_attr| { + let mut ctx = ImplContext { input: &input, struct_attr, kind: Kind::OwnedInto, dst_ty: &struct_attr.ty.path, src_ty: &ty, has_post_init: false, + destructured_src: false }; let pre_init = struct_pre_init(&ctx, &struct_attr.init_data); @@ -86,19 +98,18 @@ fn struct_impl(input: Struct) -> TokenStream { } let post_init = struct_post_init(&ctx); ctx.has_post_init = post_init.is_some(); - let init = struct_init_block(&ctx); - let dst = if struct_attr.ty.nameless_tuple || post_init.is_some() { TokenStream::new() } else { struct_attr.ty.path.clone() }; - quote_into_trait(&ctx, pre_init, quote!(#dst #init), post_init) + quote_into_trait(&ctx, pre_init, main_code_block(&ctx), post_init) }); - let ref_into_impls = input.attrs.iter_for_kind(&Kind::RefInto).map(|struct_attr| { - let mut ctx = ImplContext{ + let ref_into_impls = attrs.iter_for_kind(&Kind::RefInto).map(|struct_attr| { + let mut ctx = ImplContext { input: &input, struct_attr, kind: Kind::RefInto, dst_ty: &struct_attr.ty.path, src_ty: &ty, has_post_init: false, + destructured_src: false }; let pre_init = struct_pre_init(&ctx, &struct_attr.init_data); @@ -107,19 +118,18 @@ fn struct_impl(input: Struct) -> TokenStream { } let post_init = struct_post_init(&ctx); ctx.has_post_init = post_init.is_some(); - let init = struct_init_block(&ctx); - let dst = if struct_attr.ty.nameless_tuple || post_init.is_some() { TokenStream::new() } else { struct_attr.ty.path.clone() }; - quote_into_trait(&ctx, pre_init, quote!(#dst #init), post_init) + quote_into_trait(&ctx, pre_init, main_code_block(&ctx), post_init) }); - let owned_into_existing_impls = input.attrs.iter_for_kind(&Kind::OwnedIntoExisting).map(|struct_attr| { - let mut ctx = ImplContext{ + let owned_into_existing_impls = attrs.iter_for_kind(&Kind::OwnedIntoExisting).map(|struct_attr| { + let mut ctx = ImplContext { input: &input, struct_attr, kind: Kind::OwnedIntoExisting, dst_ty: &struct_attr.ty.path, src_ty: &ty, has_post_init: false, + destructured_src: false }; let pre_init = struct_pre_init(&ctx, &struct_attr.init_data); if let Some(quick_return) = &struct_attr.quick_return { @@ -128,18 +138,18 @@ fn struct_impl(input: Struct) -> TokenStream { } let post_init = struct_post_init(&ctx); ctx.has_post_init = post_init.is_some(); - let init = struct_init_block(&ctx); - quote_into_existing_trait(&ctx, pre_init, init, post_init) + quote_into_existing_trait(&ctx, pre_init, main_code_block(&ctx), post_init) }); - let ref_into_existing_impls = input.attrs.iter_for_kind(&Kind::RefIntoExisting).map(|struct_attr| { - let mut ctx = ImplContext{ + let ref_into_existing_impls = attrs.iter_for_kind(&Kind::RefIntoExisting).map(|struct_attr| { + let mut ctx = ImplContext { input: &input, struct_attr, kind: Kind::RefIntoExisting, dst_ty: &struct_attr.ty.path, src_ty: &ty, has_post_init: false, + destructured_src: false }; let pre_init = struct_pre_init(&ctx, &struct_attr.init_data); if let Some(quick_return) = &struct_attr.quick_return { @@ -148,8 +158,7 @@ fn struct_impl(input: Struct) -> TokenStream { } let post_init = struct_post_init(&ctx); ctx.has_post_init = post_init.is_some(); - let init = struct_init_block(&ctx); - quote_into_existing_trait(&ctx, pre_init, init, post_init) + quote_into_existing_trait(&ctx, pre_init, main_code_block(&ctx), post_init) }); let result = quote! { @@ -164,7 +173,43 @@ fn struct_impl(input: Struct) -> TokenStream { result } +fn main_code_block<'a>(ctx: &'a ImplContext) -> TokenStream { + match ctx.input { + DataType::Struct(_) => { + let struct_init_block = struct_init_block(ctx); + + match ctx.kind { + Kind::FromOwned | Kind::FromRef => { + let dst = ctx.dst_ty; + 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() }; + quote!(#dst #struct_init_block) + }, + Kind::OwnedIntoExisting | Kind::RefIntoExisting => struct_init_block, + } + }, + DataType::Enum(_) => { + let struct_init_block = struct_init_block(ctx); + + match ctx.kind { + Kind::FromOwned | Kind::FromRef => { + quote!(match value #struct_init_block) + }, + Kind::OwnedInto | Kind::RefInto => { + quote!(match self #struct_init_block) + }, + Kind::OwnedIntoExisting | Kind::RefIntoExisting => struct_init_block, + } + } + } +} + fn struct_init_block<'a>(ctx: &'a ImplContext) -> TokenStream { + let attrs = ctx.input.get_attrs(); + let members = ctx.input.get_members(); + let mut current_path = ""; let mut group_counter = 0; let mut unique_paths = HashSet::<&str>::new(); @@ -179,15 +224,15 @@ fn struct_init_block<'a>(ctx: &'a ImplContext) -> TokenStream { let mut fields: Vec<(usize, &str, FieldData)> = vec![]; - fields.extend(ctx.input.fields.iter() + fields.extend(members.iter() .map(|x| { - let path = x.attrs.child(&ctx.struct_attr.ty).map(|x| x.get_child_path_str(None)).unwrap_or(""); + let path = x.get_attrs().child(&ctx.struct_attr.ty).map(|x| x.get_child_path_str(None)).unwrap_or(""); unique_paths.insert(path); - make_tuple(path, FieldData::Field(x)) + make_tuple(path, FieldData::Field(*x)) }) .collect::>()); - fields.extend(ctx.input.attrs.ghost_attrs.iter() + fields.extend(attrs.ghost_attrs.iter() .flat_map(|x| &x.attr.ghost_data) .filter(|x| unique_paths.insert(x.get_child_path_str(None))) .map(|x| { @@ -201,17 +246,17 @@ fn struct_init_block<'a>(ctx: &'a ImplContext) -> TokenStream { } enum FieldData<'a> { - Field(&'a Field), + Field(DataTypeMember<'a>), GhostData(&'a GhostData) } fn struct_init_block_inner( - fields: &mut Peekable>, + members: &mut Peekable>, ctx: &ImplContext, field_ctx: Option<(&ChildPath, Option<&ChildData>, usize)> ) -> TokenStream { - let next = fields.peek(); + let next = members.peek(); if next.is_none() { return quote!({}) } @@ -227,7 +272,7 @@ fn struct_init_block_inner( let mut fragments: Vec = vec![]; let mut idx: usize = 0; - while let Some((_, key, field_data)) = fields.peek() { + while let Some((_, key, field_data)) = members.peek() { if let Some(field_ctx) = field_ctx { if !key.starts_with(field_ctx.0.get_child_path_str(Some(field_ctx.2))) { break; @@ -236,26 +281,33 @@ fn struct_init_block_inner( match field_data { FieldData::Field(f) => { - if !ctx.kind.is_from() && (f.attrs.ghost(&ctx.struct_attr.ty, &ctx.kind).is_some() || f.attrs.has_parent_attr(&ctx.struct_attr.ty)) { - fields.next(); + let attrs = f.get_attrs(); + if !ctx.kind.is_from() && (attrs.ghost(&ctx.struct_attr.ty, &ctx.kind).is_some() || attrs.has_parent_attr(&ctx.struct_attr.ty)) { + members.next(); continue; } if ctx.kind.is_from() { - if let Some(ghost_attr) = f.attrs.ghost(&ctx.struct_attr.ty, &ctx.kind) { + if let Some(ghost_attr) = attrs.ghost(&ctx.struct_attr.ty, &ctx.kind) { if ghost_attr.action.is_none() { - fields.next(); + members.next(); continue; } } } - let fragment = match f.attrs.child(&ctx.struct_attr.ty) { + let fragment = match attrs.child(&ctx.struct_attr.ty) { Some(child_attr) => - render_child_fragment(&child_attr.child_path, fields, ctx, field_ctx, struct_kind_hint, || render_line(f, ctx, struct_kind_hint, idx)), + match f { + DataTypeMember::Field(f) => render_child_fragment(&child_attr.child_path, members, ctx, field_ctx, struct_kind_hint, || render_struct_line(f, ctx, struct_kind_hint, idx)), + DataTypeMember::Variant(_) => todo!(), + } None => { - fields.next(); - render_line(f, ctx, struct_kind_hint, idx) + members.next(); + match f { + DataTypeMember::Field(f) => render_struct_line(f, ctx, struct_kind_hint, idx), + DataTypeMember::Variant(v) => render_enum_line(v, ctx, struct_kind_hint, idx), + } } }; fragments.push(fragment); @@ -263,7 +315,7 @@ fn struct_init_block_inner( }, FieldData::GhostData(ghost_data) => { let child_path = &ghost_data.child_path.as_ref().unwrap(); - let fragment = render_child_fragment(child_path, fields, ctx, field_ctx, struct_kind_hint, TokenStream::new); + let fragment = render_child_fragment(child_path, members, ctx, field_ctx, struct_kind_hint, TokenStream::new); fragments.push(fragment); idx += 1; @@ -272,7 +324,7 @@ fn struct_init_block_inner( } if !ctx.kind.is_from() { - if let Some(ghost_attr) = ctx.input.attrs.ghost_attr(&ctx.struct_attr.ty, &ctx.kind) { + if let Some(ghost_attr) = ctx.input.get_attrs().ghost_attr(&ctx.struct_attr.ty, &ctx.kind) { ghost_attr.ghost_data.iter().for_each(|x| { match (&x.child_path, field_ctx) { (Some(_), Some(field_ctx)) => { @@ -296,13 +348,50 @@ fn struct_init_block_inner( return quote!(#(#fragments)*) } - match (&ctx.kind, struct_kind_hint, ctx.input.named_fields) { - (Kind::FromOwned | Kind::FromRef, _, true) => quote!({#(#fragments)*}), - (Kind::FromOwned | Kind::FromRef, _, false) => quote!((#(#fragments)*)), - (_, StructKindHint::Struct, _) => quote!({#(#fragments)*}), - (_, StructKindHint::Tuple, _) => quote!((#(#fragments)*)), - (_, StructKindHint::Unspecified, true) => quote!({#(#fragments)*}), - (_, StructKindHint::Unspecified, false) => quote!((#(#fragments)*)), + match ctx.input { + DataType::Struct(s) => { + match (&ctx.kind, struct_kind_hint, s.named_fields) { + (Kind::FromOwned | Kind::FromRef, _, true) => quote!({#(#fragments)*}), + (Kind::FromOwned | Kind::FromRef, _, false) => quote!((#(#fragments)*)), + (_, StructKindHint::Struct, _) => quote!({#(#fragments)*}), + (_, StructKindHint::Tuple, _) => quote!((#(#fragments)*)), + (_, StructKindHint::Unspecified, true) => quote!({#(#fragments)*}), + (_, StructKindHint::Unspecified, false) => quote!((#(#fragments)*)), + } + }, + DataType::Enum(_) => { + quote!({#(#fragments)*}) + }, + } +} + +fn variant_destruct_block(ctx: &ImplContext<'_>) -> TokenStream { + match ctx.input { + DataType::Struct(s) => { + if s.named_fields { + let idents = s.fields.iter().map(|x| { + let attr = x.attrs.applicable_attr(&ctx.kind, &ctx.struct_attr.ty); + if !ctx.kind.is_from() || attr.is_none() { + let ident = &x.member; + quote!(#ident ,) + } else { + let attr = attr.unwrap(); + let ident = attr.get_field_name_or(&x.member); + quote!(#ident ,) + } + }); + + quote!({#(#idents)*}) + } else { + let idents = s.fields.iter().map(|x| { + let ident = format_ident!("f{}", x.idx); + quote!(#ident ,) + }); + + quote!((#(#idents)*)) + } + }, + DataType::Enum(_) => panic!("weird") } } @@ -361,9 +450,12 @@ fn struct_pre_init(ctx: &ImplContext, init_data: &Option Option { let mut fragments: Vec = vec![]; - ctx.input.fields.iter().for_each(|f| { - if !ctx.kind.is_from() && f.attrs.has_parent_attr(&ctx.struct_attr.ty) { - fragments.push(render_parent(f, ctx)) + ctx.input.get_members().iter().for_each(|f| { + if !ctx.kind.is_from() && f.get_attrs().has_parent_attr(&ctx.struct_attr.ty) { + match f { + DataTypeMember::Field(f) => fragments.push(render_parent(f, ctx)), + DataTypeMember::Variant(_) => todo!(), + } } }); @@ -390,14 +482,19 @@ fn render_child( field_ctx: (&ChildPath, usize), hint: StructKindHint) -> TokenStream { + let input = match ctx.input { + DataType::Struct(s) => *s, + _ => panic!("weird") + }; + let child_path = field_ctx.0; let path = child_path.get_child_path_str(Some(field_ctx.1)); let child_name = child_path.child_path[field_ctx.1].to_token_stream(); - let mut children = ctx.input.attrs.children_attr(&ctx.struct_attr.ty).unwrap().children.iter(); + let mut children = input.attrs.children_attr(&ctx.struct_attr.ty).unwrap().children.iter(); let child_data = children.find(|child_data| child_data.check_match(path)).unwrap(); let ty = &child_data.ty; let init = struct_init_block_inner(fields, ctx, Some((field_ctx.0, Some(child_data), field_ctx.1 ))); - match (ctx.input.named_fields, hint) { + match (input.named_fields, hint) { (true, StructKindHint::Struct | StructKindHint::Unspecified) => quote!(#child_name: #ty #init,), (true, StructKindHint::Tuple) => quote!(#ty #init,), (false, StructKindHint::Tuple | StructKindHint::Unspecified) => quote!(#ty #init,), @@ -411,15 +508,20 @@ fn render_existing_child( field_ctx: (&ChildPath, usize) ) -> TokenStream { + let input = match ctx.input { + DataType::Struct(s) => *s, + _ => panic!("weird") + }; + let child_attr = field_ctx.0; let path = child_attr.get_child_path_str(Some(field_ctx.1)); - let children_attr = ctx.input.attrs.children_attr(&ctx.struct_attr.ty); + let children_attr = input.attrs.children_attr(&ctx.struct_attr.ty); let child_data = children_attr.and_then(|x| x.children.iter().find(|child_data| child_data.check_match(path))); struct_init_block_inner(fields, ctx, Some((field_ctx.0, child_data, field_ctx.1 ))) } -fn render_line( +fn render_struct_line( f: &Field, ctx: &ImplContext, hint: StructKindHint, @@ -436,103 +538,168 @@ fn render_line( } }; + let obj = if ctx.destructured_src { TokenStream::new() } else { + match ctx.kind { + Kind::OwnedInto => quote!(self.), + Kind::RefInto => quote!(self.), + Kind::FromOwned => quote!(value.), + Kind::FromRef => quote!(value.), + Kind::OwnedIntoExisting => quote!(self.), + Kind::RefIntoExisting => quote!(self.), + } + }; + match (&f.member, attr, &ctx.kind, hint) { (syn::Member::Named(ident), None, Kind::OwnedInto | Kind::RefInto, StructKindHint::Struct | StructKindHint::Unspecified) => - if ctx.has_post_init { quote!(obj.#ident = self.#ident;) } else { quote!(#ident: self.#ident,) }, - (syn::Member::Named(ident), None, Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Struct | StructKindHint::Unspecified) =>{ + if ctx.has_post_init { quote!(obj.#ident = #obj #ident;) } else { quote!(#ident: #obj #ident,) }, + (syn::Member::Named(ident), None, Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Struct | StructKindHint::Unspecified) => { let field_path = get_field_path(&f.member); - quote!(other.#field_path = self.#ident;) + quote!(other.#field_path = #obj #ident;) }, (syn::Member::Named(ident), None, Kind::OwnedInto | Kind::RefInto, StructKindHint::Tuple) => - quote!(self.#ident,), - (syn::Member::Named(ident), None, Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Tuple) =>{ + quote!(#obj #ident,), + (syn::Member::Named(ident), None, Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Tuple) => { let index = Member::Unnamed(Index { index: f.idx as u32, span: Span::call_site() }); - quote!(other.#index = self.#ident;) + quote!(other.#index = #obj #ident;) } (syn::Member::Named(ident), None, Kind::FromOwned | Kind::FromRef, StructKindHint::Struct | StructKindHint::Unspecified) => if f.attrs.has_parent_attr(&ctx.struct_attr.ty) { if !ctx.kind.is_ref() { quote!(#ident: (&value).into(),) } else { quote!(#ident: value.into(),) } } else { let field_path = get_field_path(&f.member); - quote!(#ident: value.#field_path,) + quote!(#ident: #obj #field_path,) }, - (syn::Member::Named(ident), None, Kind::FromOwned | Kind::FromRef, StructKindHint::Tuple) =>{ + (syn::Member::Named(ident), None, Kind::FromOwned | Kind::FromRef, StructKindHint::Tuple) => { let index = Member::Unnamed(Index { index: f.idx as u32, span: Span::call_site() }); let field_path = get_field_path(&index); - quote!(#ident: value.#field_path,) + quote!(#ident: #obj #field_path,) }, (syn::Member::Unnamed(index), None, Kind::OwnedInto | Kind::RefInto, StructKindHint::Tuple | StructKindHint::Unspecified) => if ctx.has_post_init { let index2 = Member::Unnamed(Index { index: idx as u32, span: Span::call_site() }); - quote!(obj.#index2 = self.#index;) - } else { quote!(self.#index,) }, - (syn::Member::Unnamed(index), None, Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Tuple | StructKindHint::Unspecified) =>{ + quote!(obj.#index2 = #obj #index;) + } else { + let index = if ctx.destructured_src { format_ident!("f{}", index).to_token_stream() } else { index.to_token_stream() }; + quote!(#obj #index,) + }, + (syn::Member::Unnamed(index), None, Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Tuple | StructKindHint::Unspecified) => { let index2 = Member::Unnamed(Index { index: f.idx as u32, span: Span::call_site() }); - quote!(other.#index2 = self.#index;) + quote!(other.#index2 = #obj #index;) } - (syn::Member::Unnamed(_), None, Kind::FromOwned | Kind::FromRef, StructKindHint::Tuple | StructKindHint::Unspecified) => + (syn::Member::Unnamed(index), None, Kind::FromOwned | Kind::FromRef, StructKindHint::Tuple | StructKindHint::Unspecified) => if f.attrs.has_parent_attr(&ctx.struct_attr.ty) { if !ctx.kind.is_ref() { quote!((&value).into(),) } else { quote!(value.into(),) } } else { - let field_path = get_field_path(&f.member); - quote!(value.#field_path,) + let field_path = if ctx.destructured_src { get_field_path(&Member::Named(format_ident!("f{}", index))) } else { get_field_path(&f.member) }; + quote!(#obj #field_path,) }, - (syn::Member::Unnamed(_), None, _, StructKindHint::Struct) =>{ + (syn::Member::Unnamed(_), None, _, StructKindHint::Struct) => { if f.attrs.has_parent_attr(&ctx.struct_attr.ty) { if !ctx.kind.is_ref() { quote!((&value).into(),) } else { quote!(value.into(),) } } else { panic!("weird") } }, - (syn::Member::Named(ident), Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Struct | StructKindHint::Unspecified) =>{ + (syn::Member::Named(ident), Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Struct | StructKindHint::Unspecified) => { let field_name = attr.get_field_name_or(&f.member); - let right_side = attr.get_action_or(Some(ident.to_token_stream()), ctx, || quote!(self.#ident)); + let right_side = attr.get_action_or(Some(ident.to_token_stream()), ctx, || quote!(#obj #ident)); if ctx.has_post_init { quote!(obj.#field_name = #right_side;) } else { quote!(#field_name: #right_side,) } }, - (syn::Member::Named(ident), Some(attr), Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Struct | StructKindHint::Unspecified) =>{ + (syn::Member::Named(ident), Some(attr), Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Struct | StructKindHint::Unspecified) => { let field_path = get_field_path(attr.get_field_name_or(&f.member)); - let right_side = attr.get_action_or(Some(ident.to_token_stream()), ctx, || quote!(self.#ident)); + let right_side = attr.get_action_or(Some(ident.to_token_stream()), ctx, || quote!(#obj #ident)); quote!(other.#field_path = #right_side;) }, - (syn::Member::Named(ident), Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Tuple) =>{ - let right_side = attr.get_action_or(Some(get_field_path(&f.member)), ctx, || quote!(self.#ident)); + (syn::Member::Named(ident), Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Tuple) => { + let right_side = attr.get_action_or(Some(get_field_path(&f.member)), ctx, || quote!(#obj #ident)); quote!(#right_side,) }, - (syn::Member::Named(ident), Some(attr), Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Tuple) =>{ + (syn::Member::Named(ident), Some(attr), Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Tuple) => { let field_path = get_field_path(&Member::Unnamed(Index { index: idx as u32, span: Span::call_site() })); - let right_side = attr.get_action_or(Some(ident.to_token_stream()), ctx, || quote!(self.#ident)); + let right_side = attr.get_action_or(Some(ident.to_token_stream()), ctx, || quote!(#obj #ident)); quote!(other.#field_path = #right_side;) }, - (syn::Member::Named(ident), Some(attr), Kind::FromOwned | Kind::FromRef, _) =>{ - let right_side = attr.get_stuff(get_field_path, ctx, || &f.member); + (syn::Member::Named(ident), Some(attr), Kind::FromOwned | Kind::FromRef, _) => { + let right_side = attr.get_stuff(&obj, get_field_path, ctx, || &f.member); quote!(#ident: #right_side,) }, - (syn::Member::Unnamed(index), Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Tuple | StructKindHint::Unspecified) =>{ - let right_side = attr.get_action_or(Some(index.to_token_stream()), ctx, || quote!(self.#index)); + (syn::Member::Unnamed(index), Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Tuple | StructKindHint::Unspecified) => { + let right_side = attr.get_action_or(Some(index.to_token_stream()), ctx, || quote!(#obj #index)); quote!(#right_side,) }, - (syn::Member::Unnamed(index), Some(attr), Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Tuple | StructKindHint::Unspecified) =>{ + (syn::Member::Unnamed(index), Some(attr), Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Tuple | StructKindHint::Unspecified) => { let field_path = get_field_path(attr.get_field_name_or(&f.member)); - let right_side = attr.get_action_or(Some(index.to_token_stream()), ctx, || quote!(self.#index)); + let right_side = attr.get_action_or(Some(index.to_token_stream()), ctx, || quote!(#obj #index)); quote!(other.#field_path = #right_side;) }, - (syn::Member::Unnamed(index), Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Struct) =>{ + (syn::Member::Unnamed(index), Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Struct) => { let field_name = attr.get_ident(); - let right_side = attr.get_action_or(Some(index.to_token_stream()), ctx, || quote!(self.#index)); + let right_side = attr.get_action_or(Some(index.to_token_stream()), ctx, || quote!(#obj #index)); if ctx.has_post_init { quote!(obj.#field_name = #right_side;) } else { quote!(#field_name: #right_side,) } }, - (syn::Member::Unnamed(index), Some(attr), Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Struct) =>{ + (syn::Member::Unnamed(index), Some(attr), Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Struct) => { let field_path = get_field_path(attr.get_ident()); - let right_side = attr.get_action_or(Some(index.to_token_stream()), ctx, || quote!(self.#index)); + let right_side = attr.get_action_or(Some(index.to_token_stream()), ctx, || quote!(#obj #index)); quote!(other.#field_path = #right_side;) }, - (syn::Member::Unnamed(_), Some(attr), Kind::FromOwned | Kind::FromRef, _) =>{ - let right_side = attr.get_stuff(get_field_path, ctx, || &f.member); + (syn::Member::Unnamed(_), Some(attr), Kind::FromOwned | Kind::FromRef, _) => { + let right_side = attr.get_stuff(&obj, get_field_path, ctx, || &f.member); quote!(#right_side,) } } } +fn render_enum_line( + v: &Variant, + ctx: &ImplContext, + hint: StructKindHint, + idx: usize +) -> TokenStream { + let attr = v.attrs.applicable_attr(&ctx.kind, &ctx.struct_attr.ty); + + let src = ctx.src_ty; + let dst = ctx.dst_ty; + + let ident = &v.ident; + + let variant_struct: Struct<'_> = Struct { + attrs: StructAttrs { + attrs: ctx.input.get_attrs().attrs.clone(), + ghost_attrs: vec![], + where_attrs: vec![], + children_attrs: vec![], + }, + ident, + generics: &Generics { + lt_token: None, + params: Punctuated::new(), + gt_token: None, + where_clause: None, + }, + fields: v.fields.clone(), + named_fields: v.named_fields, + }; + + let new_ctx = ImplContext { + input: &DataType::Struct(&variant_struct), + destructured_src: true, + ..*ctx + }; + + let destr = if variant_struct.fields.is_empty() { TokenStream::new() } else { variant_destruct_block(&new_ctx) }; + let init = if variant_struct.fields.is_empty() { TokenStream::new() } else { struct_init_block(&new_ctx) }; + + match (v.named_fields, attr, &ctx.kind, hint) { + (_, None, Kind::FromOwned | Kind::FromRef, StructKindHint::Unspecified) => { + quote!(#src::#ident #destr => #dst::#ident #init,) + }, + (_, None, Kind::OwnedInto | Kind::RefInto, StructKindHint::Unspecified) => { + quote!(#src::#ident #destr => #dst::#ident #init,) + }, + _ => todo!() + } +} + fn render_ghost_line(ghost_data: &GhostData, ctx: &ImplContext) -> TokenStream { let ch = match &ghost_data.child_path { Some(ghost_data) => { @@ -600,8 +767,8 @@ fn quote_action(action: &TokenStream, field_path: Option, ctx: &Imp fn quote_from_trait(ctx: &ImplContext, pre_init: Option, init: TokenStream) -> TokenStream { let dst = ctx.dst_ty; let src = ctx.src_ty; - let gens = ctx.input.generics.to_token_stream(); - let where_clause = ctx.input.attrs.where_attr(&ctx.struct_attr.ty).map(|x| { + let gens = ctx.input.get_generics().to_token_stream(); + let where_clause = ctx.input.get_attrs().where_attr(&ctx.struct_attr.ty).map(|x| { let where_clause = x.where_clause.to_token_stream(); quote!(where #where_clause) }); @@ -619,8 +786,8 @@ fn quote_from_trait(ctx: &ImplContext, pre_init: Option, init: Toke fn quote_into_trait(ctx: &ImplContext, pre_init: Option, init: TokenStream, post_init: Option) -> TokenStream { let dst = ctx.dst_ty; let src = ctx.src_ty; - let gens = ctx.input.generics.to_token_stream(); - let where_clause = ctx.input.attrs.where_attr(&ctx.struct_attr.ty).map(|x|{ + let gens = ctx.input.get_generics().to_token_stream(); + let where_clause = ctx.input.get_attrs().where_attr(&ctx.struct_attr.ty).map(|x|{ let where_clause = x.where_clause.to_token_stream(); quote!(where #where_clause) }); @@ -650,8 +817,8 @@ fn quote_into_trait(ctx: &ImplContext, pre_init: Option, init: Toke fn quote_into_existing_trait(ctx: &ImplContext, pre_init: Option, init: TokenStream, post_init: Option) -> TokenStream { let dst = ctx.dst_ty; let src = ctx.src_ty; - let gens = ctx.input.generics.to_token_stream(); - let where_clause = ctx.input.attrs.where_attr(&ctx.struct_attr.ty).map(|x|{ + let gens = ctx.input.get_generics().to_token_stream(); + let where_clause = ctx.input.get_attrs().where_attr(&ctx.struct_attr.ty).map(|x|{ let where_clause = x.where_clause.to_token_stream(); quote!(where #where_clause) }); @@ -704,14 +871,14 @@ impl<'a> ApplicableAttr<'a> { } } - fn get_stuff TokenStream, F2: Fn() -> &'a Member>(&self, field_path: F1, ctx: &ImplContext, or: F2) -> TokenStream { + fn get_stuff TokenStream, F2: Fn() -> &'a Member>(&self, obj: &TokenStream, field_path: F1, ctx: &ImplContext, or: F2) -> TokenStream { match self { ApplicableAttr::Field(field_attr) => { match (&field_attr.member, &field_attr.action) { (Some(ident), Some(action)) => quote_action(action, Some(field_path(ident)), ctx), (Some(ident), None) => { let field_path = field_path(ident); - quote!(value.#field_path) + quote!(#obj #field_path) }, (None, Some(action)) => quote_action(action, Some(field_path(or())), ctx), _ => panic!("weird") diff --git a/o2o-impl/src/validate.rs b/o2o-impl/src/validate.rs index 2ed1b52..142351f 100644 --- a/o2o-impl/src/validate.rs +++ b/o2o-impl/src/validate.rs @@ -2,37 +2,40 @@ use std::collections::{HashMap, HashSet}; use proc_macro2::Span; use quote::ToTokens; use syn::{spanned::Spanned, Result}; -use crate::{ast::Struct, attr::{ChildrenAttr, FieldChildAttr, Kind, StructAttrCore, StructAttrs, StructGhostAttr, StructKindHint, TypePath, WhereAttr}}; +use crate::{ast::{DataType, Struct}, attr::{ChildrenAttr, FieldChildAttr, Kind, StructAttrCore, StructAttrs, StructGhostAttr, StructKindHint, TypePath, WhereAttr}}; -pub(crate) fn validate(input: &Struct) -> Result<()> { +pub(crate) fn validate(input: &DataType) -> Result<()> { + let attrs = input.get_attrs(); let mut errors: HashMap = HashMap::new(); - if input.attrs.attrs.is_empty() { + if attrs.attrs.is_empty() { errors.insert("At least one trait instruction is expected.".into(), Span::call_site()); } - validate_struct_attrs(input.attrs.iter_for_kind(&Kind::FromOwned), &mut errors); - validate_struct_attrs(input.attrs.iter_for_kind(&Kind::FromRef), &mut errors); - validate_struct_attrs(input.attrs.iter_for_kind(&Kind::OwnedInto), &mut errors); - validate_struct_attrs(input.attrs.iter_for_kind(&Kind::RefInto), &mut errors); - validate_struct_attrs(input.attrs.iter_for_kind(&Kind::OwnedIntoExisting), &mut errors); - validate_struct_attrs(input.attrs.iter_for_kind(&Kind::RefIntoExisting), &mut errors); + validate_struct_attrs(attrs.iter_for_kind(&Kind::FromOwned), &mut errors); + validate_struct_attrs(attrs.iter_for_kind(&Kind::FromRef), &mut errors); + validate_struct_attrs(attrs.iter_for_kind(&Kind::OwnedInto), &mut errors); + validate_struct_attrs(attrs.iter_for_kind(&Kind::RefInto), &mut errors); + validate_struct_attrs(attrs.iter_for_kind(&Kind::OwnedIntoExisting), &mut errors); + validate_struct_attrs(attrs.iter_for_kind(&Kind::RefIntoExisting), &mut errors); - let type_paths = input.attrs.attrs.iter() + let type_paths = attrs.attrs.iter() .map(|x| &x.attr.ty) .collect::>(); - validate_ghost_attrs(&Kind::FromOwned, &input.attrs.ghost_attrs, &type_paths, &mut errors); - validate_ghost_attrs(&Kind::FromRef, &input.attrs.ghost_attrs, &type_paths, &mut errors); - validate_ghost_attrs(&Kind::OwnedInto, &input.attrs.ghost_attrs, &type_paths, &mut errors); - validate_ghost_attrs(&Kind::RefInto, &input.attrs.ghost_attrs, &type_paths, &mut errors); - validate_ghost_attrs(&Kind::OwnedIntoExisting, &input.attrs.ghost_attrs, &type_paths, &mut errors); - validate_ghost_attrs(&Kind::RefIntoExisting, &input.attrs.ghost_attrs, &type_paths, &mut errors); + validate_ghost_attrs(&Kind::FromOwned, &attrs.ghost_attrs, &type_paths, &mut errors); + validate_ghost_attrs(&Kind::FromRef, &attrs.ghost_attrs, &type_paths, &mut errors); + validate_ghost_attrs(&Kind::OwnedInto, &attrs.ghost_attrs, &type_paths, &mut errors); + validate_ghost_attrs(&Kind::RefInto, &attrs.ghost_attrs, &type_paths, &mut errors); + validate_ghost_attrs(&Kind::OwnedIntoExisting, &attrs.ghost_attrs, &type_paths, &mut errors); + validate_ghost_attrs(&Kind::RefIntoExisting, &attrs.ghost_attrs, &type_paths, &mut errors); - validate_children_attrs(&input.attrs.children_attrs, &type_paths, &mut errors); - validate_where_attrs(&input.attrs.where_attrs, &type_paths, &mut errors); + validate_children_attrs(&attrs.children_attrs, &type_paths, &mut errors); + validate_where_attrs(&attrs.where_attrs, &type_paths, &mut errors); - validate_fields(input, &input.attrs, &type_paths, &mut errors); + if let DataType::Struct(s) = input { + validate_fields(s, &attrs, &type_paths, &mut errors); + } if errors.is_empty() { Ok(()) diff --git a/o2o-tests/tests/27_enum_bare_top_level_attr_tests.rs b/o2o-tests/tests/27_enum_bare_top_level_attr_tests.rs new file mode 100644 index 0000000..322aa0d --- /dev/null +++ b/o2o-tests/tests/27_enum_bare_top_level_attr_tests.rs @@ -0,0 +1,61 @@ +#[derive(PartialEq, Eq, Clone)] +enum Enum { + Item1, + Item2, +} + +#[derive(PartialEq, Eq, Clone, o2o::o2o)] +#[map(Enum)] +enum EnumDto { + Item1, + Item2, +} + +#[derive(PartialEq, Eq)] +enum EnumWithData { + Item1(i32, i16), + Item2 { str: String, i: i32 }, +} + +#[derive(Clone, PartialEq, Eq, o2o::o2o)] +#[map_owned(EnumWithData)] +enum EnumWithDataDto { + Item1(i32, i16), + Item2 { str: String, i: i32 }, +} + +#[test] +fn named2named() { + for data in vec![ + (EnumDto::Item1, Enum::Item1), + (EnumDto::Item2, Enum::Item2) + ] { + let dto_ref = &data.0; + let en: Enum = dto_ref.into(); + assert!(en == data.1); + + let en: Enum = data.0.clone().into(); + assert!(en == data.1); + + let en_ref = &data.1; + let dto: EnumDto = en_ref.into(); + assert!(dto == data.0); + + let dto: EnumDto = data.1.into(); + assert!(dto == data.0); + } +} + +#[test] +fn named2named_with_data() { + for data in vec![ + (EnumWithDataDto::Item1(123, 321), EnumWithData::Item1(123, 321)), + (EnumWithDataDto::Item2 { str: "Test".into(), i: 654 }, EnumWithData::Item2 { str: "Test".into(), i: 654 }) + ] { + let en: EnumWithData = data.0.clone().into(); + assert!(en == data.1); + + let dto: EnumWithDataDto = data.1.into(); + assert!(dto == data.0); + } +} \ No newline at end of file diff --git a/o2o-tests/tests/28_enum_bare_top_level_attr_tests.rs b/o2o-tests/tests/28_enum_bare_top_level_attr_tests.rs new file mode 100644 index 0000000..e373269 --- /dev/null +++ b/o2o-tests/tests/28_enum_bare_top_level_attr_tests.rs @@ -0,0 +1,30 @@ +#[derive(PartialEq, Eq)] +enum EnumWithData { + Item1(i32, i16), + Item2 { str: String, i: i32 }, +} + +#[derive(Clone, PartialEq, Eq, o2o::o2o)] +#[map_owned(EnumWithData)] +enum EnumWithDataDto { + Item1(#[map(1)]i16, #[map(0)]i32), + Item2 { + #[map(str)] + string: String, + i: i32 + }, +} + +#[test] +fn named2named_with_data() { + for data in vec![ + (EnumWithDataDto::Item1(123, 321), EnumWithData::Item1(321, 123)), + (EnumWithDataDto::Item2 { string: "Test".into(), i: 654 }, EnumWithData::Item2 { str: "Test".into(), i: 654 }) + ] { + let en: EnumWithData = data.0.clone().into(); + assert!(en == data.1); + + let dto: EnumWithDataDto = data.1.into(); + assert!(dto == data.0); + } +} \ No newline at end of file diff --git a/o2o-tests/tests/29_enums_playground.rs b/o2o-tests/tests/29_enums_playground.rs new file mode 100644 index 0000000..3d5ddbe --- /dev/null +++ b/o2o-tests/tests/29_enums_playground.rs @@ -0,0 +1,124 @@ +enum EnumWithData { + Item1(String), + Item2(i32, i16), +} + +enum EnumWithDataDto { + Item1(String), + Item2(i32, i16), +} + +impl From for EnumWithDataDto { + fn from(value: EnumWithData) -> EnumWithDataDto { + match value { + EnumWithData::Item1(str) => EnumWithDataDto::Item1(str), + EnumWithData::Item2(i, i2) => EnumWithDataDto::Item2(i, i2), + } + } +} + +// impl Into for EnumWithDataDto { +// fn into(self) -> EnumWithData { +// match self { +// EnumWithDataDto::Item1(str) => EnumWithData::Item1(str), +// EnumWithDataDto::Item2(i) => EnumWithData::Item2(i), +// } +// } +// } + +// impl From<&EnumWithData> for EnumWithDataDto { +// fn from(value: &EnumWithData) -> EnumWithDataDto { +// match value { +// EnumWithData::Item1(str) => EnumWithDataDto::Item1(str.clone()), +// EnumWithData::Item2(i) => EnumWithDataDto::Item2(*i), +// } +// } +// } + +enum Test { + Opt1(i32, String), + Opt2 { val: i32, str: String }, + Opt3, + Opt4, + Opt5 +} + +enum Test2 { + Opt1(i32, String), + Opt2 { val: i32, str: String}, + SubOpt(Test123) +} + +enum Test123 { + Opt3, + SubOpt(Test321) +} + +enum Test321 { + Opt4, + Opt5 +} + +impl From for Test2 { + fn from(value: Test) -> Self { + match value { + Test::Opt1(i, s) => Test2::Opt1(i, s), + Test::Opt2 { val, str } => Test2::Opt2 { val, str }, + Test::Opt3 => Test2::SubOpt(Test123::Opt3), + Test::Opt4 => Test2::SubOpt(Test123::SubOpt(Test321::Opt4)), + Test::Opt5 => Test2::SubOpt(Test123::SubOpt(Test321::Opt5)), + } + } +} + +impl Into for Test2 { + fn into(self) -> Test { + match self { + Test2::Opt1(i, s) => Test::Opt1(i, s), + Test2::Opt2 { val, str } => Test::Opt2 { val, str }, + Test2::SubOpt(sub) => match sub { + Test123::Opt3 => Test::Opt3, + Test123::SubOpt(sub) => match sub { + Test321::Opt4 => Test::Opt4, + Test321::Opt5 => Test::Opt5, + }, + }, + } + } +} + +#[derive(Default/*, o2o::o2o */)] +struct Entity { + item_1: String, + item_2: i32, + item_3: i32 +} + +#[derive(Default/*, o2o::o2o */)] +//#[map_owned(Entity| update: { Default::default() }, test: {"123"})] +//#[o2o(update_with(Default::default()))] +struct EntityDto { + //#[map(~.clone())] + item_1: String, + item_2: i32, + //#[ghost({123})] + +} + +impl std::convert::From for EntityDto { + fn from(value: Entity) -> EntityDto { + EntityDto { + item_1: value.item_1.clone(), + item_2: value.item_2 + } + } +} +impl std::convert::Into for EntityDto { + fn into(self) -> Entity { + Entity { + item_1: self.item_1.clone(), + item_2: self.item_2, + ..Default::default() + } + } +} \ No newline at end of file From 8a29758b89b4e28081c649fa18fc77ea5eaa7894 Mon Sep 17 00:00:00 2001 From: Artem Romanenia Date: Fri, 22 Mar 2024 18:16:48 +0300 Subject: [PATCH 2/3] Enums in progress --- README.md | 42 +++++- o2o-impl/src/ast.rs | 53 +++----- o2o-impl/src/attr.rs | 114 ++++++++-------- o2o-impl/src/expand.rs | 61 ++++++--- o2o-impl/src/validate.rs | 12 +- .../27_enum_bare_top_level_attr_tests.rs | 4 +- .../28_enum_bare_top_level_attr_tests.rs | 30 ----- .../28_enum_member_level_ident_only_tests.rs | 67 ++++++++++ ...num_member_level_inline_expr_only_tests.rs | 36 +++++ o2o-tests/tests/29_enums_playground.rs | 124 ------------------ ...ember_level_ident_and_inline_expr_tests.rs | 36 +++++ 11 files changed, 300 insertions(+), 279 deletions(-) delete mode 100644 o2o-tests/tests/28_enum_bare_top_level_attr_tests.rs create mode 100644 o2o-tests/tests/28_enum_member_level_ident_only_tests.rs create mode 100644 o2o-tests/tests/29_enum_member_level_inline_expr_only_tests.rs delete mode 100644 o2o-tests/tests/29_enums_playground.rs create mode 100644 o2o-tests/tests/30_enum_member_level_ident_and_inline_expr_tests.rs diff --git a/README.md b/README.md index f32fdce..7148a49 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,29 @@ let dto = PersonDto { id: 321, name: "Jack".into(), age: 23 }; let person: Person = dto.into(); assert_eq!(person.id, 321); assert_eq!(person.name, "Jack"); assert_eq!(person.age, 23); + +// Starting from v0.4.2 o2o also supports enums: + +enum Creature { + Person(Person), + Cat { nickname: String }, + Dog(String), + Other +} + +#[derive(o2o)] +#[from_owned(Creature)] +enum CreatureDto { + Person(#[map(~.into())] PersonDto), + Cat { nickname: String }, + Dog(String), + Other +} + +let creature = Creature::Cat { nickname: "Floppa".into() }; +let dto: CreatureDto = creature.into(); + +if let CreatureDto::Cat { nickname } = dto { assert_eq!(nickname, "Floppa"); } else { assert!(false) } ``` And here's the code that `o2o` generates (from here on, generated code is produced by [rust-analyzer: Expand macro recursively](https://rust-analyzer.github.io/manual.html#expand-macro-recursively) command): @@ -78,6 +101,17 @@ And here's the code that `o2o` generates (from here on, generated code is produc } } } + + impl std::convert::From for CreatureDto { + fn from(value: Creature) -> CreatureDto { + match value { + Creature::Person(f0) => CreatureDto::Person(f0.into()), + Creature::Cat { nickname } => CreatureDto::Cat { nickname: nickname }, + Creature::Dog(f0) => CreatureDto::Dog(f0), + Creature::Other => CreatureDto::Other, + } + } + } ``` @@ -107,8 +141,8 @@ And here's the code that `o2o` generates (from here on, generated code is produc - [Additional o2o instruction available via `#[o2o(...)]` syntax](#additional-o2o-instruction-available-via-o2o-syntax) - [Primitive type conversions](#primitive-type-conversions) - [Repeat instructions](#repeat-instructions) - - [Contributions](#contributions) - - [License](#license) +- [Contributions](#contributions) +- [License](#license) ## Traits and `o2o` *trait instructions* @@ -1518,11 +1552,11 @@ struct CarDto { ``` -### Contributions +## Contributions All issues, questions, pull requests are extremely welcome. -#### License +## License Licensed under either an Apache License, Version diff --git a/o2o-impl/src/ast.rs b/o2o-impl/src/ast.rs index ed19609..dfc28ac 100644 --- a/o2o-impl/src/ast.rs +++ b/o2o-impl/src/ast.rs @@ -1,12 +1,12 @@ use crate::attr::{self}; -use crate::attr::{FieldAttrs, StructAttrs}; +use crate::attr::{MemberAttrs, DataTypeAttrs}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::Comma; use syn::{Attribute, DataEnum, DataStruct, DeriveInput, Fields, Generics, Ident, Index, Member, Result}; pub(crate) struct Struct<'a> { - pub attrs: StructAttrs, + pub attrs: DataTypeAttrs, pub ident: &'a Ident, pub generics: &'a Generics, pub fields: Vec, @@ -29,7 +29,7 @@ impl<'a> Struct<'a> { #[derive(Clone)] pub(crate) struct Field { - pub attrs: FieldAttrs, + pub attrs: MemberAttrs, pub idx: usize, pub member: Member, } @@ -78,7 +78,7 @@ impl<'a> Field { } pub(crate) struct Enum<'a> { - pub attrs: StructAttrs, + pub attrs: DataTypeAttrs, pub ident: &'a Ident, pub generics: &'a Generics, pub variants: Vec @@ -98,9 +98,9 @@ impl<'a> Enum<'a> { } pub(crate) struct Variant { - pub attrs: FieldAttrs, + pub attrs: MemberAttrs, pub ident: Ident, - pub idx: usize, + _idx: usize, pub fields: Vec, pub named_fields: bool, } @@ -137,9 +137,9 @@ impl<'a> Variant { fn from_syn(i: usize, variant: &'a syn::Variant, bark: bool) -> Result { let fields = Field::multiple_from_syn(&variant.fields, bark)?; Ok(Variant { - attrs: attr::get_field_attrs(SynDataTypeMember::Variant(&variant), bark)?, + attrs: attr::get_field_attrs(SynDataTypeMember::Variant(variant), bark)?, ident: variant.ident.clone(), - idx: i, + _idx: i, fields, named_fields: matches!(&variant.fields, Fields::Named(_)), }) @@ -154,12 +154,12 @@ pub(crate) enum DataType<'a> { impl<'a> DataType<'a> { pub fn get_ident(&'a self) -> &Ident { match self { - DataType::Struct(s) => &s.ident, - DataType::Enum(e) => &e.ident + DataType::Struct(s) => s.ident, + DataType::Enum(e) => e.ident } } - pub fn get_attrs(&'a self) -> &'a StructAttrs { + pub fn get_attrs(&'a self) -> &'a DataTypeAttrs { match self { DataType::Struct(s) => &s.attrs, DataType::Enum(e) => &e.attrs @@ -168,38 +168,17 @@ impl<'a> DataType<'a> { pub fn get_members(&'a self) -> Vec { match self { - DataType::Struct(s) => s.fields.iter().map(|x| DataTypeMember::Field(x)).collect(), - DataType::Enum(e) => e.variants.iter().map(|x| DataTypeMember::Variant(x)).collect() + DataType::Struct(s) => s.fields.iter().map(DataTypeMember::Field).collect(), + DataType::Enum(e) => e.variants.iter().map(DataTypeMember::Variant).collect() } } pub fn get_generics(&'a self) -> &'a Generics { match self { - DataType::Struct(s) => &s.generics, - DataType::Enum(e) => &e.generics + DataType::Struct(s) => s.generics, + DataType::Enum(e) => e.generics } } - - // pub fn get_field(&'a self, ty: &TypePath) -> Option<&Field> { - // match self { - // DataType::Struct(s) => s.fields.iter().find(|f| - // f.attrs.field_attr_core(&Kind::OwnedInto, ty) - // .filter(|&g| g.wrapper).is_some() - // ), - // DataType::Enum(e) => None - // } - // } - - // pub fn get_field_with_action(&'a self, ty: &TypePath) -> Option<(&Field, &Option)> { - // match self { - // DataType::Struct(s) => s.fields.iter().find_map(|f| - // f.attrs.field_attr_core(&Kind::RefInto, ty) - // .filter(|&g| g.wrapper) - // .map(|x| (f, &x.action)) - // ), - // DataType::Enum(e) => None - // } - // } } pub(crate) enum SynDataTypeMember<'a> { @@ -223,7 +202,7 @@ pub(crate) enum DataTypeMember<'a> { } impl<'a> DataTypeMember<'a> { - pub fn get_attrs(&'a self) -> &'a FieldAttrs { + pub fn get_attrs(&'a self) -> &'a MemberAttrs { match self { DataTypeMember::Field(f) => &f.attrs, DataTypeMember::Variant(v) => &v.attrs diff --git a/o2o-impl/src/attr.rs b/o2o-impl/src/attr.rs index b2e7489..2012d5a 100644 --- a/o2o-impl/src/attr.rs +++ b/o2o-impl/src/attr.rs @@ -39,8 +39,8 @@ impl OptionalParenthesizedTokenStream { } enum StructInstruction { - Map(StructAttr), - Ghost(StructGhostAttr), + Map(TraitAttr), + Ghost(GhostsAttr), Where(WhereAttr), Children(ChildrenAttr), AllowUnknown, @@ -48,9 +48,9 @@ enum StructInstruction { } enum MemberInstruction { - Map(FieldAttr), - Ghost(FieldGhostAttr), - Child(FieldChildAttr), + Map(MemberAttr), + Ghost(GhostAttr), + Child(ChildAttr), Parent(ParentAttr), As(AsAttr), Repeat(RepeatFor), @@ -152,15 +152,15 @@ impl Index<&Kind> for ApplicableTo { } } -pub(crate) struct StructAttrs { - pub attrs: Vec, - pub ghost_attrs: Vec, +pub(crate) struct DataTypeAttrs { + pub attrs: Vec, + pub ghost_attrs: Vec, pub where_attrs: Vec, pub children_attrs: Vec, } -impl<'a> StructAttrs { - pub(crate) fn iter_for_kind(&'a self, kind: &'a Kind) -> impl Iterator { +impl<'a> DataTypeAttrs { + pub(crate) fn iter_for_kind(&'a self, kind: &'a Kind) -> impl Iterator { self.attrs.iter().filter(move |x| x.applicable_to[kind]).map(|x| &x.attr) } @@ -231,21 +231,21 @@ impl Parse for RepeatForWrap { } #[derive(Clone, Default)] -pub(crate) struct FieldAttrs { - pub attrs: Vec, - pub child_attrs: Vec, +pub(crate) struct MemberAttrs { + pub attrs: Vec, + pub child_attrs: Vec, pub parent_attrs: Vec, - pub ghost_attrs: Vec, + pub ghost_attrs: Vec, pub repeat: Option, pub stop_repeat: bool, } -impl<'a> FieldAttrs { - pub(crate) fn iter_for_kind(&'a self, kind: &'a Kind) -> impl Iterator { +impl<'a> MemberAttrs { + pub(crate) fn iter_for_kind(&'a self, kind: &'a Kind) -> impl Iterator { self.attrs.iter().filter(move |x| x.applicable_to[kind]) } - pub(crate) fn iter_for_kind_core(&'a self, kind: &'a Kind) -> impl Iterator { + pub(crate) fn iter_for_kind_core(&'a self, kind: &'a Kind) -> impl Iterator { self.iter_for_kind(kind).map(|x| &x.attr) } @@ -258,13 +258,13 @@ impl<'a> FieldAttrs { .map(ApplicableAttr::Field)) } - pub(crate) fn applicable_field_attr(&'a self, kind: &'a Kind, container_ty: &TypePath) -> Option<&'a FieldAttr> { + pub(crate) fn applicable_field_attr(&'a self, kind: &'a Kind, container_ty: &TypePath) -> Option<&'a MemberAttr> { self.field_attr(kind, container_ty) .or_else(|| if kind == &Kind::OwnedIntoExisting { self.field_attr(&Kind::OwnedInto, container_ty) } else { None }) .or_else(|| if kind == &Kind::RefIntoExisting { self.field_attr(&Kind::RefInto, container_ty) } else { None }) } - pub(crate) fn child(&'a self, container_ty: &TypePath) -> Option<&FieldChildAttr>{ + pub(crate) fn child(&'a self, container_ty: &TypePath) -> Option<&ChildAttr>{ self.child_attrs.iter() .find(|x| x.container_ty.is_some() && x.container_ty.as_ref().unwrap() == container_ty) .or_else(|| self.child_attrs.iter().find(|x| x.container_ty.is_none())) @@ -281,13 +281,13 @@ impl<'a> FieldAttrs { .any(|x| x.container_ty.is_none() || x.container_ty.as_ref().unwrap() == container_ty) } - pub(crate) fn field_attr(&'a self, kind: &'a Kind, container_ty: &TypePath) -> Option<&FieldAttr>{ + pub(crate) fn field_attr(&'a self, kind: &'a Kind, container_ty: &TypePath) -> Option<&MemberAttr>{ self.iter_for_kind(kind) .find(|x| x.attr.container_ty.is_some() && x.attr.container_ty.as_ref().unwrap() == container_ty) .or_else(|| self.iter_for_kind(kind).find(|x| x.attr.container_ty.is_none())) } - pub(crate) fn field_attr_core(&'a self, kind: &'a Kind, container_ty: &TypePath) -> Option<&FieldAttrCore>{ + pub(crate) fn field_attr_core(&'a self, kind: &'a Kind, container_ty: &TypePath) -> Option<&MemberAttrCore>{ self.iter_for_kind_core(kind) .find(|x| x.container_ty.is_some() && x.container_ty.as_ref().unwrap() == container_ty) .or_else(|| self.iter_for_kind_core(kind).find(|x| x.container_ty.is_none())) @@ -319,13 +319,13 @@ pub(crate) enum StructKindHint { } #[derive(Clone)] -pub(crate) struct StructAttr { - pub attr: StructAttrCore, +pub(crate) struct TraitAttr { + pub attr: TraitAttrCore, pub applicable_to: ApplicableTo, } #[derive(Clone)] -pub(crate) struct StructAttrCore { +pub(crate) struct TraitAttrCore { pub ty: TypePath, pub struct_kind_hint: StructKindHint, pub init_data: Option>, @@ -333,7 +333,7 @@ pub(crate) struct StructAttrCore { pub quick_return: Option } -impl Parse for StructAttrCore { +impl Parse for TraitAttrCore { fn parse(input: ParseStream) -> Result { let ty: TypePath = if input.peek(Paren) { let content; @@ -344,7 +344,7 @@ impl Parse for StructAttrCore { let struct_kind_hint = if ty.nameless_tuple { StructKindHint::Tuple } else { try_parse_struct_kind_hint(input)? }; if !input.peek(Token![|]){ - return Ok(StructAttrCore { ty, struct_kind_hint, init_data: None, update: None, quick_return: None }) + return Ok(TraitAttrCore { ty, struct_kind_hint, init_data: None, update: None, quick_return: None }) } input.parse::()?; @@ -368,7 +368,7 @@ impl Parse for StructAttrCore { try_parse_action(input, true)? } else { None }; - Ok(StructAttrCore { ty, struct_kind_hint, init_data, update, quick_return }) + Ok(TraitAttrCore { ty, struct_kind_hint, init_data, update, quick_return }) } } @@ -389,7 +389,7 @@ impl Parse for InitData { } } -pub(crate) struct StructGhostAttr { +pub(crate) struct GhostsAttr { pub attr: StructGhostAttrCore, pub applicable_to: ApplicableTo, } @@ -509,22 +509,22 @@ impl Hash for ChildData { } #[derive(Clone)] -pub(crate) struct FieldAttr { - pub attr: FieldAttrCore, +pub(crate) struct MemberAttr { + pub attr: MemberAttrCore, pub original_instr: String, applicable_to: ApplicableTo, } #[derive(Clone)] -pub(crate) struct FieldAttrCore { +pub(crate) struct MemberAttrCore { pub container_ty: Option, pub member: Option, pub action: Option, } -impl Parse for FieldAttrCore { +impl Parse for MemberAttrCore { fn parse(input: ParseStream) -> Result { - Ok(FieldAttrCore { + Ok(MemberAttrCore { container_ty: try_parse_container_ident(input, false), member: try_parse_optional_ident(input), action: try_parse_action(input, true)?, @@ -546,7 +546,7 @@ impl Parse for ParentAttr { } #[derive(Clone)] -pub(crate) struct FieldGhostAttr { +pub(crate) struct GhostAttr { pub attr: FieldGhostAttrCore, pub applicable_to: ApplicableTo, } @@ -567,28 +567,28 @@ impl Parse for FieldGhostAttrCore { } pub(crate) enum ApplicableAttr<'a> { - Field(&'a FieldAttrCore), + Field(&'a MemberAttrCore), Ghost(&'a FieldGhostAttrCore), } #[derive(Clone)] -pub(crate) struct FieldChildAttr { +pub(crate) struct ChildAttr { pub container_ty: Option, pub child_path: ChildPath, } -impl FieldChildAttr { +impl ChildAttr { pub(crate) fn get_child_path_str(&self, depth: Option) -> &str { self.child_path.get_child_path_str(depth) } } -impl Parse for FieldChildAttr{ +impl Parse for ChildAttr{ fn parse(input: ParseStream) -> Result { let container_ty = try_parse_container_ident(input, false); let child_path: Punctuated = Punctuated::parse_separated_nonempty(input)?; let child_path_str = build_child_path_str(&child_path); - Ok(FieldChildAttr { + Ok(ChildAttr { container_ty, child_path: ChildPath { child_path, @@ -618,7 +618,7 @@ impl Parse for AsAttr { } } -pub(crate) fn get_struct_attrs(input: &[Attribute]) -> Result<(StructAttrs, bool)> { +pub(crate) fn get_struct_attrs(input: &[Attribute]) -> Result<(DataTypeAttrs, bool)> { let mut bark = true; let mut instrs: Vec = vec![]; @@ -646,8 +646,8 @@ pub(crate) fn get_struct_attrs(input: &[Attribute]) -> Result<(StructAttrs, bool instrs.push(parse_struct_instruction(instr, p.content(), false, bark)?); } } - let mut attrs: Vec = vec![]; - let mut ghost_attrs: Vec = vec![]; + let mut attrs: Vec = vec![]; + let mut ghost_attrs: Vec = vec![]; let mut where_attrs: Vec = vec![]; let mut children_attrs: Vec = vec![]; @@ -660,10 +660,10 @@ pub(crate) fn get_struct_attrs(input: &[Attribute]) -> Result<(StructAttrs, bool StructInstruction::AllowUnknown | StructInstruction::Unrecognized => (), }; } - Ok((StructAttrs {attrs, ghost_attrs, where_attrs, children_attrs }, bark)) + Ok((DataTypeAttrs {attrs, ghost_attrs, where_attrs, children_attrs }, bark)) } -pub(crate) fn get_field_attrs(input: SynDataTypeMember, bark: bool) -> Result { +pub(crate) fn get_field_attrs(input: SynDataTypeMember, bark: bool) -> Result { let mut instrs: Vec = vec![]; for x in input.get_attrs().iter() { if x.path.is_ident("doc"){ @@ -684,9 +684,9 @@ pub(crate) fn get_field_attrs(input: SynDataTypeMember, bark: bool) -> Result = vec![]; - let mut ghost_attrs: Vec = vec![]; - let mut attrs: Vec = vec![]; + let mut child_attrs: Vec = vec![]; + let mut ghost_attrs: Vec = vec![]; + let mut attrs: Vec = vec![]; let mut parent_attrs: Vec = vec![]; let mut repeat = None; let mut stop_repeat = false; @@ -707,7 +707,7 @@ pub(crate) fn get_field_attrs(input: SynDataTypeMember, bark: bool) -> Result () }; } - Ok(FieldAttrs { child_attrs, parent_attrs, attrs, ghost_attrs, repeat, stop_repeat }) + Ok(MemberAttrs { child_attrs, parent_attrs, attrs, ghost_attrs, repeat, stop_repeat }) } fn parse_struct_instruction(instr: &Ident, input: TokenStream, own_instr: bool, bark: bool) -> Result @@ -717,7 +717,7 @@ fn parse_struct_instruction(instr: &Ident, input: TokenStream, own_instr: bool, "allow_unknown" if own_instr => Ok(StructInstruction::AllowUnknown), "owned_into" | "ref_into" | "into" | "from_owned" | "from_ref" | "from" | "map_owned" | "map_ref" | "map" | "owned_into_existing" | "ref_into_existing" | "into_existing" => - Ok(StructInstruction::Map(StructAttr { + Ok(StructInstruction::Map(TraitAttr { attr: syn::parse2(input)?, applicable_to: [ appl_owned_into(instr_str), @@ -728,7 +728,7 @@ fn parse_struct_instruction(instr: &Ident, input: TokenStream, own_instr: bool, appl_ref_into_existing(instr_str) ] })), - "ghosts" | "ghosts_ref" | "ghosts_owned" => Ok(StructInstruction::Ghost(StructGhostAttr { + "ghosts" | "ghosts_ref" | "ghosts_owned" => Ok(StructInstruction::Ghost(GhostsAttr { attr: syn::parse2(input)?, applicable_to: [ appl_ghosts_owned(instr_str), @@ -759,7 +759,7 @@ fn parse_member_instruction(instr: &Ident, input: TokenStream, own_instr: bool, match instr_str.as_ref() { "owned_into" | "ref_into" | "into" | "from_owned" | "from_ref" | "from" | "map_owned" | "map_ref" | "map" | "owned_into_existing" | "ref_into_existing" | "into_existing" => - Ok(MemberInstruction::Map(FieldAttr { + Ok(MemberInstruction::Map(MemberAttr { attr: syn::parse2(input)?, original_instr: instr_str.clone(), applicable_to: [ @@ -771,7 +771,7 @@ fn parse_member_instruction(instr: &Ident, input: TokenStream, own_instr: bool, appl_ref_into_existing(instr_str) ] })), - "ghost" | "ghost_ref" | "ghost_owned" => Ok(MemberInstruction::Ghost(FieldGhostAttr { + "ghost" | "ghost_ref" | "ghost_owned" => Ok(MemberInstruction::Ghost(GhostAttr { attr: syn::parse2(input)?, applicable_to: [ appl_ghost_owned(instr_str), @@ -904,11 +904,11 @@ fn try_parse_braced_action(input: ParseStream) -> Result { content.parse::() } -fn add_as_type_attrs(input: &syn::Field, attr: AsAttr, attrs: &mut Vec) { +fn add_as_type_attrs(input: &syn::Field, attr: AsAttr, attrs: &mut Vec) { let this_ty = input.ty.to_token_stream(); let that_ty = attr.tokens; - attrs.push(FieldAttr { - attr: FieldAttrCore { + attrs.push(MemberAttr { + attr: MemberAttrCore { container_ty: attr.container_ty.clone(), member: attr.member.clone(), action: Some(quote!(~ as #this_ty)), @@ -916,8 +916,8 @@ fn add_as_type_attrs(input: &syn::Field, attr: AsAttr, attrs: &mut Vec Result { @@ -33,7 +33,7 @@ pub fn derive(node: &DeriveInput) -> Result { #[derive(Clone)] struct ImplContext<'a> { input: &'a DataType<'a>, - struct_attr: &'a StructAttrCore, + struct_attr: &'a TraitAttrCore, kind: Kind, dst_ty: &'a TokenStream, src_ty: &'a TokenStream, @@ -173,7 +173,7 @@ fn struct_impl(input: DataType) -> TokenStream { result } -fn main_code_block<'a>(ctx: &'a ImplContext) -> TokenStream { +fn main_code_block(ctx: &ImplContext) -> TokenStream { match ctx.input { DataType::Struct(_) => { let struct_init_block = struct_init_block(ctx); @@ -371,14 +371,14 @@ fn variant_destruct_block(ctx: &ImplContext<'_>) -> TokenStream { if s.named_fields { let idents = s.fields.iter().map(|x| { let attr = x.attrs.applicable_attr(&ctx.kind, &ctx.struct_attr.ty); + if !ctx.kind.is_from() || attr.is_none() { let ident = &x.member; quote!(#ident ,) - } else { - let attr = attr.unwrap(); + } else if let Some(attr) = attr { let ident = attr.get_field_name_or(&x.member); quote!(#ident ,) - } + } else { unreachable!() } }); quote!({#(#idents)*}) @@ -624,7 +624,8 @@ fn render_struct_line( quote!(#ident: #right_side,) }, (syn::Member::Unnamed(index), Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Tuple | StructKindHint::Unspecified) => { - let right_side = attr.get_action_or(Some(index.to_token_stream()), ctx, || quote!(#obj #index)); + let index = if ctx.destructured_src { Some(format_ident!("f{}", index).to_token_stream()) } else { Some(index.to_token_stream()) }; + let right_side = attr.get_action_or(index.clone(), ctx, || quote!(#obj #index)); quote!(#right_side,) }, (syn::Member::Unnamed(index), Some(attr), Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Tuple | StructKindHint::Unspecified) => { @@ -642,8 +643,9 @@ fn render_struct_line( let right_side = attr.get_action_or(Some(index.to_token_stream()), ctx, || quote!(#obj #index)); quote!(other.#field_path = #right_side;) }, - (syn::Member::Unnamed(_), Some(attr), Kind::FromOwned | Kind::FromRef, _) => { - let right_side = attr.get_stuff(&obj, get_field_path, ctx, || &f.member); + (syn::Member::Unnamed(index), Some(attr), Kind::FromOwned | Kind::FromRef, _) => { + let or = Member::Named(format_ident!("f{}", index)); + let right_side = attr.get_stuff(&obj, get_field_path, ctx, || if ctx.destructured_src { &or } else { &f.member}); quote!(#right_side,) } } @@ -653,7 +655,7 @@ fn render_enum_line( v: &Variant, ctx: &ImplContext, hint: StructKindHint, - idx: usize + _idx: usize ) -> TokenStream { let attr = v.attrs.applicable_attr(&ctx.kind, &ctx.struct_attr.ty); @@ -663,7 +665,7 @@ fn render_enum_line( let ident = &v.ident; let variant_struct: Struct<'_> = Struct { - attrs: StructAttrs { + attrs: DataTypeAttrs { attrs: ctx.input.get_attrs().attrs.clone(), ghost_attrs: vec![], where_attrs: vec![], @@ -690,11 +692,18 @@ fn render_enum_line( let init = if variant_struct.fields.is_empty() { TokenStream::new() } else { struct_init_block(&new_ctx) }; match (v.named_fields, attr, &ctx.kind, hint) { - (_, None, Kind::FromOwned | Kind::FromRef, StructKindHint::Unspecified) => { + (_, None, _, StructKindHint::Unspecified) => { quote!(#src::#ident #destr => #dst::#ident #init,) }, - (_, None, Kind::OwnedInto | Kind::RefInto, StructKindHint::Unspecified) => { - quote!(#src::#ident #destr => #dst::#ident #init,) + (_, Some(attr), Kind::FromOwned | Kind::FromRef, StructKindHint::Unspecified) => { + let member = Member::Named(ident.clone()); + let ident2 = attr.get_field_name_or(&member); + quote!(#src::#ident2 #destr => #dst::#ident #init,) + }, + (_, Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Unspecified) => { + let member = Member::Named(ident.clone()); + let ident2 = attr.get_field_name_or(&member); + quote!(#src::#ident #destr => #dst::#ident2 #init,) }, _ => todo!() } @@ -757,9 +766,13 @@ fn quote_action(action: &TokenStream, field_path: Option, ctx: &Imp Kind::FromOwned | Kind::FromRef => quote!(value), _ => quote!(self), }; - let path = match ctx.kind { - Kind::FromOwned | Kind::FromRef => quote!(value.#field_path), - _ => quote!(self.#field_path), + let path = if ctx.destructured_src { + quote!(#field_path) + } else { + match ctx.kind { + Kind::FromOwned | Kind::FromRef => quote!(value.#field_path), + _ => quote!(self.#field_path), + } }; replace_tilde_or_at_in_expr(action, Some(&ident), Some(&path)) } @@ -875,7 +888,17 @@ impl<'a> ApplicableAttr<'a> { match self { ApplicableAttr::Field(field_attr) => { match (&field_attr.member, &field_attr.action) { - (Some(ident), Some(action)) => quote_action(action, Some(field_path(ident)), ctx), + (Some(ident), Some(action)) => { + if let Member::Unnamed(index) = ident { + if ctx.destructured_src { + let ident = Member::Named(format_ident!("f{}", index)); + quote_action(action, Some(field_path(&ident)), ctx) + } else { quote_action(action, Some(field_path(ident)), ctx)} + } else { + quote_action(action, Some(field_path(ident)), ctx) + } + + }, (Some(ident), None) => { let field_path = field_path(ident); quote!(#obj #field_path) diff --git a/o2o-impl/src/validate.rs b/o2o-impl/src/validate.rs index 142351f..16d079d 100644 --- a/o2o-impl/src/validate.rs +++ b/o2o-impl/src/validate.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; use proc_macro2::Span; use quote::ToTokens; use syn::{spanned::Spanned, Result}; -use crate::{ast::{DataType, Struct}, attr::{ChildrenAttr, FieldChildAttr, Kind, StructAttrCore, StructAttrs, StructGhostAttr, StructKindHint, TypePath, WhereAttr}}; +use crate::{ast::{DataType, Struct}, attr::{ChildrenAttr, ChildAttr, Kind, TraitAttrCore, DataTypeAttrs, GhostsAttr, StructKindHint, TypePath, WhereAttr}}; pub(crate) fn validate(input: &DataType) -> Result<()> { let attrs = input.get_attrs(); @@ -34,7 +34,7 @@ pub(crate) fn validate(input: &DataType) -> Result<()> { validate_where_attrs(&attrs.where_attrs, &type_paths, &mut errors); if let DataType::Struct(s) = input { - validate_fields(s, &attrs, &type_paths, &mut errors); + validate_fields(s, attrs, &type_paths, &mut errors); } if errors.is_empty() { @@ -48,7 +48,7 @@ pub(crate) fn validate(input: &DataType) -> Result<()> { } } -fn validate_struct_attrs<'a, I: Iterator>(attrs: I, errors: &mut HashMap) { +fn validate_struct_attrs<'a, I: Iterator>(attrs: I, errors: &mut HashMap) { let mut unique_ident = HashSet::new(); for attr in attrs { if !unique_ident.insert(&attr.ty) { @@ -57,7 +57,7 @@ fn validate_struct_attrs<'a, I: Iterator>(attrs: I, e } } -fn validate_ghost_attrs(kind: &Kind, ghost_attrs: &[StructGhostAttr], type_paths: &HashSet<&TypePath>, errors: &mut HashMap) { +fn validate_ghost_attrs(kind: &Kind, ghost_attrs: &[GhostsAttr], type_paths: &HashSet<&TypePath>, errors: &mut HashMap) { if ghost_attrs.iter().filter(|x|x.applicable_to[kind] && x.attr.container_ty.is_none()).count() > 1 { errors.insert("There can be at most one default #[ghosts(...)] instruction.".into(), Span::call_site()); } @@ -121,7 +121,7 @@ fn validate_where_attrs(where_attrs: &[WhereAttr], type_paths: &HashSet<&TypePat } } -fn validate_fields(input: &Struct, struct_attrs: &StructAttrs, type_paths: &HashSet<&TypePath>, errors: &mut HashMap) { +fn validate_fields(input: &Struct, struct_attrs: &DataTypeAttrs, type_paths: &HashSet<&TypePath>, errors: &mut HashMap) { for field in &input.fields { for field_attr in &field.attrs.attrs { if let Some(tp) = &field_attr.attr.container_ty { @@ -228,7 +228,7 @@ fn validate_fields(input: &Struct, struct_attrs: &StructAttrs, type_paths: &Hash } } -fn check_child_errors(child_attr: &FieldChildAttr, struct_attrs: &StructAttrs, tp: &TypePath, errors: &mut HashMap) { +fn check_child_errors(child_attr: &ChildAttr, struct_attrs: &DataTypeAttrs, tp: &TypePath, errors: &mut HashMap) { let children_attr = struct_attrs.children_attr(tp); for (idx, _level) in child_attr.child_path.child_path.iter().enumerate() { let path = child_attr.get_child_path_str(Some(idx)); diff --git a/o2o-tests/tests/27_enum_bare_top_level_attr_tests.rs b/o2o-tests/tests/27_enum_bare_top_level_attr_tests.rs index 322aa0d..641bea3 100644 --- a/o2o-tests/tests/27_enum_bare_top_level_attr_tests.rs +++ b/o2o-tests/tests/27_enum_bare_top_level_attr_tests.rs @@ -25,7 +25,7 @@ enum EnumWithDataDto { } #[test] -fn named2named() { +fn enum2enum() { for data in vec![ (EnumDto::Item1, Enum::Item1), (EnumDto::Item2, Enum::Item2) @@ -47,7 +47,7 @@ fn named2named() { } #[test] -fn named2named_with_data() { +fn enum2enum_with_data() { for data in vec![ (EnumWithDataDto::Item1(123, 321), EnumWithData::Item1(123, 321)), (EnumWithDataDto::Item2 { str: "Test".into(), i: 654 }, EnumWithData::Item2 { str: "Test".into(), i: 654 }) diff --git a/o2o-tests/tests/28_enum_bare_top_level_attr_tests.rs b/o2o-tests/tests/28_enum_bare_top_level_attr_tests.rs deleted file mode 100644 index e373269..0000000 --- a/o2o-tests/tests/28_enum_bare_top_level_attr_tests.rs +++ /dev/null @@ -1,30 +0,0 @@ -#[derive(PartialEq, Eq)] -enum EnumWithData { - Item1(i32, i16), - Item2 { str: String, i: i32 }, -} - -#[derive(Clone, PartialEq, Eq, o2o::o2o)] -#[map_owned(EnumWithData)] -enum EnumWithDataDto { - Item1(#[map(1)]i16, #[map(0)]i32), - Item2 { - #[map(str)] - string: String, - i: i32 - }, -} - -#[test] -fn named2named_with_data() { - for data in vec![ - (EnumWithDataDto::Item1(123, 321), EnumWithData::Item1(321, 123)), - (EnumWithDataDto::Item2 { string: "Test".into(), i: 654 }, EnumWithData::Item2 { str: "Test".into(), i: 654 }) - ] { - let en: EnumWithData = data.0.clone().into(); - assert!(en == data.1); - - let dto: EnumWithDataDto = data.1.into(); - assert!(dto == data.0); - } -} \ No newline at end of file diff --git a/o2o-tests/tests/28_enum_member_level_ident_only_tests.rs b/o2o-tests/tests/28_enum_member_level_ident_only_tests.rs new file mode 100644 index 0000000..9c3d246 --- /dev/null +++ b/o2o-tests/tests/28_enum_member_level_ident_only_tests.rs @@ -0,0 +1,67 @@ +#[derive(PartialEq, Eq)] +enum Enum { + Item1, + Item2, +} + +#[derive(Clone, PartialEq, Eq, o2o::o2o)] +#[map(Enum)] +enum EnumDto { + Item1, + #[map(Item2)] + Item2Dto, +} + +#[derive(PartialEq, Eq)] +enum EnumWithData { + Item1(i32, i16), + Item2 { str: String, i: i32 }, +} + +#[derive(Clone, PartialEq, Eq, o2o::o2o)] +#[map_owned(EnumWithData)] +enum EnumWithDataDto { + #[map(Item1)] + Item1Dto(i32, i16), + Item2 { + #[map(str)] + string: String, + i: i32 + }, +} + +#[test] +fn enum2enum() { + for data in vec![ + (EnumDto::Item1, Enum::Item1), + (EnumDto::Item2Dto, Enum::Item2) + ] { + let dto_ref = &data.0; + let en: Enum = dto_ref.into(); + assert!(en == data.1); + + let en: Enum = data.0.clone().into(); + assert!(en == data.1); + + let en_ref = &data.1; + let dto: EnumDto = en_ref.into(); + assert!(dto == data.0); + + let dto: EnumDto = data.1.into(); + assert!(dto == data.0); + } +} + +#[test] +fn enum2enum_with_data() { + for data in vec![ + (EnumWithDataDto::Item1Dto(123, 321), EnumWithData::Item1(123, 321)), + (EnumWithDataDto::Item2 { string: "Test".into(), i: 654 }, EnumWithData::Item2 { str: "Test".into(), i: 654 }) + ] { + let en: EnumWithData = data.0.clone().into(); + assert!(en == data.1); + + let dto: EnumWithDataDto = data.1.into(); + assert!(dto == data.0); + } +} \ No newline at end of file diff --git a/o2o-tests/tests/29_enum_member_level_inline_expr_only_tests.rs b/o2o-tests/tests/29_enum_member_level_inline_expr_only_tests.rs new file mode 100644 index 0000000..1ea0c3f --- /dev/null +++ b/o2o-tests/tests/29_enum_member_level_inline_expr_only_tests.rs @@ -0,0 +1,36 @@ +#[derive(PartialEq, Eq)] +enum EnumWithData { + Item1(i32, i16), + Item2 { str: String, i: i32 }, +} + +#[derive(Clone, PartialEq, Eq, o2o::o2o)] +#[map_owned(EnumWithData)] +enum EnumWithDataDto { + Item1( + i32, + #[from_owned(~ * 2)] + #[owned_into(~ / 2)] + i16 + ), + Item2 { + str: String, + #[from(~.to_string())] + #[into(~.parse::().unwrap())] + i: String + }, +} + +#[test] +fn enum2enum_with_data() { + for data in vec![ + (EnumWithDataDto::Item1(123, 222), EnumWithData::Item1(123, 111)), + (EnumWithDataDto::Item2 { str: "Test".into(), i: "654".into() }, EnumWithData::Item2 { str: "Test".into(), i: 654 }) + ] { + let en: EnumWithData = data.0.clone().into(); + assert!(en == data.1); + + let dto: EnumWithDataDto = data.1.into(); + assert!(dto == data.0); + } +} \ No newline at end of file diff --git a/o2o-tests/tests/29_enums_playground.rs b/o2o-tests/tests/29_enums_playground.rs deleted file mode 100644 index 3d5ddbe..0000000 --- a/o2o-tests/tests/29_enums_playground.rs +++ /dev/null @@ -1,124 +0,0 @@ -enum EnumWithData { - Item1(String), - Item2(i32, i16), -} - -enum EnumWithDataDto { - Item1(String), - Item2(i32, i16), -} - -impl From for EnumWithDataDto { - fn from(value: EnumWithData) -> EnumWithDataDto { - match value { - EnumWithData::Item1(str) => EnumWithDataDto::Item1(str), - EnumWithData::Item2(i, i2) => EnumWithDataDto::Item2(i, i2), - } - } -} - -// impl Into for EnumWithDataDto { -// fn into(self) -> EnumWithData { -// match self { -// EnumWithDataDto::Item1(str) => EnumWithData::Item1(str), -// EnumWithDataDto::Item2(i) => EnumWithData::Item2(i), -// } -// } -// } - -// impl From<&EnumWithData> for EnumWithDataDto { -// fn from(value: &EnumWithData) -> EnumWithDataDto { -// match value { -// EnumWithData::Item1(str) => EnumWithDataDto::Item1(str.clone()), -// EnumWithData::Item2(i) => EnumWithDataDto::Item2(*i), -// } -// } -// } - -enum Test { - Opt1(i32, String), - Opt2 { val: i32, str: String }, - Opt3, - Opt4, - Opt5 -} - -enum Test2 { - Opt1(i32, String), - Opt2 { val: i32, str: String}, - SubOpt(Test123) -} - -enum Test123 { - Opt3, - SubOpt(Test321) -} - -enum Test321 { - Opt4, - Opt5 -} - -impl From for Test2 { - fn from(value: Test) -> Self { - match value { - Test::Opt1(i, s) => Test2::Opt1(i, s), - Test::Opt2 { val, str } => Test2::Opt2 { val, str }, - Test::Opt3 => Test2::SubOpt(Test123::Opt3), - Test::Opt4 => Test2::SubOpt(Test123::SubOpt(Test321::Opt4)), - Test::Opt5 => Test2::SubOpt(Test123::SubOpt(Test321::Opt5)), - } - } -} - -impl Into for Test2 { - fn into(self) -> Test { - match self { - Test2::Opt1(i, s) => Test::Opt1(i, s), - Test2::Opt2 { val, str } => Test::Opt2 { val, str }, - Test2::SubOpt(sub) => match sub { - Test123::Opt3 => Test::Opt3, - Test123::SubOpt(sub) => match sub { - Test321::Opt4 => Test::Opt4, - Test321::Opt5 => Test::Opt5, - }, - }, - } - } -} - -#[derive(Default/*, o2o::o2o */)] -struct Entity { - item_1: String, - item_2: i32, - item_3: i32 -} - -#[derive(Default/*, o2o::o2o */)] -//#[map_owned(Entity| update: { Default::default() }, test: {"123"})] -//#[o2o(update_with(Default::default()))] -struct EntityDto { - //#[map(~.clone())] - item_1: String, - item_2: i32, - //#[ghost({123})] - -} - -impl std::convert::From for EntityDto { - fn from(value: Entity) -> EntityDto { - EntityDto { - item_1: value.item_1.clone(), - item_2: value.item_2 - } - } -} -impl std::convert::Into for EntityDto { - fn into(self) -> Entity { - Entity { - item_1: self.item_1.clone(), - item_2: self.item_2, - ..Default::default() - } - } -} \ No newline at end of file diff --git a/o2o-tests/tests/30_enum_member_level_ident_and_inline_expr_tests.rs b/o2o-tests/tests/30_enum_member_level_ident_and_inline_expr_tests.rs new file mode 100644 index 0000000..6d17c1f --- /dev/null +++ b/o2o-tests/tests/30_enum_member_level_ident_and_inline_expr_tests.rs @@ -0,0 +1,36 @@ +#[derive(PartialEq, Eq)] +enum EnumWithData { + Item1(i32, i16), + Item2 { str: String, i: i32 }, +} + +#[derive(Clone, PartialEq, Eq, o2o::o2o)] +#[map_owned(EnumWithData)] +enum EnumWithDataDto { + Item1( + #[from(0, ~.to_string())] + #[into(0, ~.parse::().unwrap())] + String, + i16 + ), + Item2 { + str: String, + #[from(i, ~.to_string())] + #[into(i, ~.parse::().unwrap())] + i_str: String + }, +} + +#[test] +fn enum2enum_with_data() { + for data in vec![ + (EnumWithDataDto::Item1("123".into(), 321), EnumWithData::Item1(123, 321)), + (EnumWithDataDto::Item2 { str: "Test".into(), i_str: "654".into() }, EnumWithData::Item2 { str: "Test".into(), i: 654 }) + ] { + let en: EnumWithData = data.0.clone().into(); + assert!(en == data.1); + + let dto: EnumWithDataDto = data.1.into(); + assert!(dto == data.0); + } +} \ No newline at end of file From fd3541dd2b09c3fb4ff10e2e3d77526627a13f89 Mon Sep 17 00:00:00 2001 From: Artem Romanenia Date: Mon, 25 Mar 2024 14:26:37 +0300 Subject: [PATCH 3/3] Add readme example --- README.md | 49 +++++++++++++++++++++++++++++++----------- o2o-impl/src/expand.rs | 11 +++++----- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 7148a49..7a1e065 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ And here's the code that `o2o` generates (from here on, generated code is produc - [The (not so big) Problem](#the-not-so-big-problem) - [Inline expressions](#inline-expressions) - [Examples](#examples) - - [Different field name](#different-field-name) + - [Different member name](#different-member-name) - [Different field type](#different-field-type) - [Nested structs](#nested-structs) - [Nested collection](#nested-collection) @@ -313,7 +313,7 @@ So finally, let's look at some examples. ## Examples -### Different field name +### Different member name ``` rust use o2o::o2o; @@ -323,14 +323,29 @@ struct Entity { another_int: i16, } +enum EntityEnum { + Entity(Entity), + SomethingElse { field: i32 } +} + #[derive(o2o)] -#[from_ref(Entity)] -#[ref_into_existing(Entity)] +#[map_ref(Entity)] struct EntityDto { some_int: i32, #[map(another_int)] different_int: i16, } + +#[derive(o2o)] +#[map_ref(EntityEnum)] +enum EntityEnumDto { + #[map(Entity)] + EntityDto(#[map(~.into())]EntityDto), + SomethingElse { + #[map(field, *~)] + f: i32 + } +} ```
View generated code @@ -350,6 +365,23 @@ struct EntityDto { other.another_int = self.different_int; } } + + impl std::convert::From<&EntityEnum> for EntityEnumDto { + fn from(value: &EntityEnum) -> EntityEnumDto { + match value { + EntityEnum::Entity(f0) => EntityEnumDto::EntityDto(f0.into()), + EntityEnum::SomethingElse { field } => EntityEnumDto::SomethingElse { f: *field }, + } + } + } + impl std::convert::Into for &EntityEnumDto { + fn into(self) -> EntityEnum { + match self { + EntityEnumDto::EntityDto(f0) => EntityEnum::Entity(f0.into()), + EntityEnumDto::SomethingElse { f } => EntityEnum::SomethingElse { field: *f }, + } + } + } ```
@@ -485,9 +517,6 @@ struct Child { #[map_owned(Entity)] struct EntityDto { some_int: i32, - // Here field name as well as type are different, so we pass in field name and tilde inline expression. - // Also, it doesn't hurt to use member trait instruction #[map()], - // which is broader than trait instruction #[map_owned] #[map(children, ~.iter().map(|p|p.into()).collect())] children_vec: Vec } @@ -588,7 +617,7 @@ enum ZodiacSign {} ``` -In a reverse case, you need to use a struct level `#[ghost()]` instruction: +In a reverse case, you need to use a struct level `#[ghosts()]` instruction: ``` rust use o2o::o2o; @@ -805,16 +834,12 @@ impl Employee { #[derive(o2o)] #[map(Employee)] #[ghosts( - // o2o supports closures with one input parameter. - // This parameter represents instance on the other side of the conversion. first_name: {@.get_first_name()}, last_name: {@.get_last_name()} )] struct EmployeeDto { #[map(id)] employee_id: i32, - // '@.' is another flavor of 'inline expression'. - // @ also represents instance on the other side of the conversion. #[ghost(@.get_full_name())] full_name: String, diff --git a/o2o-impl/src/expand.rs b/o2o-impl/src/expand.rs index a9f7ffe..28bd521 100644 --- a/o2o-impl/src/expand.rs +++ b/o2o-impl/src/expand.rs @@ -20,7 +20,6 @@ pub fn derive(node: &DeriveInput) -> Result { let input = Enum::from_syn(node, data)?; let input = DataType::Enum(&input); validate(&input)?; - //Ok(TokenStream::new()) Ok(struct_impl(input)) }, _ => Err(Error::new_spanned( @@ -579,7 +578,7 @@ fn render_struct_line( let index2 = Member::Unnamed(Index { index: idx as u32, span: Span::call_site() }); quote!(obj.#index2 = #obj #index;) } else { - let index = if ctx.destructured_src { format_ident!("f{}", index).to_token_stream() } else { index.to_token_stream() }; + let index = if ctx.destructured_src { format_ident!("f{}", index.index).to_token_stream() } else { index.to_token_stream() }; quote!(#obj #index,) }, (syn::Member::Unnamed(index), None, Kind::OwnedIntoExisting | Kind::RefIntoExisting, StructKindHint::Tuple | StructKindHint::Unspecified) => { @@ -590,7 +589,7 @@ fn render_struct_line( if f.attrs.has_parent_attr(&ctx.struct_attr.ty) { if !ctx.kind.is_ref() { quote!((&value).into(),) } else { quote!(value.into(),) } } else { - let field_path = if ctx.destructured_src { get_field_path(&Member::Named(format_ident!("f{}", index))) } else { get_field_path(&f.member) }; + let field_path = if ctx.destructured_src { get_field_path(&Member::Named(format_ident!("f{}", index.index))) } else { get_field_path(&f.member) }; quote!(#obj #field_path,) }, (syn::Member::Unnamed(_), None, _, StructKindHint::Struct) => { @@ -624,7 +623,7 @@ fn render_struct_line( quote!(#ident: #right_side,) }, (syn::Member::Unnamed(index), Some(attr), Kind::OwnedInto | Kind::RefInto, StructKindHint::Tuple | StructKindHint::Unspecified) => { - let index = if ctx.destructured_src { Some(format_ident!("f{}", index).to_token_stream()) } else { Some(index.to_token_stream()) }; + let index = if ctx.destructured_src { Some(format_ident!("f{}", index.index).to_token_stream()) } else { Some(index.to_token_stream()) }; let right_side = attr.get_action_or(index.clone(), ctx, || quote!(#obj #index)); quote!(#right_side,) }, @@ -644,7 +643,7 @@ fn render_struct_line( quote!(other.#field_path = #right_side;) }, (syn::Member::Unnamed(index), Some(attr), Kind::FromOwned | Kind::FromRef, _) => { - let or = Member::Named(format_ident!("f{}", index)); + let or = Member::Named(format_ident!("f{}", index.index)); let right_side = attr.get_stuff(&obj, get_field_path, ctx, || if ctx.destructured_src { &or } else { &f.member}); quote!(#right_side,) } @@ -891,7 +890,7 @@ impl<'a> ApplicableAttr<'a> { (Some(ident), Some(action)) => { if let Member::Unnamed(index) = ident { if ctx.destructured_src { - let ident = Member::Named(format_ident!("f{}", index)); + let ident = Member::Named(format_ident!("f{}", index.index)); quote_action(action, Some(field_path(&ident)), ctx) } else { quote_action(action, Some(field_path(ident)), ctx)} } else {