From 9b44be54fb88ddc79c95610fd5b40dad3d655124 Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Tue, 2 Jul 2024 09:53:43 +0300 Subject: [PATCH 01/12] fut: basic struct tests --- .../tests/inc/index/only_test/struct_named.rs | 10 +++++++++ .../tests/inc/index/only_test/struct_tuple.rs | 10 +++++++++ .../tests/inc/index/struct_named_manual.rs | 21 +++++++++++++++++++ .../tests/inc/index/struct_tuple_manual.rs | 19 +++++++++++++++++ module/core/derive_tools/tests/inc/mod.rs | 13 ++++++++++++ 5 files changed, 73 insertions(+) create mode 100644 module/core/derive_tools/tests/inc/index/only_test/struct_named.rs create mode 100644 module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs create mode 100644 module/core/derive_tools/tests/inc/index/struct_named_manual.rs create mode 100644 module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs new file mode 100644 index 0000000000..49ea62b4a7 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs @@ -0,0 +1,10 @@ +use super::*; + +#[ test ] +fn index() +{ + let x = StructNamed { a : true}; + + a_id!(x[0], true); +} + diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs new file mode 100644 index 0000000000..18d3262119 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs @@ -0,0 +1,10 @@ +use super::*; + +#[ test ] +fn index() +{ + let x = StructTuple(7); + + a_id!(x[0], 7); +} + diff --git a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs new file mode 100644 index 0000000000..a4baab7eb9 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs @@ -0,0 +1,21 @@ +use core::ops::Index; + +struct StructNamed +{ + a : T, +} + +impl Index for StructNamed +{ + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + match index { + 0 => &self.a, + _ => panic!("Index out of bounds"), + } + } +} + +include!( "./only_test/struct_named.rs" ); + diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs new file mode 100644 index 0000000000..8bd93950c4 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs @@ -0,0 +1,19 @@ +use core::ops::Index; + +struct StructTuple(T); + +impl Index for StructTuple +{ + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + match index { + 0 => &self.0, + _ => panic!("Index out of bounds"), + } + } +} + +include!( "./only_test/struct_tuple.rs" ); + + diff --git a/module/core/derive_tools/tests/inc/mod.rs b/module/core/derive_tools/tests/inc/mod.rs index 518d8757d4..995dce98ea 100644 --- a/module/core/derive_tools/tests/inc/mod.rs +++ b/module/core/derive_tools/tests/inc/mod.rs @@ -197,3 +197,16 @@ mod inner_from_tests mod multiple_test; } + + + +#[ cfg( feature = "derive_index" ) ] +#[ path = "index" ] +mod index_tests +{ + #[ allow( unused_imports ) ] + use super::*; + + mod struct_named_manual; + mod struct_tuple_manual; +} From ce13803bcde3b1591c62c96809108c91ae8b18bf Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Tue, 2 Jul 2024 17:24:34 +0300 Subject: [PATCH 02/12] fut: named struct draft impl --- module/core/derive_tools/Cargo.toml | 3 +- module/core/derive_tools/build.rs | 2 + module/core/derive_tools/src/lib.rs | 2 - .../tests/inc/index/only_test/struct_named.rs | 5 +- .../tests/inc/index/only_test/struct_tuple.rs | 9 +- .../tests/inc/index/struct_named.rs | 11 ++ .../tests/inc/index/struct_named_manual.rs | 8 +- .../tests/inc/index/struct_tuple.rs | 8 ++ module/core/derive_tools/tests/inc/mod.rs | 2 + module/core/derive_tools_meta/Cargo.toml | 3 + module/core/derive_tools_meta/src/derive.rs | 2 + .../derive_tools_meta/src/derive/index.rs | 118 ++++++++++++++++++ module/core/derive_tools_meta/src/lib.rs | 54 ++++++++ 13 files changed, 214 insertions(+), 13 deletions(-) create mode 100644 module/core/derive_tools/tests/inc/index/struct_named.rs create mode 100644 module/core/derive_tools/tests/inc/index/struct_tuple.rs create mode 100644 module/core/derive_tools_meta/src/derive/index.rs diff --git a/module/core/derive_tools/Cargo.toml b/module/core/derive_tools/Cargo.toml index 7aa9e77c4e..fbf82b41dd 100644 --- a/module/core/derive_tools/Cargo.toml +++ b/module/core/derive_tools/Cargo.toml @@ -150,7 +150,7 @@ derive_error = [ "derive_more", "derive_more/std", "derive_more/error" ] # derive_from = [ "derive_tools_meta/derive_from" ] # derive_reflect = [ "derive_tools_meta/derive_reflect" ] -derive_index = [ "derive_more", "derive_more/std", "derive_more/index" ] +# derive_index = [ "derive_more", "derive_more/std", "derive_more/index" ] derive_index_mut = [ "derive_more", "derive_more/std", "derive_more/index_mut" ] # derive_inner_from = [ "derive_more", "derive_more/into" ] derive_into_iterator = [ "derive_more", "derive_more/std", "derive_more/into_iterator" ] @@ -178,6 +178,7 @@ derive_clone_dyn = [ "clone_dyn/enabled" ] # derive_clone_dyn_use_alloc = [ "derive_clone_dyn", "clone_dyn/use_alloc" ] derive_from = [ "derive_tools_meta/derive_from" ] +derive_index = [ "derive_tools_meta/derive_index" ] derive_inner_from = [ "derive_tools_meta/derive_inner_from" ] derive_new = [ "derive_tools_meta/derive_new" ] diff --git a/module/core/derive_tools/build.rs b/module/core/derive_tools/build.rs index 7bf86e248f..afc1ca3107 100644 --- a/module/core/derive_tools/build.rs +++ b/module/core/derive_tools/build.rs @@ -21,6 +21,7 @@ fn main() feature = "derive_deref", feature = "derive_deref_mut", feature = "derive_from", + feature = "derive_index", feature = "derive_inner_from", feature = "derive_variadic_from", feature = "derive_reflect", @@ -36,6 +37,7 @@ fn main() feature = "derive_deref", feature = "derive_deref_mut", feature = "derive_from", + feature = "derive_index", feature = "derive_inner_from", feature = "derive_variadic_from", feature = "derive_reflect", diff --git a/module/core/derive_tools/src/lib.rs b/module/core/derive_tools/src/lib.rs index 1be9d0ed7a..168bcbe62b 100644 --- a/module/core/derive_tools/src/lib.rs +++ b/module/core/derive_tools/src/lib.rs @@ -45,8 +45,6 @@ mod derive_more pub use ::derive_more::Error; #[ cfg( feature = "derive_index_mut" ) ] pub use ::derive_more::IndexMut; - #[ cfg( feature = "derive_index" ) ] - pub use ::derive_more::Index; #[ cfg( feature = "derive_into" ) ] pub use ::derive_more::Into; #[ cfg( feature = "derive_iterator" ) ] diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs index 49ea62b4a7..bcf01e14e6 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs @@ -1,10 +1,11 @@ -use super::*; #[ test ] fn index() { let x = StructNamed { a : true}; + let exp = true; + let got = x[0]; - a_id!(x[0], true); + assert_eq!(got, exp); } diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs index 18d3262119..c332b74eab 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs @@ -1,10 +1,11 @@ -use super::*; - -#[ test ] +#[test ] fn index() { let x = StructTuple(7); + let exp = 7; + let got = x[0]; + + assert_eq!(got, exp); - a_id!(x[0], 7); } diff --git a/module/core/derive_tools/tests/inc/index/struct_named.rs b/module/core/derive_tools/tests/inc/index/struct_named.rs new file mode 100644 index 0000000000..12dc567d33 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_named.rs @@ -0,0 +1,11 @@ + +use super::*; + +#[ allow( dead_code ) ] +#[ derive( the_module::Index, Debug) ] +struct StructNamed +{ + a : T, +} + +include!( "./only_test/struct_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs index a4baab7eb9..c854894d43 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs @@ -1,13 +1,13 @@ use core::ops::Index; -struct StructNamed +struct StructNamed { - a : T, + a : bool, } -impl Index for StructNamed +impl Index for StructNamed { - type Output = T; + type Output = bool; fn index(&self, index: usize) -> &Self::Output { match index { diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/struct_tuple.rs new file mode 100644 index 0000000000..8e831e4936 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_tuple.rs @@ -0,0 +1,8 @@ +use super::*; + +#[ allow( dead_code ) ] +#[ derive( the_module::Index ) ] +struct StructNamed< T > ( T ); + +include!( "./only_test/struct_tuple.rs" ); + diff --git a/module/core/derive_tools/tests/inc/mod.rs b/module/core/derive_tools/tests/inc/mod.rs index 11953b1c08..c499972943 100644 --- a/module/core/derive_tools/tests/inc/mod.rs +++ b/module/core/derive_tools/tests/inc/mod.rs @@ -24,6 +24,7 @@ mod all_manual_test; feature = "derive_deref", feature = "derive_deref_mut", feature = "derive_from", + feature = "derive_index", feature = "derive_inner_from", feature = "derive_phantom" ) @@ -300,6 +301,7 @@ mod index_tests #[ allow( unused_imports ) ] use super::*; + mod struct_named; mod struct_named_manual; mod struct_tuple_manual; diff --git a/module/core/derive_tools_meta/Cargo.toml b/module/core/derive_tools_meta/Cargo.toml index 3eaa24dc95..f16ebfdd62 100644 --- a/module/core/derive_tools_meta/Cargo.toml +++ b/module/core/derive_tools_meta/Cargo.toml @@ -34,6 +34,7 @@ default = [ "derive_deref", "derive_from", "derive_new", + "derive_index", "derive_inner_from", "derive_as_ref", "derive_as_mut", @@ -46,6 +47,7 @@ full = [ "derive_deref", "derive_from", "derive_new", + "derive_index", "derive_inner_from", "derive_as_ref", "derive_as_mut", @@ -60,6 +62,7 @@ derive_deref = [] derive_deref_mut = [] derive_from = [] derive_new = [] +derive_index = [] derive_inner_from = [] derive_variadic_from = [ "iter_tools/iter_ext" ] derive_phantom = [] diff --git a/module/core/derive_tools_meta/src/derive.rs b/module/core/derive_tools_meta/src/derive.rs index e86b79ff3c..4854036704 100644 --- a/module/core/derive_tools_meta/src/derive.rs +++ b/module/core/derive_tools_meta/src/derive.rs @@ -17,6 +17,8 @@ pub mod deref; pub mod deref_mut; #[ cfg( feature = "derive_from" ) ] pub mod from; +#[ cfg( feature = "derive_index" ) ] +pub mod index; #[ cfg( feature = "derive_inner_from" ) ] pub mod inner_from; #[ cfg( feature = "derive_new" ) ] diff --git a/module/core/derive_tools_meta/src/derive/index.rs b/module/core/derive_tools_meta/src/derive/index.rs new file mode 100644 index 0000000000..3a376ca535 --- /dev/null +++ b/module/core/derive_tools_meta/src/derive/index.rs @@ -0,0 +1,118 @@ +use super::*; +use macro_tools::{ attr, diag, generic_params, struct_like::StructLike, Result }; + + +pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > +{ + let original_input = input.clone(); + let parsed = syn::parse::< StructLike >( input )?; + let has_debug = attr::has_debug( parsed.attrs().iter() )?; + let item_name = &parsed.ident(); + + let ( _generics_with_defaults, generics_impl, generics_ty, generics_where ) + = generic_params::decompose( &parsed.generics() ); + + let result = match parsed + { + StructLike::Struct( ref item ) => + { + generate_struct + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + &item.fields, + ) + }, + StructLike::Unit(_) | StructLike::Enum(_) => todo!() + }?; + + + + + if has_debug + { + let about = format!( "derive : Not\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); + } + + Ok( result ) +} + + + +/// An aggregator function to generate `AsRef` implementation for unit, tuple structs and the ones with named fields +fn generate_struct +( + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + fields : &syn::Fields, +) +-> Result< proc_macro2::TokenStream > +{ + match fields + { + syn::Fields::Named( fields ) => + generate_struct_named_fields + ( + item_name, + generics_impl, + generics_ty, + generics_where, + fields, + ), + &syn::Fields::Unnamed(_) | &syn::Fields::Unit => todo!() + } +} + + + + +fn generate_struct_named_fields +( + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + fields : &syn::FieldsNamed, +) +-> Result< proc_macro2::TokenStream > +{ + let fields = fields.named.clone(); + let ( field_name, field_type ) = match fields.first() + { + Some( field ) => ( field.ident.as_ref().unwrap(), &field.ty ), + None => todo!() + }; + + Ok + ( + qt! + { + #[ automatically_derived ] + impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > + where + #generics_where + { + type Output = #field_type; + + fn index( &self, index: usize ) -> &Self::Output + { + match index { + 0 => &self.#field_name, + _ => panic!("Index out of bounds"), + } + + } + } + } + ) +} + + + + + diff --git a/module/core/derive_tools_meta/src/lib.rs b/module/core/derive_tools_meta/src/lib.rs index d66ac3ca51..2d0d0b334c 100644 --- a/module/core/derive_tools_meta/src/lib.rs +++ b/module/core/derive_tools_meta/src/lib.rs @@ -13,6 +13,7 @@ feature = "derive_deref", feature = "derive_deref_mut", feature = "derive_from", + feature = "derive_index", feature = "derive_inner_from", feature = "derive_variadic_from", feature = "derive_phantom" @@ -29,6 +30,7 @@ mod derive; // feature = "derive_deref", // feature = "derive_deref_mut", // feature = "derive_from", +// feature = "derive_index", // feature = "derive_inner_from", // feature = "derive_variadic_from", // feature = "derive_phantom" @@ -568,3 +570,55 @@ pub fn phantom( _attr: proc_macro::TokenStream, input : proc_macro::TokenStream Err( err ) => err.to_compile_error().into(), } } + +/// +/// Provides an automatic [Index](core::ops::Index) trait implementation when-ever it's possible. +/// +/// This macro simplifies the indexing syntzx of struct type. +/// +/// ## Example Usage +// +/// Instead of manually implementing `Index< T >` for `IsTransparent`: +/// +/// ```rust +/// pub struct IsTransparent{ +/// a : T, +/// }; +/// +/// impl Index< usize > for IsTransparent +/// { +/// type Output = T; +/// +/// #[ inline( always ) ] +/// fn index(&self, index: usize ) -> &Self::Output { +/// match index { +/// 0 => &self.a, +/// _ => panic!("Index out of bounds"), +/// } +/// } +/// } +/// ``` +/// +/// Use `#[ derive( Index ) ]` to automatically generate the implementation: +/// +/// ```rust +/// # use derive_tools_meta::*; +/// #[ derive( Index ) ] +/// pub struct IsTransparent { +/// a: T +///}; +/// ``` +/// + +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "derive_index" ) ] +#[ proc_macro_derive( Index, attributes( debug ) ) ] +pub fn derive_index( input : proc_macro::TokenStream ) -> proc_macro::TokenStream +{ + let result = derive::index::index( input ); + match result + { + Ok( stream ) => stream.into(), + Err( err ) => err.to_compile_error().into(), + } +} From 99ed34f54d3849dad936e0e2ef88f928a6431aea Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Wed, 3 Jul 2024 12:41:09 +0300 Subject: [PATCH 03/12] fut: wnamed struct Generics realization --- .../tests/inc/index/only_test/struct_named.rs | 13 +- .../tests/inc/index/only_test/struct_tuple.rs | 13 +- .../tests/inc/index/struct_named.rs | 13 +- .../tests/inc/index/struct_named_manual.rs | 27 +- .../tests/inc/index/struct_tuple.rs | 9 +- .../tests/inc/index/struct_tuple_manual.rs | 24 +- .../derive_tools_meta/src/derive/index.rs | 271 ++++++++++++------ 7 files changed, 221 insertions(+), 149 deletions(-) diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs index bcf01e14e6..ee4c29ed1a 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs @@ -1,11 +1,10 @@ +#[test] +fn index() { + let x = StructNamed { a: true, b: false }; + let exp = (true, false); + let got = (x[0], x[1]); -#[ test ] -fn index() -{ - let x = StructNamed { a : true}; - let exp = true; - let got = x[0]; + dbg!(exp, got); assert_eq!(got, exp); } - diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs index c332b74eab..74cb9670f0 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs @@ -1,11 +1,8 @@ -#[test ] -fn index() -{ - let x = StructTuple(7); - let exp = 7; - let got = x[0]; +#[test] +fn index() { + let x = StructTuple(7, 12); + let exp = (7, 12); + let got = (x[0], x[1]); assert_eq!(got, exp); - } - diff --git a/module/core/derive_tools/tests/inc/index/struct_named.rs b/module/core/derive_tools/tests/inc/index/struct_named.rs index 12dc567d33..66197a3693 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named.rs @@ -1,11 +1,10 @@ - use super::*; -#[ allow( dead_code ) ] -#[ derive( the_module::Index, Debug) ] -struct StructNamed -{ - a : T, +#[allow(dead_code)] +#[derive(the_module::Index, Debug)] +struct StructNamed { + a: T, + b: T, } -include!( "./only_test/struct_named.rs" ); +include!("./only_test/struct_named.rs"); diff --git a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs index c854894d43..a07641fe28 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs @@ -1,21 +1,20 @@ use core::ops::Index; -struct StructNamed -{ - a : bool, +struct StructNamed { + a: T, + b: T, } -impl Index for StructNamed -{ - type Output = bool; - - fn index(&self, index: usize) -> &Self::Output { - match index { - 0 => &self.a, - _ => panic!("Index out of bounds"), - } +impl Index for StructNamed { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + match index { + 0 => &self.a, + 1 => &self.b, + _ => panic!("Index out of bounds"), } + } } -include!( "./only_test/struct_named.rs" ); - +include!("./only_test/struct_named.rs"); diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/struct_tuple.rs index 8e831e4936..c4748cedcc 100644 --- a/module/core/derive_tools/tests/inc/index/struct_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/struct_tuple.rs @@ -1,8 +1,7 @@ use super::*; -#[ allow( dead_code ) ] -#[ derive( the_module::Index ) ] -struct StructNamed< T > ( T ); - -include!( "./only_test/struct_tuple.rs" ); +#[allow(dead_code)] +#[derive(the_module::Index)] +struct StructNamed(T, T); +include!("./only_test/struct_tuple.rs"); diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs index 8bd93950c4..0f67af17dc 100644 --- a/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs @@ -1,19 +1,17 @@ use core::ops::Index; -struct StructTuple(T); +struct StructTuple(T, T); -impl Index for StructTuple -{ - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - match index { - 0 => &self.0, - _ => panic!("Index out of bounds"), - } +impl Index for StructTuple { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + match index { + 0 => &self.0, + 1 => &self.1, + _ => panic!("Index out of bounds"), } + } } -include!( "./only_test/struct_tuple.rs" ); - - +include!("./only_test/struct_tuple.rs"); diff --git a/module/core/derive_tools_meta/src/derive/index.rs b/module/core/derive_tools_meta/src/derive/index.rs index 3a376ca535..996262bb8f 100644 --- a/module/core/derive_tools_meta/src/derive/index.rs +++ b/module/core/derive_tools_meta/src/derive/index.rs @@ -1,118 +1,199 @@ use super::*; -use macro_tools::{ attr, diag, generic_params, struct_like::StructLike, Result }; +use macro_tools::{attr, diag, generic_params, struct_like::StructLike, Result}; - -pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > -{ +pub fn index(input: proc_macro::TokenStream) -> Result { let original_input = input.clone(); - let parsed = syn::parse::< StructLike >( input )?; - let has_debug = attr::has_debug( parsed.attrs().iter() )?; + let parsed = syn::parse::(input)?; + let has_debug = attr::has_debug(parsed.attrs().iter())?; let item_name = &parsed.ident(); - let ( _generics_with_defaults, generics_impl, generics_ty, generics_where ) - = generic_params::decompose( &parsed.generics() ); - - let result = match parsed - { - StructLike::Struct( ref item ) => - { - generate_struct - ( - item_name, - &generics_impl, - &generics_ty, - &generics_where, - &item.fields, - ) - }, - StructLike::Unit(_) | StructLike::Enum(_) => todo!() - }?; - - + let (_generics_with_defaults, generics_impl, generics_ty, generics_where) = generic_params::decompose(&parsed.generics()); + let result = match parsed { + StructLike::Struct(ref item) => generate_struct(item_name, &generics_impl, &generics_ty, &generics_where, &item.fields), + StructLike::Enum(_) => todo!(), + StructLike::Unit(_) => todo!(), + }?; - if has_debug - { - let about = format!( "derive : Not\nstructure : {item_name}" ); - diag::report_print( about, &original_input, &result ); + if has_debug { + let about = format!("derive : Not\nstructure : {item_name}"); + diag::report_print(about, &original_input, &result); } - Ok( result ) + Ok(result) } - - -/// An aggregator function to generate `AsRef` implementation for unit, tuple structs and the ones with named fields -fn generate_struct -( - item_name : &syn::Ident, - generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - fields : &syn::Fields, -) --> Result< proc_macro2::TokenStream > -{ - match fields - { - syn::Fields::Named( fields ) => - generate_struct_named_fields - ( - item_name, - generics_impl, - generics_ty, - generics_where, - fields, - ), - &syn::Fields::Unnamed(_) | &syn::Fields::Unit => todo!() +/// An aggregator function to generate `Index` implementation for unit, tuple structs and the ones with named fields +fn generate_struct( + item_name: &syn::Ident, + generics_impl: &syn::punctuated::Punctuated, + generics_ty: &syn::punctuated::Punctuated, + generics_where: &syn::punctuated::Punctuated, + fields: &syn::Fields, +) -> Result { + match fields { + syn::Fields::Named(fields) => generate_struct_named_fields(item_name, generics_impl, generics_ty, generics_where, fields), + syn::Fields::Unnamed(fields) => generate_struct_tuple_fields(item_name, generics_impl, generics_ty, generics_where, fields), + syn::Fields::Unit => Err(syn::Error::new( + fields.span(), + "cannot infer type: Empty type cannot be indexed", + )), } } - - - -fn generate_struct_named_fields -( - item_name : &syn::Ident, - generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - fields : &syn::FieldsNamed, -) --> Result< proc_macro2::TokenStream > -{ - let fields = fields.named.clone(); - let ( field_name, field_type ) = match fields.first() - { - Some( field ) => ( field.ident.as_ref().unwrap(), &field.ty ), - None => todo!() - }; - - Ok - ( - qt! +/// Generates `Index` implementation for structs with tuple fields +/// +/// # Example +/// +/// ## Input +/// ```rust +/// # use derive_tools_meta::Index; +/// #[ derive( Index ) ] +/// pub struct Struct< T >( T ); +/// ``` +/// +/// ## Output +/// ```rust +/// pub struct Struct< T >( T ); +/// #[ automatically_derived ] +/// impl< T > ::core::ops::Index< usize > for Struct< T > +/// { +/// type Output = T; +/// #[ inline( always ) ] +/// fn index( &self, index: usize ) -> &Self::Output +/// { +/// match index { +/// 0 => &self.0, +/// _ => panic!("Index out of bounds"), +/// } +/// } +/// } +/// ``` +/// + +fn generate_struct_tuple_fields( + item_name: &syn::Ident, + generics_impl: &syn::punctuated::Punctuated, + generics_ty: &syn::punctuated::Punctuated, + generics_where: &syn::punctuated::Punctuated, + fields: &syn::FieldsUnnamed, +) -> Result { + let fields = &fields.unnamed; + + // Get the type of the first field as the output type + let field_type = match fields.first() { + Some(field) => &field.ty, + None => { + return Err(syn::Error::new( + fields.span(), + "cannot infer type: Empty type cannot be indexed", + )) + } + }; + + // Generate match arms for each field + let match_arms = fields.iter().enumerate().map_result(|(i, field)| { + let field_name = &field.ident; + Result::Ok(quote! { + #i => &self.#field_name + }) + })?; + + Ok(qt! { + #[ automatically_derived ] + impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > + where + #generics_where { - #[ automatically_derived ] - impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > - where - #generics_where + type Output = #field_type; + + fn index( &self, index: usize ) -> &Self::Output { - type Output = #field_type; - - fn index( &self, index: usize ) -> &Self::Output - { - match index { - 0 => &self.#field_name, - _ => panic!("Index out of bounds"), - } - - } + match index { + #(#match_arms,)* + _ => panic!("Index out of bounds"), + } + } } - ) + }) } +/// Generates `Index` implementation for structs with tuple fields +/// +/// # Example +/// +/// ## Input +/// ```rust +/// # use derive_tools_meta::Index; +/// #[ derive( Index ) ] +/// pub struct Struct< T > i +/// { +/// a: T, +/// } +/// ``` +/// +/// ## Output +/// ```rust +/// pub struct Struct< T > +/// { +/// a: T, +/// }; +/// +/// #[ automatically_derived ] +/// impl< T > ::core::ops::Index< usize > for Struct< T > +/// { +/// type Output = T; +/// #[ inline( always ) ] +/// fn index( &self, index: usize ) -> &Self::Output +/// { +/// match index { +/// 0 => &self.a, +/// _ => panic!("Index out of bounds"), +/// } +/// } +/// } +/// ``` +/// + +fn generate_struct_named_fields( + item_name: &syn::Ident, + generics_impl: &syn::punctuated::Punctuated, + generics_ty: &syn::punctuated::Punctuated, + generics_where: &syn::punctuated::Punctuated, + fields: &syn::FieldsNamed, +) -> Result { + let fields = fields.named.clone(); + // Get the type of the first field as the output type + let field_type = match fields.first() { + Some(field) => &field.ty, + None => return Err(syn::Error::new(fields.span(), "Empty type cannot be indexed")), + }; + // Generate match arms for each field + let match_arms = fields.iter().enumerate().map_result(|(i, field)| { + let field_name = &field.ident; + Result::Ok(quote! { + #i => &self.#field_name + }) + })?; + + Ok(qt! { + #[ automatically_derived ] + impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > + where + #generics_where + { + type Output = #field_type; + fn index( &self, index: usize ) -> &Self::Output + { + match index { + #(#match_arms,)* + _ => panic!("Index out of bounds"), + } - + } + } + }) +} From 4443cd0486cbbdd88e752f92196038e4905bc5ac Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Wed, 3 Jul 2024 15:41:48 +0300 Subject: [PATCH 04/12] fix: formatting fix --- .../tests/inc/index/only_test/struct_named.rs | 16 +- .../tests/inc/index/only_test/struct_tuple.rs | 14 +- .../tests/inc/index/struct_named.rs | 13 +- .../tests/inc/index/struct_named_manual.rs | 17 +- .../tests/inc/index/struct_tuple.rs | 8 +- .../tests/inc/index/struct_tuple_manual.rs | 15 +- .../derive_tools_meta/src/derive/index.rs | 241 +++++++++++------- module/core/derive_tools_meta/src/lib.rs | 29 ++- 8 files changed, 215 insertions(+), 138 deletions(-) diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs index ee4c29ed1a..e399769fb2 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs @@ -1,10 +1,8 @@ -#[test] -fn index() { - let x = StructNamed { a: true, b: false }; - let exp = (true, false); - let got = (x[0], x[1]); - - dbg!(exp, got); - - assert_eq!(got, exp); +#[ test ] +fn index() +{ + let x = StructNamed{ a: true, b: false }; + let exp = ( true, false ); + let got = ( x[0], x[1] ); + assert_eq!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs index 74cb9670f0..e8606916c5 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs @@ -1,8 +1,8 @@ -#[test] -fn index() { - let x = StructTuple(7, 12); - let exp = (7, 12); - let got = (x[0], x[1]); - - assert_eq!(got, exp); +#[ test ] +fn index() +{ + let x = StructTuple( 7, 12 ); + let exp = ( 7, 12 ); + let got = ( x[0], x[1] ); + assert_eq!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/index/struct_named.rs b/module/core/derive_tools/tests/inc/index/struct_named.rs index 66197a3693..fcfaa6fecb 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named.rs @@ -1,10 +1,11 @@ use super::*; -#[allow(dead_code)] -#[derive(the_module::Index, Debug)] -struct StructNamed { - a: T, - b: T, +#[ allow( dead_code ) ] +#[ derive( the_module::Index ) ] +struct StructNamed +{ + a : T, + b : T, } -include!("./only_test/struct_named.rs"); +include!( "./only_test/struct_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs index a07641fe28..27df6485a1 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs @@ -1,20 +1,25 @@ use core::ops::Index; -struct StructNamed { +#[ allow( dead_code ) ] +struct StructNamed< T > +{ a: T, b: T, } -impl Index for StructNamed { +impl< T > Index< usize > for StructNamed< T > +{ type Output = T; - fn index(&self, index: usize) -> &Self::Output { - match index { + fn index( &self, index: usize ) -> &Self::Output + { + match index + { 0 => &self.a, 1 => &self.b, - _ => panic!("Index out of bounds"), + _ => panic!( "Index out of bounds" ), } } } -include!("./only_test/struct_named.rs"); +include!( "./only_test/struct_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/struct_tuple.rs index c4748cedcc..de3fcf6abd 100644 --- a/module/core/derive_tools/tests/inc/index/struct_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/struct_tuple.rs @@ -1,7 +1,7 @@ use super::*; -#[allow(dead_code)] -#[derive(the_module::Index)] -struct StructNamed(T, T); +#[ allow( dead_code ) ] +#[ derive( the_module::Index ) ] +struct StructNamed< T >( T, T ); -include!("./only_test/struct_tuple.rs"); +include!( "./only_test/struct_tuple.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs index 0f67af17dc..207974eceb 100644 --- a/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs @@ -1,17 +1,20 @@ use core::ops::Index; -struct StructTuple(T, T); +struct StructTuple< T >( T, T ); -impl Index for StructTuple { +impl< T > Index< usize > for StructTuple< T > +{ type Output = T; - fn index(&self, index: usize) -> &Self::Output { - match index { + fn index( &self, index: usize ) -> &Self::Output + { + match index + { 0 => &self.0, 1 => &self.1, - _ => panic!("Index out of bounds"), + _ => panic!( "Index out of bounds" ), } } } -include!("./only_test/struct_tuple.rs"); +include!( "./only_test/struct_tuple.rs" ); diff --git a/module/core/derive_tools_meta/src/derive/index.rs b/module/core/derive_tools_meta/src/derive/index.rs index 996262bb8f..ef6b46509d 100644 --- a/module/core/derive_tools_meta/src/derive/index.rs +++ b/module/core/derive_tools_meta/src/derive/index.rs @@ -1,43 +1,88 @@ use super::*; -use macro_tools::{attr, diag, generic_params, struct_like::StructLike, Result}; +use macro_tools:: +{ + attr, + diag, + generic_params, + struct_like::StructLike, + Result +}; -pub fn index(input: proc_macro::TokenStream) -> Result { +pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { let original_input = input.clone(); - let parsed = syn::parse::(input)?; - let has_debug = attr::has_debug(parsed.attrs().iter())?; + let parsed = syn::parse::< StructLike >( input )?; + let has_debug = attr::has_debug( parsed.attrs().iter() )?; let item_name = &parsed.ident(); - let (_generics_with_defaults, generics_impl, generics_ty, generics_where) = generic_params::decompose(&parsed.generics()); + let ( _generics_with_defaults, generics_impl, generics_ty, generics_where ) + = generic_params::decompose( &parsed.generics() ); - let result = match parsed { - StructLike::Struct(ref item) => generate_struct(item_name, &generics_impl, &generics_ty, &generics_where, &item.fields), - StructLike::Enum(_) => todo!(), - StructLike::Unit(_) => todo!(), + let result = match parsed + { + StructLike::Struct( ref item ) => + generate_struct + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + &item.fields + ), + StructLike::Enum(_) => + todo!(), + StructLike::Unit(_) => + todo!(), }?; - if has_debug { - let about = format!("derive : Not\nstructure : {item_name}"); - diag::report_print(about, &original_input, &result); + if has_debug + { + let about = format!( "derive : Not\nstructure : {item_name}" ); + diag::report_print( about, &original_input, &result ); } - Ok(result) + Ok( result ) } /// An aggregator function to generate `Index` implementation for unit, tuple structs and the ones with named fields -fn generate_struct( +fn generate_struct +( item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated, - generics_ty: &syn::punctuated::Punctuated, - generics_where: &syn::punctuated::Punctuated, + generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, fields: &syn::Fields, -) -> Result { - match fields { - syn::Fields::Named(fields) => generate_struct_named_fields(item_name, generics_impl, generics_ty, generics_where, fields), - syn::Fields::Unnamed(fields) => generate_struct_tuple_fields(item_name, generics_impl, generics_ty, generics_where, fields), - syn::Fields::Unit => Err(syn::Error::new( - fields.span(), - "cannot infer type: Empty type cannot be indexed", - )), +) +-> Result< proc_macro2::TokenStream > +{ + match fields + { + + syn::Fields::Named( fields ) => + generate_struct_named_fields + ( + item_name, + generics_impl, + generics_ty, + generics_where, + fields + ), + + syn::Fields::Unnamed( fields ) => + generate_struct_tuple_fields + ( + item_name, + generics_impl, + generics_ty, + generics_where, + fields + ), + + syn::Fields::Unit => Err( + syn::Error::new( + fields.span(), + "cannot infer type: Empty type cannot be indexed", + ) + ), } } @@ -62,61 +107,69 @@ fn generate_struct( /// #[ inline( always ) ] /// fn index( &self, index: usize ) -> &Self::Output /// { -/// match index { -/// 0 => &self.0, -/// _ => panic!("Index out of bounds"), +/// match index +/// { +/// 0 => &self.0, +/// _ => panic!( "Index out of bounds" ), /// } /// } /// } /// ``` /// -fn generate_struct_tuple_fields( +fn generate_struct_tuple_fields +( item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated, - generics_ty: &syn::punctuated::Punctuated, - generics_where: &syn::punctuated::Punctuated, + generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, fields: &syn::FieldsUnnamed, -) -> Result { - let fields = &fields.unnamed; +) +-> Result< proc_macro2::TokenStream > +{ + let fields = fields.unnamed.clone(); // Get the type of the first field as the output type let field_type = match fields.first() { Some(field) => &field.ty, - None => { - return Err(syn::Error::new( - fields.span(), - "cannot infer type: Empty type cannot be indexed", - )) - } + None => return Err( + syn::Error::new( fields.span(), "cannot infer type: Empty type cannot be indexed" ) + ), }; // Generate match arms for each field - let match_arms = fields.iter().enumerate().map_result(|(i, field)| { - let field_name = &field.ident; - Result::Ok(quote! { - #i => &self.#field_name - }) - })?; - - Ok(qt! { - #[ automatically_derived ] - impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > - where - #generics_where + let match_arms = fields.iter().enumerate().map(|( index, field )| { - type Output = #field_type; - - fn index( &self, index: usize ) -> &Self::Output + let field_name = &field.ident; + qt! { - match index { - #(#match_arms,)* - _ => panic!("Index out of bounds"), + #index => &self.#field_name } + } + ); + Ok + ( + qt! + { + #[ automatically_derived ] + impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > + where + #generics_where + { + type Output = #field_type; + #[ inline( always ) ] + fn index( &self, index: usize ) -> &Self::Output + { + match index + { + #(#match_arms,)* + _ => panic!( "Index out of bounds" ), + } + } } } - }) + ) } /// Generates `Index` implementation for structs with tuple fields @@ -147,53 +200,67 @@ fn generate_struct_tuple_fields( /// #[ inline( always ) ] /// fn index( &self, index: usize ) -> &Self::Output /// { -/// match index { -/// 0 => &self.a, -/// _ => panic!("Index out of bounds"), +/// match index +/// { +/// 0 => &self.a, +/// _ => panic!( "Index out of bounds" ), /// } /// } /// } /// ``` /// -fn generate_struct_named_fields( +fn generate_struct_named_fields +( item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated, - generics_ty: &syn::punctuated::Punctuated, - generics_where: &syn::punctuated::Punctuated, + generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, fields: &syn::FieldsNamed, -) -> Result { +) +-> Result< proc_macro2::TokenStream > +{ let fields = fields.named.clone(); // Get the type of the first field as the output type let field_type = match fields.first() { Some(field) => &field.ty, - None => return Err(syn::Error::new(fields.span(), "Empty type cannot be indexed")), + None => return Err( + syn::Error::new(fields.span(), "Empty type cannot be indexed") + ), }; + // Generate match arms for each field - let match_arms = fields.iter().enumerate().map_result(|(i, field)| { - let field_name = &field.ident; - Result::Ok(quote! { - #i => &self.#field_name - }) - })?; - - Ok(qt! { - #[ automatically_derived ] - impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > - where - #generics_where + let match_arms = fields.iter().enumerate().map(|( index, field )| { - type Output = #field_type; - - fn index( &self, index: usize ) -> &Self::Output + let field_name = &field.ident; + qt! { - match index { - #(#match_arms,)* - _ => panic!("Index out of bounds"), + #index => &self.#field_name } + } + ); + Ok + ( + qt! + { + #[ automatically_derived ] + impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > + where + #generics_where + { + type Output = #field_type; + #[ inline( always ) ] + fn index( &self, index: usize ) -> &Self::Output + { + match index + { + #(#match_arms,)* + _ => panic!( "Index out of bounds" ), + } + } } } - }) + ) } diff --git a/module/core/derive_tools_meta/src/lib.rs b/module/core/derive_tools_meta/src/lib.rs index 2d0d0b334c..884d254d3e 100644 --- a/module/core/derive_tools_meta/src/lib.rs +++ b/module/core/derive_tools_meta/src/lib.rs @@ -581,21 +581,24 @@ pub fn phantom( _attr: proc_macro::TokenStream, input : proc_macro::TokenStream /// Instead of manually implementing `Index< T >` for `IsTransparent`: /// /// ```rust -/// pub struct IsTransparent{ +/// pub struct IsTransparent< T > +/// { /// a : T, -/// }; +/// } /// -/// impl Index< usize > for IsTransparent +/// impl< T > Index< usize > for IsTransparent< T > /// { -/// type Output = T; -/// -/// #[ inline( always ) ] -/// fn index(&self, index: usize ) -> &Self::Output { -/// match index { -/// 0 => &self.a, -/// _ => panic!("Index out of bounds"), +/// type Output = T; +/// +/// #[ inline( always ) ] +/// fn index( &self, index: usize ) -> &Self::Output +/// { +/// match index +/// { +/// 0 => &self.a, +/// _ => panic!( "Index out of bounds" ), /// } -/// } +/// } /// } /// ``` /// @@ -604,8 +607,8 @@ pub fn phantom( _attr: proc_macro::TokenStream, input : proc_macro::TokenStream /// ```rust /// # use derive_tools_meta::*; /// #[ derive( Index ) ] -/// pub struct IsTransparent { -/// a: T +/// pub struct IsTransparent< T > { +/// a : T ///}; /// ``` /// From b4ca258462be63736a4d52a4447357c72dcea2a8 Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Thu, 4 Jul 2024 10:07:15 +0300 Subject: [PATCH 05/12] fut: enum and struct macros implementation --- .../tests/inc/index/enum_named.rs | 11 + .../tests/inc/index/enum_named_manual.rs | 32 ++ .../tests/inc/index/enum_tuple.rs | 12 + .../tests/inc/index/enum_tuple_manual.rs | 28 ++ .../tests/inc/index/only_test/enum_named.rs | 9 + .../tests/inc/index/only_test/enum_tuple.rs | 11 + .../tests/inc/index/struct_named.rs | 2 +- .../tests/inc/index/struct_tuple.rs | 2 +- module/core/derive_tools/tests/inc/mod.rs | 5 + .../derive_tools_meta/src/derive/index.rs | 326 ++++++++++++++++-- 10 files changed, 401 insertions(+), 37 deletions(-) create mode 100644 module/core/derive_tools/tests/inc/index/enum_named.rs create mode 100644 module/core/derive_tools/tests/inc/index/enum_named_manual.rs create mode 100644 module/core/derive_tools/tests/inc/index/enum_tuple.rs create mode 100644 module/core/derive_tools/tests/inc/index/enum_tuple_manual.rs create mode 100644 module/core/derive_tools/tests/inc/index/only_test/enum_named.rs create mode 100644 module/core/derive_tools/tests/inc/index/only_test/enum_tuple.rs diff --git a/module/core/derive_tools/tests/inc/index/enum_named.rs b/module/core/derive_tools/tests/inc/index/enum_named.rs new file mode 100644 index 0000000000..d1e6283108 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/enum_named.rs @@ -0,0 +1,11 @@ +use super::*; + +#[ allow( dead_code ) ] +#[ derive( the_module::Index ) ] +enum EnumNamed < T > +{ + A { a : T, b : T }, + B { a : T, b : T } +} + +include!( "./only_test/enum_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/enum_named_manual.rs b/module/core/derive_tools/tests/inc/index/enum_named_manual.rs new file mode 100644 index 0000000000..69eacc60ea --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/enum_named_manual.rs @@ -0,0 +1,32 @@ + use core::ops::Index; + +#[ allow( dead_code) ] + +enum EnumNamed < T > +{ + A { a : T, b : T }, + B { a : T, b : T }, +} + +impl< T > Index< usize > for EnumNamed< T > +{ + type Output = T; + + fn index( &self, index: usize ) -> &Self::Output + { + match index + { + 0 => match self + { + EnumNamed::A { a, .. } | EnumNamed::B { a, .. } => a, + }, + 1 => match self + { + EnumNamed::A { b, .. } | EnumNamed::B { b, .. } => b, + }, + _ => panic!( "Index out of bounds" ), + } + } +} + +include!( "./only_test/enum_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/enum_tuple.rs b/module/core/derive_tools/tests/inc/index/enum_tuple.rs new file mode 100644 index 0000000000..069f3c2520 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/enum_tuple.rs @@ -0,0 +1,12 @@ +use super::*; + +#[ allow( dead_code ) ] +#[ derive( the_module::Index ) ] +enum EnumTuple < T > +{ + A( T ), + B( T ) +} + +include!( "./only_test/enum_tuple.rs" ); + diff --git a/module/core/derive_tools/tests/inc/index/enum_tuple_manual.rs b/module/core/derive_tools/tests/inc/index/enum_tuple_manual.rs new file mode 100644 index 0000000000..cb30354631 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/enum_tuple_manual.rs @@ -0,0 +1,28 @@ + use core::ops::Index; + +#[ allow( dead_code) ] + +enum EnumTuple < T > +{ + A ( T ), + B ( T ) +} + +impl< T > Index< usize > for EnumTuple< T > +{ + type Output = T; + + fn index( &self, index: usize ) -> &Self::Output + { + match index + { + 0 => match self + { + EnumTuple::A( a ) | EnumTuple::B( a ) => a, + }, + _ => panic!( "Index out of bounds" ), + } + } +} + +include!( "./only_test/enum_tuple.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/only_test/enum_named.rs b/module/core/derive_tools/tests/inc/index/only_test/enum_named.rs new file mode 100644 index 0000000000..36c83b7b93 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/enum_named.rs @@ -0,0 +1,9 @@ +#[ test ] +fn index() +{ + let x = EnumNamed::A{ a : false, b : true }; + let exp = ( false, true ); + let got = ( x[0], x[1] ); + + assert_eq!(got, exp); +} diff --git a/module/core/derive_tools/tests/inc/index/only_test/enum_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/enum_tuple.rs new file mode 100644 index 0000000000..cdfbbe1b63 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/enum_tuple.rs @@ -0,0 +1,11 @@ +#[ test ] +fn index() +{ + let x = EnumTuple::A( true ); + let x1 = EnumTuple::B( false ); + let exp = (true, false); + let got = (x[0], x1[0]); + + assert_eq!(got, exp); +} + diff --git a/module/core/derive_tools/tests/inc/index/struct_named.rs b/module/core/derive_tools/tests/inc/index/struct_named.rs index fcfaa6fecb..74d4dfdf84 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named.rs @@ -2,7 +2,7 @@ use super::*; #[ allow( dead_code ) ] #[ derive( the_module::Index ) ] -struct StructNamed +struct StructNamed< T > { a : T, b : T, diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/struct_tuple.rs index de3fcf6abd..3cd64ebdb3 100644 --- a/module/core/derive_tools/tests/inc/index/struct_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/struct_tuple.rs @@ -2,6 +2,6 @@ use super::*; #[ allow( dead_code ) ] #[ derive( the_module::Index ) ] -struct StructNamed< T >( T, T ); +struct StructTuple< T >( T, T ); include!( "./only_test/struct_tuple.rs" ); diff --git a/module/core/derive_tools/tests/inc/mod.rs b/module/core/derive_tools/tests/inc/mod.rs index c499972943..46fba5be19 100644 --- a/module/core/derive_tools/tests/inc/mod.rs +++ b/module/core/derive_tools/tests/inc/mod.rs @@ -303,6 +303,11 @@ mod index_tests mod struct_named; mod struct_named_manual; + mod struct_tuple; mod struct_tuple_manual; + mod enum_named; + mod enum_named_manual; + mod enum_tuple; + mod enum_tuple_manual; } diff --git a/module/core/derive_tools_meta/src/derive/index.rs b/module/core/derive_tools_meta/src/derive/index.rs index ef6b46509d..656b25dfd9 100644 --- a/module/core/derive_tools_meta/src/derive/index.rs +++ b/module/core/derive_tools_meta/src/derive/index.rs @@ -8,6 +8,8 @@ use macro_tools:: Result }; + +/// Generates [Index](core::ops::Index) trait implementation. pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { let original_input = input.clone(); let parsed = syn::parse::< StructLike >( input )?; @@ -28,10 +30,21 @@ pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr &generics_where, &item.fields ), - StructLike::Enum(_) => - todo!(), - StructLike::Unit(_) => - todo!(), + StructLike::Enum( ref item ) => + generate_enum + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + &item.variants, + ), + StructLike::Unit( ref item ) => Err( + syn::Error::new( + item.fields.span(), + "cannot infer type: Empty type cannot be indexed", + ) + ), }?; if has_debug @@ -43,7 +56,7 @@ pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr Ok( result ) } -/// An aggregator function to generate `Index` implementation for unit, tuple structs and the ones with named fields +/// An aggregator function to generate `Index` implementation for tuple and named structs fn generate_struct ( item_name: &syn::Ident, @@ -56,7 +69,6 @@ fn generate_struct { match fields { - syn::Fields::Named( fields ) => generate_struct_named_fields ( @@ -86,6 +98,8 @@ fn generate_struct } } + + /// Generates `Index` implementation for structs with tuple fields /// /// # Example @@ -109,8 +123,8 @@ fn generate_struct /// { /// match index /// { -/// 0 => &self.0, -/// _ => panic!( "Index out of bounds" ), +/// 0 => &self.0, +/// _ => panic!( "Index out of bounds" ), /// } /// } /// } @@ -129,21 +143,13 @@ fn generate_struct_tuple_fields { let fields = fields.unnamed.clone(); - // Get the type of the first field as the output type - let field_type = match fields.first() { - Some(field) => &field.ty, - None => return Err( - syn::Error::new( fields.span(), "cannot infer type: Empty type cannot be indexed" ) - ), - }; - // Generate match arms for each field - let match_arms = fields.iter().enumerate().map(|( index, field )| + let match_arms = fields.iter().enumerate().map(|( index, _field )| { - let field_name = &field.ident; + let index = syn::Index::from( index ); qt! { - #index => &self.#field_name + #index => &self.#index } } ); @@ -157,22 +163,22 @@ fn generate_struct_tuple_fields where #generics_where { - type Output = #field_type; + type Output = T; #[ inline( always ) ] fn index( &self, index: usize ) -> &Self::Output { - match index - { - #(#match_arms,)* - _ => panic!( "Index out of bounds" ), - } + match index + { + #(#match_arms,)* + _ => panic!( "Index out of bounds" ), + } } } } ) } -/// Generates `Index` implementation for structs with tuple fields +/// Generates `Index` implementation for structs with named fields /// /// # Example /// @@ -180,7 +186,7 @@ fn generate_struct_tuple_fields /// ```rust /// # use derive_tools_meta::Index; /// #[ derive( Index ) ] -/// pub struct Struct< T > i +/// pub struct Struct< T > /// { /// a: T, /// } @@ -222,21 +228,269 @@ fn generate_struct_named_fields { let fields = fields.named.clone(); - // Get the type of the first field as the output type - let field_type = match fields.first() { - Some(field) => &field.ty, - None => return Err( - syn::Error::new(fields.span(), "Empty type cannot be indexed") + // Generate match arms for each field + let match_arms = fields.iter().enumerate().map(|( index, field )| + { + let index = syn::Index::from( index ); + let field_name = &field.ident; + qt! + { + #index => &self.#field_name + } + } + ); + + Ok + ( + qt! + { + #[ automatically_derived ] + impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > + where + #generics_where + { + type Output = T; + #[ inline( always ) ] + fn index( &self, index: usize ) -> &Self::Output + { + match index + { + #(#match_arms,)* + _ => panic!( "Index out of bounds" ), + } + } + } + } + ) +} + + + +/// An aggregator function to generate `Index` implementation for Enum +fn generate_enum +( + item_name: &syn::Ident, + generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + variants : &syn::punctuated::Punctuated, +) +-> Result< proc_macro2::TokenStream > +{ + let fields = match variants.first() + { + Some( variant ) => &variant.fields, + None => return Err( + syn::Error::new( + generics_ty.span(), + "cannot infer type: Empty type cannot be indexed", + ) ), }; + let idents = variants.iter().map( | v | v.ident.clone() ).collect::< Vec< _ > >(); + + + match fields + { + syn::Fields::Named( ref item) => + generate_enum_named_fields + ( + item_name, + generics_impl, + generics_ty, + generics_where, + &idents, + item + ), + syn::Fields::Unnamed( ref item ) => + generate_enum_tuple_fields + ( + item_name, + generics_impl, + generics_ty, + generics_where, + &idents, + item + ), + syn::Fields::Unit => Err( + syn::Error::new( + fields.span(), + "cannot infer type: Empty type cannot be indexed", + ) + ), + } +} + + +/// Generates `Index` implementation for enums with tuple fields +/// +/// # Example +/// +/// ## Input +/// ```rust +/// # use derive_tools_meta::Index; +/// #[ derive( Index ) ] +/// pub enum EnumTuple< T > +/// { +/// A( T ), +/// B( T ), +/// } +/// ``` +/// +/// ## Output +/// ```rust +/// pub enum EnumTuple< T > +/// { +/// A( T ), +/// B( T ), +/// } +/// +/// #[ automatically_derived ] +/// impl< T > ::core::ops::Index< usize > for EnumTuple< T > +/// { +/// type Output = T; +/// #[ inline( always ) ] +/// fn index( &self, index: usize ) -> &Self::Output +/// { +/// match index +/// { +/// 0 => match self +/// { +/// EnumTuple::A( a ) | EnumTuple::B( a ) => a, +/// }, +/// _ => panic!( "Index out of bounds" ), +/// } +/// } +/// } +/// ``` +/// + +fn generate_enum_tuple_fields +( + item_name: &syn::Ident, + generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + variant_idents : &[ syn::Ident ], + fields: &syn::FieldsUnnamed, +) +-> Result< proc_macro2::TokenStream > +{ + let fields = fields.unnamed.clone(); + + // Generate match arms for each field + let match_arms = fields.iter().enumerate().map(|( index, _field )| + { + let index = syn::Index::from( index ); + qt! + { + #index => match self + { + #( #item_name::#variant_idents( v ) )|* => v + } + } + } + ); + + Ok + ( + qt! + { + #[ automatically_derived ] + impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > + where + #generics_where + { + type Output = T; + #[ inline( always ) ] + fn index( &self, index: usize ) -> &Self::Output + { + match index + { + #(#match_arms)* + _ => panic!( "Index out of bounds" ), + } + } + } + } + ) +} + + + + + +/// Generates `Index` implementation for enums with named fields +/// +/// # Example +/// +/// ## Input +/// ```rust +/// # use derive_tools_meta::Index; +/// #[ derive( Index ) ] +/// pub enum EnumNamed< T > +/// { +/// A { a: T, b: T }, +/// B { a: T, b: T }, +/// } +/// ``` +/// +/// ## Output +/// ```rust +/// pub enum EnumNamed< T > +/// { +/// A { a: T, b: T }, +/// B { a: T, b: T }, +/// } +/// +/// #[ automatically_derived ] +/// impl< T > ::core::ops::Index< usize > for EnumNamed< T > +/// { +/// type Output = T; +/// #[ inline( always ) ] +/// fn index( &self, index: usize ) -> &Self::Output +/// { +/// match index +/// { +/// 0 => match self +/// { +/// EnumNamed::A { a, .. } | EnumNamed::B { a, .. } => a, +/// }, +/// 1 => match self +// { +/// EnumNamed::A { b, .. } | EnumNamed::B { b, .. } => b, +/// }, +/// _ => panic!( "Index out of bounds" ), +/// } +/// } +/// } +/// ``` +/// +fn generate_enum_named_fields +( + item_name: &syn::Ident, + generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + variant_idents : &[ syn::Ident ], + fields: &syn::FieldsNamed, +) +-> Result< proc_macro2::TokenStream > +{ + let fields = fields.named.clone(); + // Generate match arms for each field let match_arms = fields.iter().enumerate().map(|( index, field )| { + let index = syn::Index::from( index ); let field_name = &field.ident; qt! { - #index => &self.#field_name + #index => match self + { + #( #item_name::#variant_idents { #field_name: v, .. } )|* => v, + } } } ); @@ -250,13 +504,13 @@ fn generate_struct_named_fields where #generics_where { - type Output = #field_type; + type Output = T; #[ inline( always ) ] fn index( &self, index: usize ) -> &Self::Output { match index { - #(#match_arms,)* + #(#match_arms)* _ => panic!( "Index out of bounds" ), } } @@ -264,3 +518,5 @@ fn generate_struct_named_fields } ) } + + From 21b70064092cd0511c626857827c1509d9590650 Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Fri, 5 Jul 2024 01:56:20 +0300 Subject: [PATCH 06/12] refactor: #[ index ] attribute implementation and general macros, tests refactor --- .../tests/inc/index/enum_named.rs | 5 +- .../tests/inc/index/enum_named_manual.rs | 11 +- .../tests/inc/index/enum_tuple.rs | 12 -- .../tests/inc/index/enum_unnamed.rs | 14 ++ ...tuple_manual.rs => enum_unnamed_manual.rs} | 16 +- .../tests/inc/index/only_test/enum_named.rs | 6 +- .../tests/inc/index/only_test/enum_tuple.rs | 11 -- .../tests/inc/index/only_test/enum_unnamed.rs | 10 + .../tests/inc/index/only_test/struct_named.rs | 7 +- .../tests/inc/index/only_test/struct_tuple.rs | 4 +- .../tests/inc/index/struct_named.rs | 6 +- .../tests/inc/index/struct_named_manual.rs | 14 +- .../tests/inc/index/struct_tuple.rs | 2 +- .../tests/inc/index/struct_tuple_manual.rs | 13 +- module/core/derive_tools/tests/inc/mod.rs | 8 +- .../derive_tools_meta/src/derive/index.rs | 185 ++++++++++++++---- .../src/derive/index/field_attributes.rs | 121 ++++++++++++ .../src/derive/index/item_attributes.rs | 122 ++++++++++++ module/core/derive_tools_meta/src/lib.rs | 9 +- 19 files changed, 465 insertions(+), 111 deletions(-) delete mode 100644 module/core/derive_tools/tests/inc/index/enum_tuple.rs create mode 100644 module/core/derive_tools/tests/inc/index/enum_unnamed.rs rename module/core/derive_tools/tests/inc/index/{enum_tuple_manual.rs => enum_unnamed_manual.rs} (51%) delete mode 100644 module/core/derive_tools/tests/inc/index/only_test/enum_tuple.rs create mode 100644 module/core/derive_tools/tests/inc/index/only_test/enum_unnamed.rs create mode 100644 module/core/derive_tools_meta/src/derive/index/field_attributes.rs create mode 100644 module/core/derive_tools_meta/src/derive/index/item_attributes.rs diff --git a/module/core/derive_tools/tests/inc/index/enum_named.rs b/module/core/derive_tools/tests/inc/index/enum_named.rs index d1e6283108..bff8cf740b 100644 --- a/module/core/derive_tools/tests/inc/index/enum_named.rs +++ b/module/core/derive_tools/tests/inc/index/enum_named.rs @@ -4,8 +4,7 @@ use super::*; #[ derive( the_module::Index ) ] enum EnumNamed < T > { - A { a : T, b : T }, - B { a : T, b : T } -} + A { a : T }, + } include!( "./only_test/enum_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/enum_named_manual.rs b/module/core/derive_tools/tests/inc/index/enum_named_manual.rs index 69eacc60ea..3dac83591d 100644 --- a/module/core/derive_tools/tests/inc/index/enum_named_manual.rs +++ b/module/core/derive_tools/tests/inc/index/enum_named_manual.rs @@ -4,8 +4,7 @@ enum EnumNamed < T > { - A { a : T, b : T }, - B { a : T, b : T }, + A { a : Vec< T > }, } impl< T > Index< usize > for EnumNamed< T > @@ -18,11 +17,15 @@ impl< T > Index< usize > for EnumNamed< T > { 0 => match self { - EnumNamed::A { a, .. } | EnumNamed::B { a, .. } => a, + EnumNamed::A { a, .. } => &a[0], }, 1 => match self { - EnumNamed::A { b, .. } | EnumNamed::B { b, .. } => b, + EnumNamed::A { a, .. } => &a[1], + }, + 2 => match self + { + EnumNamed::A { a, .. } => &a[2], }, _ => panic!( "Index out of bounds" ), } diff --git a/module/core/derive_tools/tests/inc/index/enum_tuple.rs b/module/core/derive_tools/tests/inc/index/enum_tuple.rs deleted file mode 100644 index 069f3c2520..0000000000 --- a/module/core/derive_tools/tests/inc/index/enum_tuple.rs +++ /dev/null @@ -1,12 +0,0 @@ -use super::*; - -#[ allow( dead_code ) ] -#[ derive( the_module::Index ) ] -enum EnumTuple < T > -{ - A( T ), - B( T ) -} - -include!( "./only_test/enum_tuple.rs" ); - diff --git a/module/core/derive_tools/tests/inc/index/enum_unnamed.rs b/module/core/derive_tools/tests/inc/index/enum_unnamed.rs new file mode 100644 index 0000000000..bb533c631a --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/enum_unnamed.rs @@ -0,0 +1,14 @@ +use super::*; + +#[ allow( dead_code ) ] +#[ derive( the_module::Index ) ] +#[ debug ] +enum Enum +{ + Nothing, + #[ index ] + IndexVector( Vec ) +} + +include!( "./only_test/enum_unnamed.rs" ); + diff --git a/module/core/derive_tools/tests/inc/index/enum_tuple_manual.rs b/module/core/derive_tools/tests/inc/index/enum_unnamed_manual.rs similarity index 51% rename from module/core/derive_tools/tests/inc/index/enum_tuple_manual.rs rename to module/core/derive_tools/tests/inc/index/enum_unnamed_manual.rs index cb30354631..02d79286ff 100644 --- a/module/core/derive_tools/tests/inc/index/enum_tuple_manual.rs +++ b/module/core/derive_tools/tests/inc/index/enum_unnamed_manual.rs @@ -1,14 +1,12 @@ use core::ops::Index; #[ allow( dead_code) ] - -enum EnumTuple < T > +enum Enum < T > { - A ( T ), - B ( T ) + IndexVector( Vec< T > ), } -impl< T > Index< usize > for EnumTuple< T > +impl< T > Index< usize > for Enum< T > { type Output = T; @@ -18,11 +16,15 @@ impl< T > Index< usize > for EnumTuple< T > { 0 => match self { - EnumTuple::A( a ) | EnumTuple::B( a ) => a, + Enum::IndexVector( a ) => &a[0], + }, + 1 => match self + { + Enum::IndexVector( a ) => &a[1], }, _ => panic!( "Index out of bounds" ), } } } -include!( "./only_test/enum_tuple.rs" ); +include!( "./only_test/enum_unnamed.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/only_test/enum_named.rs b/module/core/derive_tools/tests/inc/index/only_test/enum_named.rs index 36c83b7b93..8f0e66bfaf 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/enum_named.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/enum_named.rs @@ -1,9 +1,9 @@ #[ test ] fn index() { - let x = EnumNamed::A{ a : false, b : true }; - let exp = ( false, true ); - let got = ( x[0], x[1] ); + let x = EnumNamed::A{ a : vec![ 44, 12, 9 ]}; + let exp = ( 44, 9 ); + let got = ( x[0], x[2] ); assert_eq!(got, exp); } diff --git a/module/core/derive_tools/tests/inc/index/only_test/enum_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/enum_tuple.rs deleted file mode 100644 index cdfbbe1b63..0000000000 --- a/module/core/derive_tools/tests/inc/index/only_test/enum_tuple.rs +++ /dev/null @@ -1,11 +0,0 @@ -#[ test ] -fn index() -{ - let x = EnumTuple::A( true ); - let x1 = EnumTuple::B( false ); - let exp = (true, false); - let got = (x[0], x1[0]); - - assert_eq!(got, exp); -} - diff --git a/module/core/derive_tools/tests/inc/index/only_test/enum_unnamed.rs b/module/core/derive_tools/tests/inc/index/only_test/enum_unnamed.rs new file mode 100644 index 0000000000..7d21f74549 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/enum_unnamed.rs @@ -0,0 +1,10 @@ +#[ test ] +fn index() +{ + let x = Enum::IndexVector(vec![12, 55]); + let exp = (12, 55); + let got = (x[0], x[1]); + + assert_eq!(got, exp); +} + diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs index e399769fb2..24c539936c 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs @@ -1,8 +1,9 @@ #[ test ] fn index() { - let x = StructNamed{ a: true, b: false }; - let exp = ( true, false ); - let got = ( x[0], x[1] ); + let x = StructNamed{ a: vec![false, true] }; + let v = vec![false, true]; + let exp = ( v[ 0 ], v[ 1 ] ); + let got = ( x[ 0 ], x[ 1 ] ); assert_eq!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs index e8606916c5..5439a7d05c 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs @@ -1,8 +1,8 @@ #[ test ] fn index() { - let x = StructTuple( 7, 12 ); - let exp = ( 7, 12 ); + let x = StructTuple( vec![2,44,81], 2, 12); + let exp = ( 2, 44 ); let got = ( x[0], x[1] ); assert_eq!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/index/struct_named.rs b/module/core/derive_tools/tests/inc/index/struct_named.rs index 74d4dfdf84..93ac1fbf05 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named.rs @@ -1,11 +1,11 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] use super::*; -#[ allow( dead_code ) ] #[ derive( the_module::Index ) ] struct StructNamed< T > { - a : T, - b : T, + a : Vec< T >, } include!( "./only_test/struct_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs index 27df6485a1..ff84e039f0 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs @@ -1,24 +1,18 @@ use core::ops::Index; #[ allow( dead_code ) ] -struct StructNamed< T > +struct StructNamed< T > { - a: T, - b: T, + a: Vec< T > } -impl< T > Index< usize > for StructNamed< T > +impl< T > Index< usize > for StructNamed< T > { type Output = T; fn index( &self, index: usize ) -> &Self::Output { - match index - { - 0 => &self.a, - 1 => &self.b, - _ => panic!( "Index out of bounds" ), - } + &self.a[index] } } diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/struct_tuple.rs index 3cd64ebdb3..e6740b7a54 100644 --- a/module/core/derive_tools/tests/inc/index/struct_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/struct_tuple.rs @@ -2,6 +2,6 @@ use super::*; #[ allow( dead_code ) ] #[ derive( the_module::Index ) ] -struct StructTuple< T >( T, T ); +struct StructTuple< T >( Vec< T >, u8, u8 ); include!( "./only_test/struct_tuple.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs index 207974eceb..579d21a773 100644 --- a/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs @@ -1,18 +1,19 @@ use core::ops::Index; -struct StructTuple< T >( T, T ); +#[ allow( dead_code) ] +struct StructTuple< T >(Vec< T >, u8, u8); -impl< T > Index< usize > for StructTuple< T > +impl< T > Index< usize > for StructTuple< T > { type Output = T; fn index( &self, index: usize ) -> &Self::Output { - match index - { - 0 => &self.0, - 1 => &self.1, + match index { + 0 => &self.0[0], + 1 => &self.0[1], _ => panic!( "Index out of bounds" ), + } } } diff --git a/module/core/derive_tools/tests/inc/mod.rs b/module/core/derive_tools/tests/inc/mod.rs index 46fba5be19..73344a6d28 100644 --- a/module/core/derive_tools/tests/inc/mod.rs +++ b/module/core/derive_tools/tests/inc/mod.rs @@ -303,11 +303,11 @@ mod index_tests mod struct_named; mod struct_named_manual; - mod struct_tuple; + // mod struct_tuple; mod struct_tuple_manual; - mod enum_named; + // mod enum_named; mod enum_named_manual; - mod enum_tuple; - mod enum_tuple_manual; + // mod enum_unnamed; + mod enum_unnamed_manual; } diff --git a/module/core/derive_tools_meta/src/derive/index.rs b/module/core/derive_tools_meta/src/derive/index.rs index 656b25dfd9..ff5e328e07 100644 --- a/module/core/derive_tools_meta/src/derive/index.rs +++ b/module/core/derive_tools_meta/src/derive/index.rs @@ -1,13 +1,20 @@ + use super::*; use macro_tools:: { - attr, - diag, - generic_params, - struct_like::StructLike, - Result + attr, diag, generic_params, proc_macro2::TokenStream, struct_like::StructLike, Result }; +#[ path = "index/field_attributes.rs" ] +mod field_attributes; +use field_attributes::*; + +#[ path = "index/item_attributes.rs" ] +mod item_attributes; +use item_attributes::*; + + + /// Generates [Index](core::ops::Index) trait implementation. pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { @@ -15,29 +22,49 @@ pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr let parsed = syn::parse::< StructLike >( input )?; let has_debug = attr::has_debug( parsed.attrs().iter() )?; let item_name = &parsed.ident(); - + let item_attrs = ItemAttributes::from_attrs( parsed.attrs().iter() )?; + let ( _generics_with_defaults, generics_impl, generics_ty, generics_where ) = generic_params::decompose( &parsed.generics() ); let result = match parsed { - StructLike::Struct( ref item ) => - generate_struct + StructLike::Enum( ref item ) => + { + let variants_result : Result< Vec< proc_macro2::TokenStream > > = item.variants.iter().map( | variant | + { + generate_enum ( - item_name, + item_name, + &item_attrs, &generics_impl, &generics_ty, - &generics_where, - &item.fields - ), - StructLike::Enum( ref item ) => - generate_enum + &generics_where, + variant, + &original_input, + &item.variants, + )?; + +Ok( qt!{} ) + } + + ).collect(); + + let variants = variants_result?; + + Ok(qt! + { + #( #variants )* + }) + }, + StructLike::Struct( ref item ) => + generate_struct ( item_name, &generics_impl, &generics_ty, &generics_where, - &item.variants, + &item.fields, ), StructLike::Unit( ref item ) => Err( syn::Error::new( @@ -67,6 +94,7 @@ fn generate_struct ) -> Result< proc_macro2::TokenStream > { + match fields { syn::Fields::Named( fields ) => @@ -149,7 +177,7 @@ fn generate_struct_tuple_fields let index = syn::Index::from( index ); qt! { - #index => &self.#index + #index => &self.0[index] } } ); @@ -229,13 +257,13 @@ fn generate_struct_named_fields let fields = fields.named.clone(); // Generate match arms for each field - let match_arms = fields.iter().enumerate().map(|( index, field )| + let generated = fields.iter().enumerate().map(|( _index, field )| { - let index = syn::Index::from( index ); let field_name = &field.ident; + qt! { - #index => &self.#field_name + &self.#field_name[index] } } ); @@ -253,11 +281,8 @@ fn generate_struct_named_fields #[ inline( always ) ] fn index( &self, index: usize ) -> &Self::Output { - match index - { - #(#match_arms,)* - _ => panic!( "Index out of bounds" ), - } + + #(#generated)* } } } @@ -270,30 +295,60 @@ fn generate_struct_named_fields fn generate_enum ( item_name: &syn::Ident, + item_attrs : &ItemAttributes, generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + variant : &syn::Variant, + _original_input : &proc_macro::TokenStream, variants : &syn::punctuated::Punctuated, ) -> Result< proc_macro2::TokenStream > { - let fields = match variants.first() - { - Some( variant ) => &variant.fields, - None => return Err( - syn::Error::new( - generics_ty.span(), - "cannot infer type: Empty type cannot be indexed", - ) - ), - }; + + let fields = &variant.fields; let idents = variants.iter().map( | v | v.ident.clone() ).collect::< Vec< _ > >(); + let attrs = FieldAttributes::from_attrs( variant.attrs.iter() )?; + + + if !attrs.index.value( item_attrs.index.value( true ) ) + { + return Ok( qt!{} ) + } + + if fields.len() <= 0 + { + return Ok( qt!{} ) + } + + let ( args, _use_src ) = if fields.len() == 1 + { + let field = fields.iter().next().unwrap(); + ( + qt!{ #field }, + qt!{ src }, + ) + } + else + { + let src_i = ( 0..fields.len() ).map( | e | + { + let i = syn::Index::from( e ); + qt!{ src.#i, } + }); + ( + qt!{ #fields }, + qt!{ #( #src_i )* }, + // qt!{ src.0, src.1 }, + ) + }; + match fields { - syn::Fields::Named( ref item) => + syn::Fields::Named( ref item ) => generate_enum_named_fields ( item_name, @@ -310,16 +365,18 @@ fn generate_enum generics_impl, generics_ty, generics_where, + item, &idents, - item + &args, ), syn::Fields::Unit => Err( syn::Error::new( - fields.span(), + variant.fields.span(), "cannot infer type: Empty type cannot be indexed", ) ), } + } @@ -372,13 +429,57 @@ fn generate_enum_tuple_fields generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - variant_idents : &[ syn::Ident ], fields: &syn::FieldsUnnamed, + variant_idents : &[ syn::Ident ], + _args: &TokenStream ) -> Result< proc_macro2::TokenStream > { let fields = fields.unnamed.clone(); + + // Generate match arms for each field + let match_arms = fields.iter().enumerate().map(|( index, _field )| + { + let index = syn::Index::from( index ); + + + qt! + { + #index => match self + { + #( #item_name::#variant_idents( v ) )|* => &v[index] + } + } + } + ); + + + Ok + ( + qt! + { + #[ automatically_derived ] + impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > + where + #generics_where + { + type Output = T; + #[ inline( always ) ] + fn index( &self, index: usize ) -> &Self::Output + { + match index + { + + #(#match_arms)* + _ => panic!( "Index out of bounds" ), + } + } + } + } + ) + +/* // Generate match arms for each field let match_arms = fields.iter().enumerate().map(|( index, _field )| { @@ -414,7 +515,7 @@ fn generate_enum_tuple_fields } } } - ) + )*/ } @@ -485,11 +586,13 @@ fn generate_enum_named_fields { let index = syn::Index::from( index ); let field_name = &field.ident; + + dbg!(&field_name); qt! { #index => match self { - #( #item_name::#variant_idents { #field_name: v, .. } )|* => v, + #( #item_name::#variant_idents { #field_name: v, .. } )|* => v[index], } } } @@ -519,4 +622,4 @@ fn generate_enum_named_fields ) } - + diff --git a/module/core/derive_tools_meta/src/derive/index/field_attributes.rs b/module/core/derive_tools_meta/src/derive/index/field_attributes.rs new file mode 100644 index 0000000000..a61c5499dc --- /dev/null +++ b/module/core/derive_tools_meta/src/derive/index/field_attributes.rs @@ -0,0 +1,121 @@ + + use macro_tools:: + { + ct, + syn_err, + syn, + qt, + Result, + AttributePropertyComponent, + AttributePropertyOptionalSingletone, + Assign, + }; + + /// Represents the attributes of a struct. Aggregates all its attributes. + #[ derive( Debug, Default ) ] + pub struct FieldAttributes + { + /// Attribute for customizing the mutation process. + pub index : AttributePropertyIndex, + + pub debug : AttributePropertyDebug, + } + + impl FieldAttributes + { + /// Constructs a `ItemAttributes` instance from an iterator of attributes. + /// + /// This function parses the provided attributes and assigns them to the + /// appropriate fields in the `ItemAttributes` struct. + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > + { + let mut result = Self::default(); + + // Closure to generate an error message for unknown attributes. + let error = | attr : & syn::Attribute | -> syn::Error + { + let known_attributes = ct::str::format! + ( + "Known attributes are: {}, {}.", + "debug", + AttributePropertyIndex::KEYWORD, + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute( key1 = val1, key2 = val2 ) ]'\n {known_attributes}\n But got: '{}'", + qt! { #attr } + ) + }; + + for attr in attrs + { + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; + let key_str = format!( "{}", key_ident ); + // if attr::is_standard( & key_str ) + // { + // continue; + // } + match key_str.as_ref() + { + AttributePropertyIndex::KEYWORD => result.assign( AttributePropertyIndex::from( true ) ), + "debug" => {}, + _ => {}, + // _ => return Err( error( attr ) ), + } + } + + Ok( result ) + } + } + + +impl< IntoT > Assign< AttributePropertyIndex, IntoT > for FieldAttributes +where + IntoT : Into< AttributePropertyIndex >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.index.assign( component.into() ); + } +} + + + + + + // == Attribute properties + + /// Marker type for attribute property to specify whether to provide a sketch as a hint. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct AttributePropertyDebugMarker; + + impl AttributePropertyComponent for AttributePropertyDebugMarker + { + const KEYWORD : & 'static str = "debug"; + } + + /// Specifies whether to provide a sketch as a hint. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + pub type AttributePropertyDebug = AttributePropertyOptionalSingletone< AttributePropertyDebugMarker >; + + // == + + /// Marker type for attribute property to indicate whether a custom code should be generated. + /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct AttributePropertyIndexMarker; + + impl AttributePropertyComponent for AttributePropertyIndexMarker + { + const KEYWORD : & 'static str = "index"; + } + + /// Indicates whether a custom code should be generated. + /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. + pub type AttributePropertyIndex = AttributePropertyOptionalSingletone< AttributePropertyIndexMarker >; + + // == test code + diff --git a/module/core/derive_tools_meta/src/derive/index/item_attributes.rs b/module/core/derive_tools_meta/src/derive/index/item_attributes.rs new file mode 100644 index 0000000000..600de27532 --- /dev/null +++ b/module/core/derive_tools_meta/src/derive/index/item_attributes.rs @@ -0,0 +1,122 @@ + + use macro_tools:: + { + ct, + syn_err, + syn, + qt, + Result, + AttributePropertyComponent, + AttributePropertyOptionalSingletone, + Assign, + }; + + /// Represents the attributes of a struct. Aggregates all its attributes. + #[ derive( Debug, Default ) ] + pub struct ItemAttributes + { + /// Attribute for customizing the mutation process. + pub index : AttributePropertyIndex, + + pub debug : AttributePropertyDebug, + + } + + impl ItemAttributes + { + /// Constructs a `ItemAttributes` instance from an iterator of attributes. + /// + /// This function parses the provided attributes and assigns them to the + /// appropriate fields in the `ItemAttributes` struct. + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > + { + let mut result = Self::default(); + + // Closure to generate an error message for unknown attributes. + let error = | attr : & syn::Attribute | -> syn::Error + { + let known_attributes = ct::str::format! + ( + "Known attributes are: {}, {}.", + "debug", + AttributePropertyIndex::KEYWORD, + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute ]'\n {known_attributes}\n But got: '{}'", + qt! { #attr } + ) + }; + + for attr in attrs + { + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; + let key_str = format!( "{}", key_ident ); + // if attr::is_standard( & key_str ) + // { + // continue; + // } + match key_str.as_ref() + { + AttributePropertyIndex::KEYWORD => result.assign( AttributePropertyIndex::from(true) ), + "debug" => {}, + _ => {}, + // _ => return Err( error( attr ) ), + } + } + + Ok( result ) + } + } + + + impl< IntoT > Assign< AttributePropertyIndex, IntoT > for ItemAttributes +where + IntoT : Into< AttributePropertyIndex >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.index.assign( component.into() ); + } +} + + + + + // == Attribute properties + + /// Marker type for attribute property to specify whether to provide a sketch as a hint. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct AttributePropertyDebugMarker; + + impl AttributePropertyComponent for AttributePropertyDebugMarker + { + const KEYWORD : & 'static str = "debug"; + } + + /// Specifies whether to provide a sketch as a hint. + /// Defaults to `false`, which means no hint is provided unless explicitly requested. + pub type AttributePropertyDebug = AttributePropertyOptionalSingletone< AttributePropertyDebugMarker >; + + // == + + /// Marker type for attribute property to indicate whether a custom code should be generated. + /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct AttributePropertyIndexMarker; + + impl AttributePropertyComponent for AttributePropertyIndexMarker + { + const KEYWORD : & 'static str = "index"; + } + + /// Indicates whether a custom code should be generated. + /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. + pub type AttributePropertyIndex = AttributePropertyOptionalSingletone< AttributePropertyIndexMarker >; + + // == test code + + diff --git a/module/core/derive_tools_meta/src/lib.rs b/module/core/derive_tools_meta/src/lib.rs index 884d254d3e..c914047506 100644 --- a/module/core/derive_tools_meta/src/lib.rs +++ b/module/core/derive_tools_meta/src/lib.rs @@ -615,7 +615,14 @@ pub fn phantom( _attr: proc_macro::TokenStream, input : proc_macro::TokenStream #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_index" ) ] -#[ proc_macro_derive( Index, attributes( debug ) ) ] +#[ proc_macro_derive +( + Index, + attributes( + debug, + index + ) +)] pub fn derive_index( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { let result = derive::index::index( input ); From d9bc7477ad4562a17b76b22e4d6e5e7ed5855593 Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Fri, 5 Jul 2024 13:55:16 +0300 Subject: [PATCH 07/12] ref: basic implementation --- .../tests/inc/index/enum_named.rs | 10 - .../tests/inc/index/enum_named_manual.rs | 35 - .../tests/inc/index/enum_unnamed.rs | 14 - .../tests/inc/index/enum_unnamed_manual.rs | 30 - .../tests/inc/index/only_test/enum_named.rs | 9 - .../tests/inc/index/only_test/enum_unnamed.rs | 10 - .../index/only_test/struct_multiple_named.rs | 15 + .../index/only_test/struct_multiple_tuple.rs | 9 + .../tests/inc/index/only_test/struct_named.rs | 8 +- .../tests/inc/index/only_test/struct_tuple.rs | 5 +- .../inc/index/struct_multiple_named_manual.rs | 21 + .../inc/index/struct_multiple_tuple_manual.rs | 17 + .../tests/inc/index/struct_named.rs | 2 + .../tests/inc/index/struct_named_manual.rs | 4 +- .../tests/inc/index/struct_tuple_manual.rs | 11 +- module/core/derive_tools/tests/inc/mod.rs | 15 +- .../derive_tools_meta/src/derive/index.rs | 607 +----------------- .../src/derive/index/field_attributes.rs | 167 +++-- .../src/derive/index/item_attributes.rs | 169 ++--- 19 files changed, 233 insertions(+), 925 deletions(-) delete mode 100644 module/core/derive_tools/tests/inc/index/enum_named.rs delete mode 100644 module/core/derive_tools/tests/inc/index/enum_named_manual.rs delete mode 100644 module/core/derive_tools/tests/inc/index/enum_unnamed.rs delete mode 100644 module/core/derive_tools/tests/inc/index/enum_unnamed_manual.rs delete mode 100644 module/core/derive_tools/tests/inc/index/only_test/enum_named.rs delete mode 100644 module/core/derive_tools/tests/inc/index/only_test/enum_unnamed.rs create mode 100644 module/core/derive_tools/tests/inc/index/only_test/struct_multiple_named.rs create mode 100644 module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs create mode 100644 module/core/derive_tools/tests/inc/index/struct_multiple_named_manual.rs create mode 100644 module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs diff --git a/module/core/derive_tools/tests/inc/index/enum_named.rs b/module/core/derive_tools/tests/inc/index/enum_named.rs deleted file mode 100644 index bff8cf740b..0000000000 --- a/module/core/derive_tools/tests/inc/index/enum_named.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::*; - -#[ allow( dead_code ) ] -#[ derive( the_module::Index ) ] -enum EnumNamed < T > -{ - A { a : T }, - } - -include!( "./only_test/enum_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/enum_named_manual.rs b/module/core/derive_tools/tests/inc/index/enum_named_manual.rs deleted file mode 100644 index 3dac83591d..0000000000 --- a/module/core/derive_tools/tests/inc/index/enum_named_manual.rs +++ /dev/null @@ -1,35 +0,0 @@ - use core::ops::Index; - -#[ allow( dead_code) ] - -enum EnumNamed < T > -{ - A { a : Vec< T > }, -} - -impl< T > Index< usize > for EnumNamed< T > -{ - type Output = T; - - fn index( &self, index: usize ) -> &Self::Output - { - match index - { - 0 => match self - { - EnumNamed::A { a, .. } => &a[0], - }, - 1 => match self - { - EnumNamed::A { a, .. } => &a[1], - }, - 2 => match self - { - EnumNamed::A { a, .. } => &a[2], - }, - _ => panic!( "Index out of bounds" ), - } - } -} - -include!( "./only_test/enum_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/enum_unnamed.rs b/module/core/derive_tools/tests/inc/index/enum_unnamed.rs deleted file mode 100644 index bb533c631a..0000000000 --- a/module/core/derive_tools/tests/inc/index/enum_unnamed.rs +++ /dev/null @@ -1,14 +0,0 @@ -use super::*; - -#[ allow( dead_code ) ] -#[ derive( the_module::Index ) ] -#[ debug ] -enum Enum -{ - Nothing, - #[ index ] - IndexVector( Vec ) -} - -include!( "./only_test/enum_unnamed.rs" ); - diff --git a/module/core/derive_tools/tests/inc/index/enum_unnamed_manual.rs b/module/core/derive_tools/tests/inc/index/enum_unnamed_manual.rs deleted file mode 100644 index 02d79286ff..0000000000 --- a/module/core/derive_tools/tests/inc/index/enum_unnamed_manual.rs +++ /dev/null @@ -1,30 +0,0 @@ - use core::ops::Index; - -#[ allow( dead_code) ] -enum Enum < T > -{ - IndexVector( Vec< T > ), -} - -impl< T > Index< usize > for Enum< T > -{ - type Output = T; - - fn index( &self, index: usize ) -> &Self::Output - { - match index - { - 0 => match self - { - Enum::IndexVector( a ) => &a[0], - }, - 1 => match self - { - Enum::IndexVector( a ) => &a[1], - }, - _ => panic!( "Index out of bounds" ), - } - } -} - -include!( "./only_test/enum_unnamed.rs" ); diff --git a/module/core/derive_tools/tests/inc/index/only_test/enum_named.rs b/module/core/derive_tools/tests/inc/index/only_test/enum_named.rs deleted file mode 100644 index 8f0e66bfaf..0000000000 --- a/module/core/derive_tools/tests/inc/index/only_test/enum_named.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[ test ] -fn index() -{ - let x = EnumNamed::A{ a : vec![ 44, 12, 9 ]}; - let exp = ( 44, 9 ); - let got = ( x[0], x[2] ); - - assert_eq!(got, exp); -} diff --git a/module/core/derive_tools/tests/inc/index/only_test/enum_unnamed.rs b/module/core/derive_tools/tests/inc/index/only_test/enum_unnamed.rs deleted file mode 100644 index 7d21f74549..0000000000 --- a/module/core/derive_tools/tests/inc/index/only_test/enum_unnamed.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[ test ] -fn index() -{ - let x = Enum::IndexVector(vec![12, 55]); - let exp = (12, 55); - let got = (x[0], x[1]); - - assert_eq!(got, exp); -} - diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_named.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_named.rs new file mode 100644 index 0000000000..f03ad5af7d --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_named.rs @@ -0,0 +1,15 @@ +#[ test ] +fn index() +{ + let x = StructMultipleNamed + { + a: vec![ 12, 22 ], + b: vec![ 33, 55 ] + }; + let v = vec![ 33, 55 ]; + let exp = ( v[ 0 ], v[ 1 ] ); + let got = ( x[ 0 ], x[ 1 ] ); + + assert_eq!( got, exp ); +} + diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs new file mode 100644 index 0000000000..dcb8a03b99 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs @@ -0,0 +1,9 @@ +#[ test ] +fn index() +{ + let x = StructTupleMultiple( false, vec![ 2, 44, 81 ] ); + let exp = ( 2, 44 ); + let got = ( x[ 0 ], x[ 1 ] ); + assert_eq!( got, exp ); +} + diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs index 24c539936c..53b2ab3f79 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs @@ -1,9 +1,13 @@ #[ test ] fn index() { - let x = StructNamed{ a: vec![false, true] }; - let v = vec![false, true]; + let x = StructNamed + { + a: vec![ false, true ] + }; + let v = vec![ false, true ]; let exp = ( v[ 0 ], v[ 1 ] ); let got = ( x[ 0 ], x[ 1 ] ); + assert_eq!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs index 5439a7d05c..ef3d479590 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs @@ -1,8 +1,9 @@ #[ test ] fn index() { - let x = StructTuple( vec![2,44,81], 2, 12); + let x = StructTuple( vec![ 2, 44, 81 ] ); let exp = ( 2, 44 ); - let got = ( x[0], x[1] ); + let got = ( x[ 0 ], x[ 1 ] ); + assert_eq!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/index/struct_multiple_named_manual.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_named_manual.rs new file mode 100644 index 0000000000..ce46c871fd --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_named_manual.rs @@ -0,0 +1,21 @@ +use core::ops::Index; + +#[ allow( dead_code ) ] +struct StructMultipleNamed< T > +{ + a: Vec< T >, + b: Vec< T >, +} + +impl< T > Index< usize > for StructMultipleNamed< T > +{ + type Output = T; + + fn index( &self, index : usize ) -> &Self::Output + { + &self.b[ index ] + } +} + +include!( "./only_test/struct_multiple_named.rs" ); + diff --git a/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs new file mode 100644 index 0000000000..2b2dfafbc3 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs @@ -0,0 +1,17 @@ +use core::ops::Index; + +#[ allow( dead_code) ] +struct StructTupleMultiple< T >( bool, Vec< T > ); + +impl< T > Index< usize > for StructTupleMultiple< T > +{ + type Output = T; + + fn index( &self, index : usize ) -> &Self::Output + { + &self.1[ index ] + } +} + +include!( "./only_test/struct_multiple_tuple.rs" ); + diff --git a/module/core/derive_tools/tests/inc/index/struct_named.rs b/module/core/derive_tools/tests/inc/index/struct_named.rs index 93ac1fbf05..f2f28e0e96 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named.rs @@ -3,8 +3,10 @@ use super::*; #[ derive( the_module::Index ) ] +#[debug] struct StructNamed< T > { + #[index] a : Vec< T >, } diff --git a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs index ff84e039f0..f09d4bbeb1 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs @@ -10,9 +10,9 @@ impl< T > Index< usize > for StructNamed< T > { type Output = T; - fn index( &self, index: usize ) -> &Self::Output + fn index( &self, index : usize ) -> &Self::Output { - &self.a[index] + &self.a[ index ] } } diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs index 579d21a773..14582ff909 100644 --- a/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs @@ -1,20 +1,15 @@ use core::ops::Index; #[ allow( dead_code) ] -struct StructTuple< T >(Vec< T >, u8, u8); +struct StructTuple< T >( Vec< T > ); impl< T > Index< usize > for StructTuple< T > { type Output = T; - fn index( &self, index: usize ) -> &Self::Output + fn index( &self, index : usize ) -> &Self::Output { - match index { - 0 => &self.0[0], - 1 => &self.0[1], - _ => panic!( "Index out of bounds" ), - - } + &self.0[ index ] } } diff --git a/module/core/derive_tools/tests/inc/mod.rs b/module/core/derive_tools/tests/inc/mod.rs index 73344a6d28..5f9596af1d 100644 --- a/module/core/derive_tools/tests/inc/mod.rs +++ b/module/core/derive_tools/tests/inc/mod.rs @@ -301,13 +301,16 @@ mod index_tests #[ allow( unused_imports ) ] use super::*; - mod struct_named; + // Must be implemented: + // - Try-build test for Enum + // - Try-build test for Unit + // - Try-build test for Named Struct without fields + + // mod struct_named; mod struct_named_manual; + mod struct_multiple_named_manual; // mod struct_tuple; mod struct_tuple_manual; - // mod enum_named; - mod enum_named_manual; - // mod enum_unnamed; - mod enum_unnamed_manual; - + mod struct_multiple_tuple_manual; + } diff --git a/module/core/derive_tools_meta/src/derive/index.rs b/module/core/derive_tools_meta/src/derive/index.rs index ff5e328e07..e4bf46dbfa 100644 --- a/module/core/derive_tools_meta/src/derive/index.rs +++ b/module/core/derive_tools_meta/src/derive/index.rs @@ -1,77 +1,30 @@ - use super::*; use macro_tools:: { - attr, diag, generic_params, proc_macro2::TokenStream, struct_like::StructLike, Result + attr, + diag, + struct_like::StructLike, + Result }; -#[ path = "index/field_attributes.rs" ] -mod field_attributes; -use field_attributes::*; - -#[ path = "index/item_attributes.rs" ] -mod item_attributes; -use item_attributes::*; - - - - -/// Generates [Index](core::ops::Index) trait implementation. pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { let original_input = input.clone(); let parsed = syn::parse::< StructLike >( input )?; let has_debug = attr::has_debug( parsed.attrs().iter() )?; let item_name = &parsed.ident(); - let item_attrs = ItemAttributes::from_attrs( parsed.attrs().iter() )?; - - let ( _generics_with_defaults, generics_impl, generics_ty, generics_where ) - = generic_params::decompose( &parsed.generics() ); let result = match parsed { - StructLike::Enum( ref item ) => - { - let variants_result : Result< Vec< proc_macro2::TokenStream > > = item.variants.iter().map( | variant | - { - generate_enum - ( - item_name, - &item_attrs, - &generics_impl, - &generics_ty, - &generics_where, - variant, - &original_input, - &item.variants, - )?; - -Ok( qt!{} ) - } - - ).collect(); - - let variants = variants_result?; - - Ok(qt! - { - #( #variants )* - }) - }, - StructLike::Struct( ref item ) => - generate_struct - ( - item_name, - &generics_impl, - &generics_ty, - &generics_where, - &item.fields, - ), - StructLike::Unit( ref item ) => Err( + StructLike::Struct( ref item ) => Err( syn::Error::new( item.fields.span(), - "cannot infer type: Empty type cannot be indexed", + "Not implemented yet", ) ), + StructLike::Enum(_) => + unimplemented!( "Index not implemented for Enum" ), + StructLike::Unit(_) => + unimplemented!( "Index not implemented for Unit" ), }?; if has_debug @@ -83,543 +36,3 @@ Ok( qt!{} ) Ok( result ) } -/// An aggregator function to generate `Index` implementation for tuple and named structs -fn generate_struct -( - item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - fields: &syn::Fields, -) --> Result< proc_macro2::TokenStream > -{ - - match fields - { - syn::Fields::Named( fields ) => - generate_struct_named_fields - ( - item_name, - generics_impl, - generics_ty, - generics_where, - fields - ), - - syn::Fields::Unnamed( fields ) => - generate_struct_tuple_fields - ( - item_name, - generics_impl, - generics_ty, - generics_where, - fields - ), - - syn::Fields::Unit => Err( - syn::Error::new( - fields.span(), - "cannot infer type: Empty type cannot be indexed", - ) - ), - } -} - - - -/// Generates `Index` implementation for structs with tuple fields -/// -/// # Example -/// -/// ## Input -/// ```rust -/// # use derive_tools_meta::Index; -/// #[ derive( Index ) ] -/// pub struct Struct< T >( T ); -/// ``` -/// -/// ## Output -/// ```rust -/// pub struct Struct< T >( T ); -/// #[ automatically_derived ] -/// impl< T > ::core::ops::Index< usize > for Struct< T > -/// { -/// type Output = T; -/// #[ inline( always ) ] -/// fn index( &self, index: usize ) -> &Self::Output -/// { -/// match index -/// { -/// 0 => &self.0, -/// _ => panic!( "Index out of bounds" ), -/// } -/// } -/// } -/// ``` -/// - -fn generate_struct_tuple_fields -( - item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - fields: &syn::FieldsUnnamed, -) --> Result< proc_macro2::TokenStream > -{ - let fields = fields.unnamed.clone(); - - // Generate match arms for each field - let match_arms = fields.iter().enumerate().map(|( index, _field )| - { - let index = syn::Index::from( index ); - qt! - { - #index => &self.0[index] - } - } - ); - - Ok - ( - qt! - { - #[ automatically_derived ] - impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > - where - #generics_where - { - type Output = T; - #[ inline( always ) ] - fn index( &self, index: usize ) -> &Self::Output - { - match index - { - #(#match_arms,)* - _ => panic!( "Index out of bounds" ), - } - } - } - } - ) -} - -/// Generates `Index` implementation for structs with named fields -/// -/// # Example -/// -/// ## Input -/// ```rust -/// # use derive_tools_meta::Index; -/// #[ derive( Index ) ] -/// pub struct Struct< T > -/// { -/// a: T, -/// } -/// ``` -/// -/// ## Output -/// ```rust -/// pub struct Struct< T > -/// { -/// a: T, -/// }; -/// -/// #[ automatically_derived ] -/// impl< T > ::core::ops::Index< usize > for Struct< T > -/// { -/// type Output = T; -/// #[ inline( always ) ] -/// fn index( &self, index: usize ) -> &Self::Output -/// { -/// match index -/// { -/// 0 => &self.a, -/// _ => panic!( "Index out of bounds" ), -/// } -/// } -/// } -/// ``` -/// - -fn generate_struct_named_fields -( - item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - fields: &syn::FieldsNamed, -) --> Result< proc_macro2::TokenStream > -{ - let fields = fields.named.clone(); - - // Generate match arms for each field - let generated = fields.iter().enumerate().map(|( _index, field )| - { - let field_name = &field.ident; - - qt! - { - &self.#field_name[index] - } - } - ); - - Ok - ( - qt! - { - #[ automatically_derived ] - impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > - where - #generics_where - { - type Output = T; - #[ inline( always ) ] - fn index( &self, index: usize ) -> &Self::Output - { - - #(#generated)* - } - } - } - ) -} - - - -/// An aggregator function to generate `Index` implementation for Enum -fn generate_enum -( - item_name: &syn::Ident, - item_attrs : &ItemAttributes, - generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - variant : &syn::Variant, - _original_input : &proc_macro::TokenStream, - variants : &syn::punctuated::Punctuated, -) --> Result< proc_macro2::TokenStream > -{ - - let fields = &variant.fields; - - let idents = variants.iter().map( | v | v.ident.clone() ).collect::< Vec< _ > >(); - - let attrs = FieldAttributes::from_attrs( variant.attrs.iter() )?; - - - if !attrs.index.value( item_attrs.index.value( true ) ) - { - return Ok( qt!{} ) - } - - if fields.len() <= 0 - { - return Ok( qt!{} ) - } - - let ( args, _use_src ) = if fields.len() == 1 - { - let field = fields.iter().next().unwrap(); - ( - qt!{ #field }, - qt!{ src }, - ) - } - else - { - let src_i = ( 0..fields.len() ).map( | e | - { - let i = syn::Index::from( e ); - qt!{ src.#i, } - }); - ( - qt!{ #fields }, - qt!{ #( #src_i )* }, - // qt!{ src.0, src.1 }, - ) - }; - - - match fields - { - syn::Fields::Named( ref item ) => - generate_enum_named_fields - ( - item_name, - generics_impl, - generics_ty, - generics_where, - &idents, - item - ), - syn::Fields::Unnamed( ref item ) => - generate_enum_tuple_fields - ( - item_name, - generics_impl, - generics_ty, - generics_where, - item, - &idents, - &args, - ), - syn::Fields::Unit => Err( - syn::Error::new( - variant.fields.span(), - "cannot infer type: Empty type cannot be indexed", - ) - ), - } - -} - - -/// Generates `Index` implementation for enums with tuple fields -/// -/// # Example -/// -/// ## Input -/// ```rust -/// # use derive_tools_meta::Index; -/// #[ derive( Index ) ] -/// pub enum EnumTuple< T > -/// { -/// A( T ), -/// B( T ), -/// } -/// ``` -/// -/// ## Output -/// ```rust -/// pub enum EnumTuple< T > -/// { -/// A( T ), -/// B( T ), -/// } -/// -/// #[ automatically_derived ] -/// impl< T > ::core::ops::Index< usize > for EnumTuple< T > -/// { -/// type Output = T; -/// #[ inline( always ) ] -/// fn index( &self, index: usize ) -> &Self::Output -/// { -/// match index -/// { -/// 0 => match self -/// { -/// EnumTuple::A( a ) | EnumTuple::B( a ) => a, -/// }, -/// _ => panic!( "Index out of bounds" ), -/// } -/// } -/// } -/// ``` -/// - -fn generate_enum_tuple_fields -( - item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - fields: &syn::FieldsUnnamed, - variant_idents : &[ syn::Ident ], - _args: &TokenStream -) --> Result< proc_macro2::TokenStream > -{ - let fields = fields.unnamed.clone(); - - - // Generate match arms for each field - let match_arms = fields.iter().enumerate().map(|( index, _field )| - { - let index = syn::Index::from( index ); - - - qt! - { - #index => match self - { - #( #item_name::#variant_idents( v ) )|* => &v[index] - } - } - } - ); - - - Ok - ( - qt! - { - #[ automatically_derived ] - impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > - where - #generics_where - { - type Output = T; - #[ inline( always ) ] - fn index( &self, index: usize ) -> &Self::Output - { - match index - { - - #(#match_arms)* - _ => panic!( "Index out of bounds" ), - } - } - } - } - ) - -/* - // Generate match arms for each field - let match_arms = fields.iter().enumerate().map(|( index, _field )| - { - let index = syn::Index::from( index ); - qt! - { - #index => match self - { - #( #item_name::#variant_idents( v ) )|* => v - } - } - } - ); - - Ok - ( - qt! - { - #[ automatically_derived ] - impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > - where - #generics_where - { - type Output = T; - #[ inline( always ) ] - fn index( &self, index: usize ) -> &Self::Output - { - match index - { - #(#match_arms)* - _ => panic!( "Index out of bounds" ), - } - } - } - } - )*/ -} - - - - - -/// Generates `Index` implementation for enums with named fields -/// -/// # Example -/// -/// ## Input -/// ```rust -/// # use derive_tools_meta::Index; -/// #[ derive( Index ) ] -/// pub enum EnumNamed< T > -/// { -/// A { a: T, b: T }, -/// B { a: T, b: T }, -/// } -/// ``` -/// -/// ## Output -/// ```rust -/// pub enum EnumNamed< T > -/// { -/// A { a: T, b: T }, -/// B { a: T, b: T }, -/// } -/// -/// #[ automatically_derived ] -/// impl< T > ::core::ops::Index< usize > for EnumNamed< T > -/// { -/// type Output = T; -/// #[ inline( always ) ] -/// fn index( &self, index: usize ) -> &Self::Output -/// { -/// match index -/// { -/// 0 => match self -/// { -/// EnumNamed::A { a, .. } | EnumNamed::B { a, .. } => a, -/// }, -/// 1 => match self -// { -/// EnumNamed::A { b, .. } | EnumNamed::B { b, .. } => b, -/// }, -/// _ => panic!( "Index out of bounds" ), -/// } -/// } -/// } -/// ``` -/// -fn generate_enum_named_fields -( - item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - variant_idents : &[ syn::Ident ], - fields: &syn::FieldsNamed, -) --> Result< proc_macro2::TokenStream > -{ - let fields = fields.named.clone(); - - // Generate match arms for each field - let match_arms = fields.iter().enumerate().map(|( index, field )| - { - let index = syn::Index::from( index ); - let field_name = &field.ident; - - dbg!(&field_name); - qt! - { - #index => match self - { - #( #item_name::#variant_idents { #field_name: v, .. } )|* => v[index], - } - } - } - ); - - Ok - ( - qt! - { - #[ automatically_derived ] - impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > - where - #generics_where - { - type Output = T; - #[ inline( always ) ] - fn index( &self, index: usize ) -> &Self::Output - { - match index - { - #(#match_arms)* - _ => panic!( "Index out of bounds" ), - } - } - } - } - ) -} - - diff --git a/module/core/derive_tools_meta/src/derive/index/field_attributes.rs b/module/core/derive_tools_meta/src/derive/index/field_attributes.rs index a61c5499dc..65eeb1ab50 100644 --- a/module/core/derive_tools_meta/src/derive/index/field_attributes.rs +++ b/module/core/derive_tools_meta/src/derive/index/field_attributes.rs @@ -1,74 +1,72 @@ +use macro_tools:: +{ + ct, + syn_err, + syn, + qt, + Result, + AttributePropertyComponent, + AttributePropertyOptionalSingletone, + Assign, +}; + +/// +/// Attributes of a field / variant +/// + +/// Represents the attributes of a struct. Aggregates all its attributes. +#[ derive( Debug, Default ) ] +pub struct FieldAttributes +{ + /// Specifies whether we should generate Index implementation for the field. + pub index : AttributePropertyIndex, +} - use macro_tools:: +impl FieldAttributes +{ + /// Constructs a `ItemAttributes` instance from an iterator of attributes. + /// + /// This function parses the provided attributes and assigns them to the + /// appropriate fields in the `ItemAttributes` struct. + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > { - ct, - syn_err, - syn, - qt, - Result, - AttributePropertyComponent, - AttributePropertyOptionalSingletone, - Assign, - }; + let mut result = Self::default(); - /// Represents the attributes of a struct. Aggregates all its attributes. - #[ derive( Debug, Default ) ] - pub struct FieldAttributes + // Closure to generate an error message for unknown attributes. + let error = | attr : & syn::Attribute | -> syn::Error { - /// Attribute for customizing the mutation process. - pub index : AttributePropertyIndex, - - pub debug : AttributePropertyDebug, - } - - impl FieldAttributes - { - /// Constructs a `ItemAttributes` instance from an iterator of attributes. - /// - /// This function parses the provided attributes and assigns them to the - /// appropriate fields in the `ItemAttributes` struct. - pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > + let known_attributes = ct::concatcp! + ( + "Known attributes are : ", + "debug", + ", ", AttributePropertyIndex::KEYWORD, + ".", + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute ]'\n {known_attributes}\n But got: '{}'", + qt! { #attr } + ) + }; + + for attr in attrs { - let mut result = Self::default(); - - // Closure to generate an error message for unknown attributes. - let error = | attr : & syn::Attribute | -> syn::Error - { - let known_attributes = ct::str::format! - ( - "Known attributes are: {}, {}.", - "debug", - AttributePropertyIndex::KEYWORD, - ); - syn_err! - ( - attr, - "Expects an attribute of format '#[ attribute( key1 = val1, key2 = val2 ) ]'\n {known_attributes}\n But got: '{}'", - qt! { #attr } - ) - }; - - for attr in attrs + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; + let key_str = format!( "{}", key_ident ); + + match key_str.as_ref() { - let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; - let key_str = format!( "{}", key_ident ); - // if attr::is_standard( & key_str ) - // { - // continue; - // } - match key_str.as_ref() - { - AttributePropertyIndex::KEYWORD => result.assign( AttributePropertyIndex::from( true ) ), - "debug" => {}, - _ => {}, - // _ => return Err( error( attr ) ), - } + AttributePropertyIndex::KEYWORD => result.assign( AttributePropertyIndex::from( true ) ), + "debug" => {}, + _ => {}, + // _ => return Err( error( attr ) ), } - - Ok( result ) } - } + Ok( result ) + } +} impl< IntoT > Assign< AttributePropertyIndex, IntoT > for FieldAttributes where @@ -82,40 +80,21 @@ where } +// == Attribute properties +/// Marker type for attribute property to indicate whether a index code should be generated. +/// Defaults to `false`, meaning no index code is generated unless explicitly requested. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyIndexMarker; +impl AttributePropertyComponent for AttributePropertyIndexMarker +{ + const KEYWORD : & 'static str = "index"; +} - // == Attribute properties - - /// Marker type for attribute property to specify whether to provide a sketch as a hint. - /// Defaults to `false`, which means no hint is provided unless explicitly requested. - #[ derive( Debug, Default, Clone, Copy ) ] - pub struct AttributePropertyDebugMarker; - - impl AttributePropertyComponent for AttributePropertyDebugMarker - { - const KEYWORD : & 'static str = "debug"; - } - - /// Specifies whether to provide a sketch as a hint. - /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub type AttributePropertyDebug = AttributePropertyOptionalSingletone< AttributePropertyDebugMarker >; - - // == - - /// Marker type for attribute property to indicate whether a custom code should be generated. - /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. - #[ derive( Debug, Default, Clone, Copy ) ] - pub struct AttributePropertyIndexMarker; - - impl AttributePropertyComponent for AttributePropertyIndexMarker - { - const KEYWORD : & 'static str = "index"; - } - - /// Indicates whether a custom code should be generated. - /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. - pub type AttributePropertyIndex = AttributePropertyOptionalSingletone< AttributePropertyIndexMarker >; +/// Indicates whether a index code should be generated. +/// Defaults to `false`, meaning no index code is generated unless explicitly requested. +pub type AttributePropertyIndex = AttributePropertyOptionalSingletone< AttributePropertyIndexMarker >; - // == test code +// == diff --git a/module/core/derive_tools_meta/src/derive/index/item_attributes.rs b/module/core/derive_tools_meta/src/derive/index/item_attributes.rs index 600de27532..1444a37744 100644 --- a/module/core/derive_tools_meta/src/derive/index/item_attributes.rs +++ b/module/core/derive_tools_meta/src/derive/index/item_attributes.rs @@ -1,122 +1,79 @@ +use macro_tools:: +{ + ct, + syn_err, + syn, + qt, + Result, + AttributePropertyComponent, + AttributePropertyOptionalSingletone, +}; + +/// Represents the attributes of a struct. Aggregates all its attributes. +#[ derive( Debug, Default ) ] +pub struct ItemAttributes +{ + pub debug : AttributePropertyDebug, +} - use macro_tools:: - { - ct, - syn_err, - syn, - qt, - Result, - AttributePropertyComponent, - AttributePropertyOptionalSingletone, - Assign, - }; - - /// Represents the attributes of a struct. Aggregates all its attributes. - #[ derive( Debug, Default ) ] - pub struct ItemAttributes +impl ItemAttributes +{ + /// Constructs a `ItemAttributes` instance from an iterator of attributes. + /// + /// This function parses the provided attributes and assigns them to the + /// appropriate fields in the `ItemAttributes` struct. + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > { - /// Attribute for customizing the mutation process. - pub index : AttributePropertyIndex, - - pub debug : AttributePropertyDebug, - - } + let result = Self::default(); - impl ItemAttributes - { - /// Constructs a `ItemAttributes` instance from an iterator of attributes. - /// - /// This function parses the provided attributes and assigns them to the - /// appropriate fields in the `ItemAttributes` struct. - pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > + // Closure to generate an error message for unknown attributes. + let error = | attr : & syn::Attribute | -> syn::Error { - let mut result = Self::default(); - - // Closure to generate an error message for unknown attributes. - let error = | attr : & syn::Attribute | -> syn::Error - { - let known_attributes = ct::str::format! - ( - "Known attributes are: {}, {}.", - "debug", - AttributePropertyIndex::KEYWORD, - ); - syn_err! - ( - attr, - "Expects an attribute of format '#[ attribute ]'\n {known_attributes}\n But got: '{}'", - qt! { #attr } - ) - }; - - for attr in attrs + let known_attributes = ct::concatcp! + ( + "Known attributes are: ", + "debug", + "." + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute ]'\n {known_attributes}\n But got: '{}'", + qt! { #attr } + ) + }; + + for attr in attrs + { + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; + let key_str = format!( "{}", key_ident ); + match key_str.as_ref() { - let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; - let key_str = format!( "{}", key_ident ); - // if attr::is_standard( & key_str ) - // { - // continue; - // } - match key_str.as_ref() - { - AttributePropertyIndex::KEYWORD => result.assign( AttributePropertyIndex::from(true) ), - "debug" => {}, - _ => {}, - // _ => return Err( error( attr ) ), - } + "debug" => {}, + _ => {}, + // _ => return Err( error( attr ) ), } - - Ok( result ) } - } - - impl< IntoT > Assign< AttributePropertyIndex, IntoT > for ItemAttributes -where - IntoT : Into< AttributePropertyIndex >, -{ - #[ inline( always ) ] - fn assign( &mut self, component : IntoT ) - { - self.index.assign( component.into() ); + Ok( result ) } } +// == Attribute properties - - - // == Attribute properties - - /// Marker type for attribute property to specify whether to provide a sketch as a hint. - /// Defaults to `false`, which means no hint is provided unless explicitly requested. - #[ derive( Debug, Default, Clone, Copy ) ] - pub struct AttributePropertyDebugMarker; - - impl AttributePropertyComponent for AttributePropertyDebugMarker - { - const KEYWORD : & 'static str = "debug"; - } - - /// Specifies whether to provide a sketch as a hint. - /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub type AttributePropertyDebug = AttributePropertyOptionalSingletone< AttributePropertyDebugMarker >; +/// Marker type for attribute property to specify whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyDebugMarker; - // == - - /// Marker type for attribute property to indicate whether a custom code should be generated. - /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. - #[ derive( Debug, Default, Clone, Copy ) ] - pub struct AttributePropertyIndexMarker; - - impl AttributePropertyComponent for AttributePropertyIndexMarker - { - const KEYWORD : & 'static str = "index"; - } - - /// Indicates whether a custom code should be generated. - /// Defaults to `false`, meaning no custom code is generated unless explicitly requested. - pub type AttributePropertyIndex = AttributePropertyOptionalSingletone< AttributePropertyIndexMarker >; +impl AttributePropertyComponent for AttributePropertyDebugMarker +{ + const KEYWORD : & 'static str = "debug"; +} - // == test code +/// Specifies whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +pub type AttributePropertyDebug = AttributePropertyOptionalSingletone< AttributePropertyDebugMarker >; +// == From 20968b28b820371526916c15a66cc49a37efc97a Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Fri, 5 Jul 2024 14:07:31 +0300 Subject: [PATCH 08/12] ref: comments fix --- .../src/derive/index/field_attributes.rs | 2 -- module/core/derive_tools_meta/src/lib.rs | 33 +++++++++---------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/module/core/derive_tools_meta/src/derive/index/field_attributes.rs b/module/core/derive_tools_meta/src/derive/index/field_attributes.rs index 65eeb1ab50..c30996dfd1 100644 --- a/module/core/derive_tools_meta/src/derive/index/field_attributes.rs +++ b/module/core/derive_tools_meta/src/derive/index/field_attributes.rs @@ -38,7 +38,6 @@ impl FieldAttributes let known_attributes = ct::concatcp! ( "Known attributes are : ", - "debug", ", ", AttributePropertyIndex::KEYWORD, ".", ); @@ -58,7 +57,6 @@ impl FieldAttributes match key_str.as_ref() { AttributePropertyIndex::KEYWORD => result.assign( AttributePropertyIndex::from( true ) ), - "debug" => {}, _ => {}, // _ => return Err( error( attr ) ), } diff --git a/module/core/derive_tools_meta/src/lib.rs b/module/core/derive_tools_meta/src/lib.rs index c914047506..ad7e5aeef3 100644 --- a/module/core/derive_tools_meta/src/lib.rs +++ b/module/core/derive_tools_meta/src/lib.rs @@ -574,42 +574,39 @@ pub fn phantom( _attr: proc_macro::TokenStream, input : proc_macro::TokenStream /// /// Provides an automatic [Index](core::ops::Index) trait implementation when-ever it's possible. /// -/// This macro simplifies the indexing syntzx of struct type. +/// This macro simplifies the indexing syntax of struct type. /// /// ## Example Usage // /// Instead of manually implementing `Index< T >` for `IsTransparent`: /// /// ```rust +/// use core::ops::Index; /// pub struct IsTransparent< T > /// { -/// a : T, +/// a : Vec< T >, /// } /// /// impl< T > Index< usize > for IsTransparent< T > /// { -/// type Output = T; -/// -/// #[ inline( always ) ] -/// fn index( &self, index: usize ) -> &Self::Output -/// { -/// match index -/// { -/// 0 => &self.a, -/// _ => panic!( "Index out of bounds" ), -/// } -/// } +/// type Output = T; +/// +/// #[ inline( always ) ] +/// fn index( &self, index: usize ) -> &Self::Output +/// { +/// &self.a[ index ] +/// } /// } /// ``` /// -/// Use `#[ derive( Index ) ]` to automatically generate the implementation: +/// Use `#[ index ]` to automatically generate the implementation: /// /// ```rust /// # use derive_tools_meta::*; -/// #[ derive( Index ) ] -/// pub struct IsTransparent< T > { -/// a : T -///}; +/// pub struct IsTransparent< T > +/// { +/// a : Vec< T > +/// }; /// ``` /// From 96c4b6c334870b82e7e32122ad6b1cd274c3e4fc Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Fri, 5 Jul 2024 18:16:24 +0300 Subject: [PATCH 09/12] fut: struct implementation --- .../index/only_test/struct_multiple_tuple.rs | 2 +- .../tests/inc/index/struct_multiple_named.rs | 14 ++ .../tests/inc/index/struct_multiple_tuple.rs | 15 ++ .../inc/index/struct_multiple_tuple_manual.rs | 4 +- .../tests/inc/index/struct_named.rs | 3 +- .../tests/inc/index/struct_tuple.rs | 6 +- module/core/derive_tools/tests/inc/mod.rs | 28 +-- .../derive_tools_meta/src/derive/index.rs | 183 +++++++++++++++++- 8 files changed, 231 insertions(+), 24 deletions(-) create mode 100644 module/core/derive_tools/tests/inc/index/struct_multiple_named.rs create mode 100644 module/core/derive_tools/tests/inc/index/struct_multiple_tuple.rs diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs index dcb8a03b99..07fc355544 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs @@ -1,7 +1,7 @@ #[ test ] fn index() { - let x = StructTupleMultiple( false, vec![ 2, 44, 81 ] ); + let x = StructMultipleTuple( false, vec![ 2, 44, 81 ] ); let exp = ( 2, 44 ); let got = ( x[ 0 ], x[ 1 ] ); assert_eq!( got, exp ); diff --git a/module/core/derive_tools/tests/inc/index/struct_multiple_named.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_named.rs new file mode 100644 index 0000000000..a99e72a7b5 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_named.rs @@ -0,0 +1,14 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( the_module::Index ) ] +struct StructMultipleNamed< T > +{ + a : Vec< T >, + #[ index ] + b : Vec< T >, +} + +include!( "./only_test/struct_multiple_named.rs" ); + diff --git a/module/core/derive_tools/tests/inc/index/struct_multiple_tuple.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple.rs new file mode 100644 index 0000000000..c5a5629176 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple.rs @@ -0,0 +1,15 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( the_module::Index ) ] +struct StructMultipleTuple< T > +( + bool, + #[ index ] + Vec< T >, +); + +include!( "./only_test/struct_multiple_tuple.rs" ); + + diff --git a/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs index 2b2dfafbc3..8c7358bb90 100644 --- a/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs @@ -1,9 +1,9 @@ use core::ops::Index; #[ allow( dead_code) ] -struct StructTupleMultiple< T >( bool, Vec< T > ); +struct StructMultipleTuple< T >( bool, Vec< T > ); -impl< T > Index< usize > for StructTupleMultiple< T > +impl< T > Index< usize > for StructMultipleTuple< T > { type Output = T; diff --git a/module/core/derive_tools/tests/inc/index/struct_named.rs b/module/core/derive_tools/tests/inc/index/struct_named.rs index f2f28e0e96..ca5b884595 100644 --- a/module/core/derive_tools/tests/inc/index/struct_named.rs +++ b/module/core/derive_tools/tests/inc/index/struct_named.rs @@ -3,10 +3,9 @@ use super::*; #[ derive( the_module::Index ) ] -#[debug] struct StructNamed< T > { - #[index] + #[ index ] a : Vec< T >, } diff --git a/module/core/derive_tools/tests/inc/index/struct_tuple.rs b/module/core/derive_tools/tests/inc/index/struct_tuple.rs index e6740b7a54..97728a8753 100644 --- a/module/core/derive_tools/tests/inc/index/struct_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/struct_tuple.rs @@ -2,6 +2,10 @@ use super::*; #[ allow( dead_code ) ] #[ derive( the_module::Index ) ] -struct StructTuple< T >( Vec< T >, u8, u8 ); +struct StructTuple< T > +( + #[ index ] + Vec< T > +); include!( "./only_test/struct_tuple.rs" ); diff --git a/module/core/derive_tools/tests/inc/mod.rs b/module/core/derive_tools/tests/inc/mod.rs index ec8fa71fde..062616ab33 100644 --- a/module/core/derive_tools/tests/inc/mod.rs +++ b/module/core/derive_tools/tests/inc/mod.rs @@ -302,19 +302,21 @@ mod phantom_tests #[ path = "index" ] mod index_tests { - #[ allow( unused_imports ) ] - use super::*; + #[ allow( unused_imports ) ] + use super::*; - // Must be implemented: - // - Try-build test for Enum - // - Try-build test for Unit - // - Try-build test for Named Struct without fields + // Must be implemented: + // - Try-build test for Enum + // - Try-build test for Unit + // - Try-build test for Named Struct without fields - // mod struct_named; - mod struct_named_manual; - mod struct_multiple_named_manual; - // mod struct_tuple; - mod struct_tuple_manual; - mod struct_multiple_tuple_manual; - + mod struct_named; + mod struct_multiple_named; + mod struct_named_manual; + mod struct_multiple_named_manual; + mod struct_tuple; + mod struct_multiple_tuple; + mod struct_tuple_manual; + mod struct_multiple_tuple_manual; + } diff --git a/module/core/derive_tools_meta/src/derive/index.rs b/module/core/derive_tools_meta/src/derive/index.rs index e4bf46dbfa..e00b2753aa 100644 --- a/module/core/derive_tools_meta/src/derive/index.rs +++ b/module/core/derive_tools_meta/src/derive/index.rs @@ -3,6 +3,7 @@ use macro_tools:: { attr, diag, + generic_params, struct_like::StructLike, Result }; @@ -13,13 +14,20 @@ pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr let has_debug = attr::has_debug( parsed.attrs().iter() )?; let item_name = &parsed.ident(); + let ( _generics_with_defaults, generics_impl, generics_ty, generics_where ) + = generic_params::decompose( &parsed.generics() ); + let result = match parsed { - StructLike::Struct( ref item ) => Err( - syn::Error::new( - item.fields.span(), - "Not implemented yet", - ) + StructLike::Struct( ref item ) => + generate_struct + ( + item_name, + &generics_impl, + &generics_ty, + &generics_where, + &item.fields, + ), StructLike::Enum(_) => unimplemented!( "Index not implemented for Enum" ), @@ -36,3 +44,168 @@ pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr Ok( result ) } + + +/// An aggregator function to generate `Index` implementation for tuple and named structs +fn generate_struct +( + item_name: &syn::Ident, + generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + fields: &syn::Fields, +) +-> Result< proc_macro2::TokenStream > +{ + + match fields + { + syn::Fields::Named( fields ) => + generate_struct_named_fields + ( + item_name, + generics_impl, + generics_ty, + generics_where, + fields + ), + + syn::Fields::Unnamed( fields ) => + generate_struct_tuple_fields + ( + item_name, + generics_impl, + generics_ty, + generics_where, + fields + ), + + syn::Fields::Unit => + unimplemented!( "Index not implemented for Unit" ), + } +} + + +fn generate_struct_named_fields +( + item_name: &syn::Ident, + generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + fields: &syn::FieldsNamed, +) +-> Result< proc_macro2::TokenStream > +{ + let fields = fields.named.clone(); + let non_empty_attrs: Vec<&syn::Field> = fields.iter().filter(|field| + !field.attrs.is_empty() + ).collect(); + + if non_empty_attrs.len() != 1 + { + return Err( + syn::Error::new_spanned + ( + &fields, + "Only one field can include #[index] derive macro" + ) + ); + } + + let generated = fields.iter().map(|field| { + let field_name = &field.ident; + + if !field.attrs.is_empty() + { + qt! + { + &self.#field_name[index] + } + } + else + { + qt!{ } + } + }); + + Ok + ( + qt! + { + #[ automatically_derived ] + impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > + where + #generics_where + { + type Output = T; + #[ inline( always ) ] + fn index( &self, index : usize ) -> &Self::Output + { + #(#generated)* + } + } + } + ) +} + +fn generate_struct_tuple_fields +( + item_name: &syn::Ident, + generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + fields: &syn::FieldsUnnamed, +) +-> Result< proc_macro2::TokenStream > +{ + let fields = fields.unnamed.clone(); + let non_empty_attrs: Vec<&syn::Field> = fields.iter().filter(|field| + !field.attrs.is_empty() + ).collect(); + + if non_empty_attrs.len() != 1 + { + return Err( + syn::Error::new_spanned + ( + &fields, + "Only one field can include #[index] derive macro" + ) + ); + } + + let generated = fields.iter().enumerate().map(|( i, field )| + { + let i = syn::Index::from( i ); + if !field.attrs.is_empty() { + qt! + { + &self.#i[index] + } + } + else + { + qt!{ } + } + }); + + Ok + ( + qt! + { + #[ automatically_derived ] + impl< #generics_impl > ::core::ops::Index< usize > for #item_name< #generics_ty > + where + #generics_where + { + type Output = T; + #[ inline( always ) ] + fn index( &self, index : usize ) -> &Self::Output + { + #(#generated)* + } + } + } + ) +} + From d8c4db50adb97337c8da93311c21246f99602790 Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Mon, 8 Jul 2024 08:39:00 +0300 Subject: [PATCH 10/12] fut: add try_build tests, ref: delete not used attrs --- .../tests/inc/index/compiletime/enum.rs | 15 +++ .../tests/inc/index/compiletime/enum.stderr | 7 ++ .../tests/inc/index/compiletime/struct.rs | 14 +++ .../tests/inc/index/compiletime/struct.stderr | 8 ++ .../index/compiletime/struct_named_empty.rs | 14 +++ .../compiletime/struct_named_empty.stderr | 7 ++ .../inc/index/compiletime/struct_unit.rs | 12 +++ .../inc/index/compiletime/struct_unit.stderr | 7 ++ module/core/derive_tools/tests/inc/mod.rs | 22 +++-- .../src/derive/index/field_attributes.rs | 98 ------------------- .../src/derive/index/item_attributes.rs | 79 --------------- 11 files changed, 100 insertions(+), 183 deletions(-) create mode 100644 module/core/derive_tools/tests/inc/index/compiletime/enum.rs create mode 100644 module/core/derive_tools/tests/inc/index/compiletime/enum.stderr create mode 100644 module/core/derive_tools/tests/inc/index/compiletime/struct.rs create mode 100644 module/core/derive_tools/tests/inc/index/compiletime/struct.stderr create mode 100644 module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.rs create mode 100644 module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.stderr create mode 100644 module/core/derive_tools/tests/inc/index/compiletime/struct_unit.rs create mode 100644 module/core/derive_tools/tests/inc/index/compiletime/struct_unit.stderr delete mode 100644 module/core/derive_tools_meta/src/derive/index/field_attributes.rs delete mode 100644 module/core/derive_tools_meta/src/derive/index/item_attributes.rs diff --git a/module/core/derive_tools/tests/inc/index/compiletime/enum.rs b/module/core/derive_tools/tests/inc/index/compiletime/enum.rs new file mode 100644 index 0000000000..64042f87e3 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/enum.rs @@ -0,0 +1,15 @@ +use derive_tools::Index; + +#[ derive( Index ) ] +enum Enum< T > +{ + Nothing, + #[ index ] + IndexVector( Vec< T > ) +} + +fn main() +{ +} + + diff --git a/module/core/derive_tools/tests/inc/index/compiletime/enum.stderr b/module/core/derive_tools/tests/inc/index/compiletime/enum.stderr new file mode 100644 index 0000000000..fd33fec0c1 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/enum.stderr @@ -0,0 +1,7 @@ +error: proc-macro derive panicked + --> tests/inc/index/compiletime/enum.rs:3:12 + | +3 | #[ derive( Index ) ] + | ^^^^^ + | + = help: message: not implemented: Index not implemented for Enum diff --git a/module/core/derive_tools/tests/inc/index/compiletime/struct.rs b/module/core/derive_tools/tests/inc/index/compiletime/struct.rs new file mode 100644 index 0000000000..1b9d3785a8 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct.rs @@ -0,0 +1,14 @@ +use derive_tools::Index; + +#[ derive( Index ) ] +struct StructMultipleNamed< T > +{ + #[ index ] + a: Vec< T >, + #[ index ] + b : Vec< T >, +} + +fn main() +{ +} diff --git a/module/core/derive_tools/tests/inc/index/compiletime/struct.stderr b/module/core/derive_tools/tests/inc/index/compiletime/struct.stderr new file mode 100644 index 0000000000..09740e8e3e --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct.stderr @@ -0,0 +1,8 @@ +error: Only one field can include #[index] derive macro + --> tests/inc/index/compiletime/struct.rs:6:3 + | +6 | / #[index] +7 | | a: Vec< T >, +8 | | #[index] +9 | | b : Vec< T >, + | |_______________^ diff --git a/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.rs b/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.rs new file mode 100644 index 0000000000..ce4a88d40e --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.rs @@ -0,0 +1,14 @@ +use derive_tools::Index; + +#[ derive( Index ) ] +struct EmptyStruct +{ +} + +fn main() +{ +} + + + + diff --git a/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.stderr b/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.stderr new file mode 100644 index 0000000000..fc7bc03403 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.stderr @@ -0,0 +1,7 @@ +error: proc-macro derive panicked + --> tests/inc/index/compiletime/struct_named_empty.rs:3:12 + | +3 | #[ derive( Index ) ] + | ^^^^^ + | + = help: message: not implemented: Index not implemented for Unit diff --git a/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.rs b/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.rs new file mode 100644 index 0000000000..6ff9a3c2ba --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.rs @@ -0,0 +1,12 @@ +use derive_tools::Index; + +#[ derive( Index ) ] +struct StructUnit; + +fn main() +{ + +} + + + diff --git a/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.stderr b/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.stderr new file mode 100644 index 0000000000..644279835a --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.stderr @@ -0,0 +1,7 @@ +error: proc-macro derive panicked + --> tests/inc/index/compiletime/struct_unit.rs:3:12 + | +3 | #[ derive( Index ) ] + | ^^^^^ + | + = help: message: not implemented: Index not implemented for Unit diff --git a/module/core/derive_tools/tests/inc/mod.rs b/module/core/derive_tools/tests/inc/mod.rs index 062616ab33..feff00a160 100644 --- a/module/core/derive_tools/tests/inc/mod.rs +++ b/module/core/derive_tools/tests/inc/mod.rs @@ -249,7 +249,6 @@ mod inner_from_tests } - #[ cfg( feature = "derive_phantom" ) ] #[ path = "phantom" ] mod phantom_tests @@ -304,11 +303,6 @@ mod index_tests { #[ allow( unused_imports ) ] use super::*; - - // Must be implemented: - // - Try-build test for Enum - // - Try-build test for Unit - // - Try-build test for Named Struct without fields mod struct_named; mod struct_multiple_named; @@ -319,4 +313,20 @@ mod index_tests mod struct_tuple_manual; mod struct_multiple_tuple_manual; + only_for_terminal_module! + { + #[ test_tools::nightly ] + #[ test ] + fn index_trybuild() + { + + println!( "current_dir : {:?}", std::env::current_dir().unwrap() ); + let t = test_tools::compiletime::TestCases::new(); + + t.compile_fail( "tests/inc/index/compiletime/struct.rs" ); + t.compile_fail( "tests/inc/index/compiletime/struct_unit.rs" ); + t.compile_fail( "tests/inc/index/compiletime/struct_named_empty.rs" ); + t.compile_fail( "tests/inc/index/compiletime/enum.rs" ); + } + } } diff --git a/module/core/derive_tools_meta/src/derive/index/field_attributes.rs b/module/core/derive_tools_meta/src/derive/index/field_attributes.rs deleted file mode 100644 index c30996dfd1..0000000000 --- a/module/core/derive_tools_meta/src/derive/index/field_attributes.rs +++ /dev/null @@ -1,98 +0,0 @@ -use macro_tools:: -{ - ct, - syn_err, - syn, - qt, - Result, - AttributePropertyComponent, - AttributePropertyOptionalSingletone, - Assign, -}; - -/// -/// Attributes of a field / variant -/// - -/// Represents the attributes of a struct. Aggregates all its attributes. -#[ derive( Debug, Default ) ] -pub struct FieldAttributes -{ - /// Specifies whether we should generate Index implementation for the field. - pub index : AttributePropertyIndex, -} - -impl FieldAttributes -{ - /// Constructs a `ItemAttributes` instance from an iterator of attributes. - /// - /// This function parses the provided attributes and assigns them to the - /// appropriate fields in the `ItemAttributes` struct. - pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > - { - let mut result = Self::default(); - - // Closure to generate an error message for unknown attributes. - let error = | attr : & syn::Attribute | -> syn::Error - { - let known_attributes = ct::concatcp! - ( - "Known attributes are : ", - ", ", AttributePropertyIndex::KEYWORD, - ".", - ); - syn_err! - ( - attr, - "Expects an attribute of format '#[ attribute ]'\n {known_attributes}\n But got: '{}'", - qt! { #attr } - ) - }; - - for attr in attrs - { - let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; - let key_str = format!( "{}", key_ident ); - - match key_str.as_ref() - { - AttributePropertyIndex::KEYWORD => result.assign( AttributePropertyIndex::from( true ) ), - _ => {}, - // _ => return Err( error( attr ) ), - } - } - - Ok( result ) - } -} - -impl< IntoT > Assign< AttributePropertyIndex, IntoT > for FieldAttributes -where - IntoT : Into< AttributePropertyIndex >, -{ - #[ inline( always ) ] - fn assign( &mut self, component : IntoT ) - { - self.index.assign( component.into() ); - } -} - - -// == Attribute properties - -/// Marker type for attribute property to indicate whether a index code should be generated. -/// Defaults to `false`, meaning no index code is generated unless explicitly requested. -#[ derive( Debug, Default, Clone, Copy ) ] -pub struct AttributePropertyIndexMarker; - -impl AttributePropertyComponent for AttributePropertyIndexMarker -{ - const KEYWORD : & 'static str = "index"; -} - -/// Indicates whether a index code should be generated. -/// Defaults to `false`, meaning no index code is generated unless explicitly requested. -pub type AttributePropertyIndex = AttributePropertyOptionalSingletone< AttributePropertyIndexMarker >; - -// == - diff --git a/module/core/derive_tools_meta/src/derive/index/item_attributes.rs b/module/core/derive_tools_meta/src/derive/index/item_attributes.rs deleted file mode 100644 index 1444a37744..0000000000 --- a/module/core/derive_tools_meta/src/derive/index/item_attributes.rs +++ /dev/null @@ -1,79 +0,0 @@ -use macro_tools:: -{ - ct, - syn_err, - syn, - qt, - Result, - AttributePropertyComponent, - AttributePropertyOptionalSingletone, -}; - -/// Represents the attributes of a struct. Aggregates all its attributes. -#[ derive( Debug, Default ) ] -pub struct ItemAttributes -{ - pub debug : AttributePropertyDebug, -} - -impl ItemAttributes -{ - /// Constructs a `ItemAttributes` instance from an iterator of attributes. - /// - /// This function parses the provided attributes and assigns them to the - /// appropriate fields in the `ItemAttributes` struct. - pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > - { - let result = Self::default(); - - // Closure to generate an error message for unknown attributes. - let error = | attr : & syn::Attribute | -> syn::Error - { - let known_attributes = ct::concatcp! - ( - "Known attributes are: ", - "debug", - "." - ); - syn_err! - ( - attr, - "Expects an attribute of format '#[ attribute ]'\n {known_attributes}\n But got: '{}'", - qt! { #attr } - ) - }; - - for attr in attrs - { - let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; - let key_str = format!( "{}", key_ident ); - match key_str.as_ref() - { - "debug" => {}, - _ => {}, - // _ => return Err( error( attr ) ), - } - } - - Ok( result ) - } -} - -// == Attribute properties - -/// Marker type for attribute property to specify whether to provide a sketch as a hint. -/// Defaults to `false`, which means no hint is provided unless explicitly requested. -#[ derive( Debug, Default, Clone, Copy ) ] -pub struct AttributePropertyDebugMarker; - -impl AttributePropertyComponent for AttributePropertyDebugMarker -{ - const KEYWORD : & 'static str = "debug"; -} - -/// Specifies whether to provide a sketch as a hint. -/// Defaults to `false`, which means no hint is provided unless explicitly requested. -pub type AttributePropertyDebug = AttributePropertyOptionalSingletone< AttributePropertyDebugMarker >; - -// == - From b9fd648eb4b772733ef3cae47ce269997b8b32b2 Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Mon, 8 Jul 2024 08:51:29 +0300 Subject: [PATCH 11/12] ref: formatting --- .../tests/inc/index/compiletime/enum.rs | 2 -- .../tests/inc/index/compiletime/struct.stderr | 4 ++-- .../index/compiletime/struct_named_empty.rs | 4 ---- .../inc/index/compiletime/struct_unit.rs | 3 --- .../index/only_test/struct_multiple_named.rs | 1 - .../index/only_test/struct_multiple_tuple.rs | 1 - .../tests/inc/index/struct_multiple_named.rs | 1 - .../inc/index/struct_multiple_named_manual.rs | 1 - .../tests/inc/index/struct_multiple_tuple.rs | 2 -- .../inc/index/struct_multiple_tuple_manual.rs | 1 - .../derive_tools_meta/src/derive/index.rs | 21 +++++++++---------- module/core/derive_tools_meta/src/lib.rs | 2 +- 12 files changed, 13 insertions(+), 30 deletions(-) diff --git a/module/core/derive_tools/tests/inc/index/compiletime/enum.rs b/module/core/derive_tools/tests/inc/index/compiletime/enum.rs index 64042f87e3..543ebde1f0 100644 --- a/module/core/derive_tools/tests/inc/index/compiletime/enum.rs +++ b/module/core/derive_tools/tests/inc/index/compiletime/enum.rs @@ -11,5 +11,3 @@ enum Enum< T > fn main() { } - - diff --git a/module/core/derive_tools/tests/inc/index/compiletime/struct.stderr b/module/core/derive_tools/tests/inc/index/compiletime/struct.stderr index 09740e8e3e..e150d4f21d 100644 --- a/module/core/derive_tools/tests/inc/index/compiletime/struct.stderr +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct.stderr @@ -1,8 +1,8 @@ error: Only one field can include #[index] derive macro --> tests/inc/index/compiletime/struct.rs:6:3 | -6 | / #[index] +6 | / #[ index ] 7 | | a: Vec< T >, -8 | | #[index] +8 | | #[ index ] 9 | | b : Vec< T >, | |_______________^ diff --git a/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.rs b/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.rs index ce4a88d40e..ec15e88da3 100644 --- a/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.rs +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.rs @@ -8,7 +8,3 @@ struct EmptyStruct fn main() { } - - - - diff --git a/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.rs b/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.rs index 6ff9a3c2ba..84375aef65 100644 --- a/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.rs +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.rs @@ -7,6 +7,3 @@ fn main() { } - - - diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_named.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_named.rs index f03ad5af7d..6153f8751e 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_named.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_named.rs @@ -12,4 +12,3 @@ fn index() assert_eq!( got, exp ); } - diff --git a/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs index 07fc355544..18dc0a71e1 100644 --- a/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs @@ -6,4 +6,3 @@ fn index() let got = ( x[ 0 ], x[ 1 ] ); assert_eq!( got, exp ); } - diff --git a/module/core/derive_tools/tests/inc/index/struct_multiple_named.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_named.rs index a99e72a7b5..3dea73695e 100644 --- a/module/core/derive_tools/tests/inc/index/struct_multiple_named.rs +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_named.rs @@ -11,4 +11,3 @@ struct StructMultipleNamed< T > } include!( "./only_test/struct_multiple_named.rs" ); - diff --git a/module/core/derive_tools/tests/inc/index/struct_multiple_named_manual.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_named_manual.rs index ce46c871fd..6e5bc2cba2 100644 --- a/module/core/derive_tools/tests/inc/index/struct_multiple_named_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_named_manual.rs @@ -18,4 +18,3 @@ impl< T > Index< usize > for StructMultipleNamed< T > } include!( "./only_test/struct_multiple_named.rs" ); - diff --git a/module/core/derive_tools/tests/inc/index/struct_multiple_tuple.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple.rs index c5a5629176..1228949d1f 100644 --- a/module/core/derive_tools/tests/inc/index/struct_multiple_tuple.rs +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple.rs @@ -11,5 +11,3 @@ struct StructMultipleTuple< T > ); include!( "./only_test/struct_multiple_tuple.rs" ); - - diff --git a/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs index 8c7358bb90..12a58b2ae6 100644 --- a/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs @@ -14,4 +14,3 @@ impl< T > Index< usize > for StructMultipleTuple< T > } include!( "./only_test/struct_multiple_tuple.rs" ); - diff --git a/module/core/derive_tools_meta/src/derive/index.rs b/module/core/derive_tools_meta/src/derive/index.rs index e00b2753aa..1bccca95f0 100644 --- a/module/core/derive_tools_meta/src/derive/index.rs +++ b/module/core/derive_tools_meta/src/derive/index.rs @@ -29,9 +29,9 @@ pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr &item.fields, ), - StructLike::Enum(_) => + StructLike::Enum( _ ) => unimplemented!( "Index not implemented for Enum" ), - StructLike::Unit(_) => + StructLike::Unit( _ ) => unimplemented!( "Index not implemented for Unit" ), }?; @@ -44,8 +44,6 @@ pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr Ok( result ) } - - /// An aggregator function to generate `Index` implementation for tuple and named structs fn generate_struct ( @@ -97,7 +95,7 @@ fn generate_struct_named_fields -> Result< proc_macro2::TokenStream > { let fields = fields.named.clone(); - let non_empty_attrs: Vec<&syn::Field> = fields.iter().filter(|field| + let non_empty_attrs : Vec<&syn::Field> = fields.iter().filter(| field | !field.attrs.is_empty() ).collect(); @@ -112,14 +110,15 @@ fn generate_struct_named_fields ); } - let generated = fields.iter().map(|field| { + let generated = fields.iter().map(| field | + { let field_name = &field.ident; if !field.attrs.is_empty() { qt! { - &self.#field_name[index] + &self.#field_name[ index ] } } else @@ -141,7 +140,7 @@ fn generate_struct_named_fields #[ inline( always ) ] fn index( &self, index : usize ) -> &Self::Output { - #(#generated)* + #( #generated )* } } } @@ -159,7 +158,7 @@ fn generate_struct_tuple_fields -> Result< proc_macro2::TokenStream > { let fields = fields.unnamed.clone(); - let non_empty_attrs: Vec<&syn::Field> = fields.iter().filter(|field| + let non_empty_attrs : Vec<&syn::Field> = fields.iter().filter(|field| !field.attrs.is_empty() ).collect(); @@ -180,7 +179,7 @@ fn generate_struct_tuple_fields if !field.attrs.is_empty() { qt! { - &self.#i[index] + &self.#i[ index ] } } else @@ -202,7 +201,7 @@ fn generate_struct_tuple_fields #[ inline( always ) ] fn index( &self, index : usize ) -> &Self::Output { - #(#generated)* + #( #generated )* } } } diff --git a/module/core/derive_tools_meta/src/lib.rs b/module/core/derive_tools_meta/src/lib.rs index ad7e5aeef3..972b62d689 100644 --- a/module/core/derive_tools_meta/src/lib.rs +++ b/module/core/derive_tools_meta/src/lib.rs @@ -592,7 +592,7 @@ pub fn phantom( _attr: proc_macro::TokenStream, input : proc_macro::TokenStream /// type Output = T; /// /// #[ inline( always ) ] -/// fn index( &self, index: usize ) -> &Self::Output +/// fn index( &self, index : usize ) -> &Self::Output /// { /// &self.a[ index ] /// } From 4e28e7145294c473d87d0e11f1765985555f3e52 Mon Sep 17 00:00:00 2001 From: Inkvisto Date: Mon, 8 Jul 2024 15:25:52 +0300 Subject: [PATCH 12/12] fix: formatting and added attributes --- .../derive_tools_meta/src/derive/index.rs | 37 +++---- .../src/derive/index/field_attributes.rs | 99 +++++++++++++++++++ .../src/derive/index/item_attributes.rs | 80 +++++++++++++++ module/core/derive_tools_meta/src/lib.rs | 7 +- 4 files changed, 202 insertions(+), 21 deletions(-) create mode 100644 module/core/derive_tools_meta/src/derive/index/field_attributes.rs create mode 100644 module/core/derive_tools_meta/src/derive/index/item_attributes.rs diff --git a/module/core/derive_tools_meta/src/derive/index.rs b/module/core/derive_tools_meta/src/derive/index.rs index 1bccca95f0..19a68ef63b 100644 --- a/module/core/derive_tools_meta/src/derive/index.rs +++ b/module/core/derive_tools_meta/src/derive/index.rs @@ -8,7 +8,8 @@ use macro_tools:: Result }; -pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > { +pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStream > +{ let original_input = input.clone(); let parsed = syn::parse::< StructLike >( input )?; let has_debug = attr::has_debug( parsed.attrs().iter() )?; @@ -47,11 +48,11 @@ pub fn index( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr /// An aggregator function to generate `Index` implementation for tuple and named structs fn generate_struct ( - item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - fields: &syn::Fields, + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where : &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + fields : &syn::Fields, ) -> Result< proc_macro2::TokenStream > { @@ -86,16 +87,16 @@ fn generate_struct fn generate_struct_named_fields ( - item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - fields: &syn::FieldsNamed, + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where : &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + fields : &syn::FieldsNamed, ) -> Result< proc_macro2::TokenStream > { let fields = fields.named.clone(); - let non_empty_attrs : Vec<&syn::Field> = fields.iter().filter(| field | + let non_empty_attrs : Vec< &syn::Field > = fields.iter().filter(| field | !field.attrs.is_empty() ).collect(); @@ -149,16 +150,16 @@ fn generate_struct_named_fields fn generate_struct_tuple_fields ( - item_name: &syn::Ident, - generics_impl: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_ty: &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, - generics_where: &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, - fields: &syn::FieldsUnnamed, + item_name : &syn::Ident, + generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + generics_where : &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + fields : &syn::FieldsUnnamed, ) -> Result< proc_macro2::TokenStream > { let fields = fields.unnamed.clone(); - let non_empty_attrs : Vec<&syn::Field> = fields.iter().filter(|field| + let non_empty_attrs : Vec< &syn::Field > = fields.iter().filter(| field | !field.attrs.is_empty() ).collect(); diff --git a/module/core/derive_tools_meta/src/derive/index/field_attributes.rs b/module/core/derive_tools_meta/src/derive/index/field_attributes.rs new file mode 100644 index 0000000000..f21e170305 --- /dev/null +++ b/module/core/derive_tools_meta/src/derive/index/field_attributes.rs @@ -0,0 +1,99 @@ +use macro_tools:: +{ + ct, + syn_err, + syn, + qt, + Result, + AttributePropertyComponent, + AttributePropertyOptionalSingletone, + Assign, +}; + +/// +/// Attributes of a field / variant +/// + +/// Represents the attributes of a struct. Aggregates all its attributes. +#[ derive( Debug, Default ) ] +pub struct FieldAttributes +{ + /// Specifies whether we should generate Index implementation for the field. + pub index : AttributePropertyIndex, +} + +impl FieldAttributes +{ + /// Constructs a `ItemAttributes` instance from an iterator of attributes. + /// + /// This function parses the provided attributes and assigns them to the + /// appropriate fields in the `ItemAttributes` struct. + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > + { + let mut result = Self::default(); + + // Closure to generate an error message for unknown attributes. + let error = | attr : & syn::Attribute | -> syn::Error + { + let known_attributes = ct::concatcp! + ( + "Known attributes are : ", + ", ", AttributePropertyIndex::KEYWORD, + ".", + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute ]'\n {known_attributes}\n But got: '{}'", + qt! { #attr } + ) + }; + + for attr in attrs + { + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; + let key_str = format!( "{}", key_ident ); + + match key_str.as_ref() + { + AttributePropertyIndex::KEYWORD => result.assign( AttributePropertyIndex::from( true ) ), + _ => {}, + // _ => return Err( error( attr ) ), + } + } + + Ok( result ) + } +} + +impl< IntoT > Assign< AttributePropertyIndex, IntoT > for FieldAttributes +where + IntoT : Into< AttributePropertyIndex >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.index.assign( component.into() ); + } +} + + +// == Attribute properties + +/// Marker type for attribute property to indicate whether a index code should be generated. +/// Defaults to `false`, meaning no index code is generated unless explicitly requested. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyIndexMarker; + +impl AttributePropertyComponent for AttributePropertyIndexMarker +{ + const KEYWORD : & 'static str = "index"; +} + +/// Indicates whether a index code should be generated. +/// Defaults to `false`, meaning no index code is generated unless explicitly requested. +pub type AttributePropertyIndex = AttributePropertyOptionalSingletone< AttributePropertyIndexMarker >; + +// == + + diff --git a/module/core/derive_tools_meta/src/derive/index/item_attributes.rs b/module/core/derive_tools_meta/src/derive/index/item_attributes.rs new file mode 100644 index 0000000000..0e223789e0 --- /dev/null +++ b/module/core/derive_tools_meta/src/derive/index/item_attributes.rs @@ -0,0 +1,80 @@ +use macro_tools:: +{ + ct, + syn_err, + syn, + qt, + Result, + AttributePropertyComponent, + AttributePropertyOptionalSingletone, +}; + +/// Represents the attributes of a struct. Aggregates all its attributes. +#[ derive( Debug, Default ) ] +pub struct ItemAttributes +{ + pub debug : AttributePropertyDebug, +} + +impl ItemAttributes +{ + /// Constructs a `ItemAttributes` instance from an iterator of attributes. + /// + /// This function parses the provided attributes and assigns them to the + /// appropriate fields in the `ItemAttributes` struct. + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = & 'a syn::Attribute > ) -> Result< Self > + { + let result = Self::default(); + + // Closure to generate an error message for unknown attributes. + let error = | attr : & syn::Attribute | -> syn::Error + { + let known_attributes = ct::concatcp! + ( + "Known attributes are: ", + "debug", + "." + ); + syn_err! + ( + attr, + "Expects an attribute of format '#[ attribute ]'\n {known_attributes}\n But got: '{}'", + qt! { #attr } + ) + }; + + for attr in attrs + { + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; + let key_str = format!( "{}", key_ident ); + match key_str.as_ref() + { + "debug" => {}, + _ => {}, + // _ => return Err( error( attr ) ), + } + } + + Ok( result ) + } +} + +// == Attribute properties + +/// Marker type for attribute property to specify whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyDebugMarker; + +impl AttributePropertyComponent for AttributePropertyDebugMarker +{ + const KEYWORD : & 'static str = "debug"; +} + +/// Specifies whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +pub type AttributePropertyDebug = AttributePropertyOptionalSingletone< AttributePropertyDebugMarker >; + +// == + + diff --git a/module/core/derive_tools_meta/src/lib.rs b/module/core/derive_tools_meta/src/lib.rs index 972b62d689..05aaebac0a 100644 --- a/module/core/derive_tools_meta/src/lib.rs +++ b/module/core/derive_tools_meta/src/lib.rs @@ -615,9 +615,10 @@ pub fn phantom( _attr: proc_macro::TokenStream, input : proc_macro::TokenStream #[ proc_macro_derive ( Index, - attributes( - debug, - index + attributes + ( + debug, // item + index, // field ) )] pub fn derive_index( input : proc_macro::TokenStream ) -> proc_macro::TokenStream