diff --git a/module/core/derive_tools/Cargo.toml b/module/core/derive_tools/Cargo.toml index a2fb09b022..777d5f745f 100644 --- a/module/core/derive_tools/Cargo.toml +++ b/module/core/derive_tools/Cargo.toml @@ -151,7 +151,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" ] @@ -180,6 +180,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 5db00f6a68..50a2fe8ed6 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/compiletime/enum.rs b/module/core/derive_tools/tests/inc/index/compiletime/enum.rs new file mode 100644 index 0000000000..543ebde1f0 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/enum.rs @@ -0,0 +1,13 @@ +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..e150d4f21d --- /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..ec15e88da3 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct_named_empty.rs @@ -0,0 +1,10 @@ +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..84375aef65 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/compiletime/struct_unit.rs @@ -0,0 +1,9 @@ +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/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..6153f8751e --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_named.rs @@ -0,0 +1,14 @@ +#[ 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..18dc0a71e1 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_multiple_tuple.rs @@ -0,0 +1,8 @@ +#[ test ] +fn index() +{ + 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/only_test/struct_named.rs b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs new file mode 100644 index 0000000000..53b2ab3f79 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_named.rs @@ -0,0 +1,13 @@ +#[ test ] +fn index() +{ + 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 new file mode 100644 index 0000000000..ef3d479590 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/only_test/struct_tuple.rs @@ -0,0 +1,9 @@ +#[ test ] +fn index() +{ + let x = StructTuple( 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..3dea73695e --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_named.rs @@ -0,0 +1,13 @@ +#![ 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_named_manual.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_named_manual.rs new file mode 100644 index 0000000000..6e5bc2cba2 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_named_manual.rs @@ -0,0 +1,20 @@ +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.rs b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple.rs new file mode 100644 index 0000000000..1228949d1f --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple.rs @@ -0,0 +1,13 @@ +#![ 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 new file mode 100644 index 0000000000..12a58b2ae6 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_multiple_tuple_manual.rs @@ -0,0 +1,16 @@ +use core::ops::Index; + +#[ allow( dead_code) ] +struct StructMultipleTuple< T >( bool, Vec< T > ); + +impl< T > Index< usize > for StructMultipleTuple< 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 new file mode 100644 index 0000000000..ca5b884595 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_named.rs @@ -0,0 +1,12 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( the_module::Index ) ] +struct StructNamed< T > +{ + #[ index ] + 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 new file mode 100644 index 0000000000..f09d4bbeb1 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_named_manual.rs @@ -0,0 +1,19 @@ +use core::ops::Index; + +#[ allow( dead_code ) ] +struct StructNamed< T > +{ + a: Vec< T > +} + +impl< T > Index< usize > for StructNamed< T > +{ + type Output = T; + + fn index( &self, index : usize ) -> &Self::Output + { + &self.a[ index ] + } +} + +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 new file mode 100644 index 0000000000..97728a8753 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_tuple.rs @@ -0,0 +1,11 @@ +use super::*; + +#[ allow( dead_code ) ] +#[ derive( the_module::Index ) ] +struct StructTuple< T > +( + #[ index ] + Vec< 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 new file mode 100644 index 0000000000..14582ff909 --- /dev/null +++ b/module/core/derive_tools/tests/inc/index/struct_tuple_manual.rs @@ -0,0 +1,16 @@ +use core::ops::Index; + +#[ allow( dead_code) ] +struct StructTuple< T >( Vec< T > ); + +impl< T > Index< usize > for StructTuple< T > +{ + type Output = T; + + fn index( &self, index : usize ) -> &Self::Output + { + &self.0[ index ] + } +} + +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 36c16aa17c..feff00a160 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" ) @@ -294,3 +295,38 @@ mod phantom_tests } } } + + +#[ cfg( feature = "derive_index" ) ] +#[ path = "index" ] +mod index_tests +{ + #[ allow( unused_imports ) ] + use super::*; + + 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; + + 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/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 68b4f1bb76..b8e4e131cd 100644 --- a/module/core/derive_tools_meta/src/derive.rs +++ b/module/core/derive_tools_meta/src/derive.rs @@ -18,6 +18,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..19a68ef63b --- /dev/null +++ b/module/core/derive_tools_meta/src/derive/index.rs @@ -0,0 +1,211 @@ +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::Enum( _ ) => + unimplemented!( "Index not implemented for Enum" ), + StructLike::Unit( _ ) => + unimplemented!( "Index not implemented for Unit" ), + }?; + + 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 `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 )* + } + } + } + ) +} + 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 d66ac3ca51..05aaebac0a 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,63 @@ 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 syntax of struct type. +/// +/// ## Example Usage +// +/// Instead of manually implementing `Index< T >` for `IsTransparent`: +/// +/// ```rust +/// use core::ops::Index; +/// pub struct IsTransparent< T > +/// { +/// a : Vec< T >, +/// } +/// +/// impl< T > Index< usize > for IsTransparent< T > +/// { +/// type Output = T; +/// +/// #[ inline( always ) ] +/// fn index( &self, index : usize ) -> &Self::Output +/// { +/// &self.a[ index ] +/// } +/// } +/// ``` +/// +/// Use `#[ index ]` to automatically generate the implementation: +/// +/// ```rust +/// # use derive_tools_meta::*; +/// pub struct IsTransparent< T > +/// { +/// a : Vec< T > +/// }; +/// ``` +/// + +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "derive_index" ) ] +#[ proc_macro_derive +( + Index, + attributes + ( + debug, // item + index, // field + ) +)] +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(), + } +}