diff --git a/Cargo.toml b/Cargo.toml index 2f07b12e04..2819bbf5ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,7 +104,7 @@ default-features = false features = [ "enabled" ] [workspace.dependencies.collection_tools] -version = "~0.6.0" +version = "~0.8.0" path = "module/core/collection_tools" default-features = false @@ -214,6 +214,11 @@ version = "~2.0.0" path = "module/core/former_meta" default-features = false +[workspace.dependencies.former_types] +version = "~2.1.0" +path = "module/core/former_types" +default-features = false + [workspace.dependencies.impls_index] version = "~0.7.0" path = "module/core/impls_index" diff --git a/module/core/collection_tools/Cargo.toml b/module/core/collection_tools/Cargo.toml index 066aad99c0..65a81d7cde 100644 --- a/module/core/collection_tools/Cargo.toml +++ b/module/core/collection_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "collection_tools" -version = "0.6.0" +version = "0.8.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -39,20 +39,20 @@ use_alloc = [ default = [ "enabled", - "reexports", + # "reexports", "collection_constructors", "collection_into_constructors", ] full = [ "enabled", - "reexports", + # "reexports", "collection_constructors", "collection_into_constructors", ] enabled = [] -reexports = [] +# reexports = [] # Collection constructors, like `hmap!{ "key" => "val" }` collection_constructors = [] diff --git a/module/core/collection_tools/src/lib.rs b/module/core/collection_tools/src/lib.rs index 5ce79350a2..6171c0e365 100644 --- a/module/core/collection_tools/src/lib.rs +++ b/module/core/collection_tools/src/lib.rs @@ -8,10 +8,6 @@ #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] extern crate alloc; -// qqq : make subdirectory for each container -- done - -// qqq : move out of lib.rs file -- moved to `collections.rs` - /// Module containing all collection macros #[ cfg( feature = "enabled" ) ] #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] @@ -59,18 +55,12 @@ pub mod orphan #[ cfg( feature = "enabled" ) ] pub mod exposed { + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::prelude::*; -} - -/// Prelude to use essentials: `use my_module::prelude::*`. -#[ cfg( feature = "enabled" ) ] -pub mod prelude -{ - // qqq : for Anton : uncomment, make it working and cover by tests -- renamed to reexports - #[ cfg( feature = "reexports" ) ] + // #[ cfg( feature = "reexports" ) ] #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] @@ -86,14 +76,23 @@ pub mod prelude vecd::VecDeque, }; - #[ cfg( feature = "reexports" ) ] + // #[ cfg( feature = "reexports" ) ] #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use + pub use { HashMap as Map, HashSet as Set, Vec as DynArray, }; + + // qqq : cover by tests presence of all containers immidiately in collection_tools::* and in collection_tools::exposed::* + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] +pub mod prelude +{ } diff --git a/module/core/data_type/Cargo.toml b/module/core/data_type/Cargo.toml index 8b21b92043..cbb30c67f3 100644 --- a/module/core/data_type/Cargo.toml +++ b/module/core/data_type/Cargo.toml @@ -54,7 +54,8 @@ no_std = [] use_alloc = [ "no_std" ] enabled = [] -dt_prelude = [ "collection_tools/reexports" ] +# dt_prelude = [ "collection_tools/reexports" ] +dt_prelude = [] # rid off maybe? dt_interval = [ "interval_adapter/enabled" ] dt_collections = [ "collection_tools/enabled" ] dt_either = [ "either" ] diff --git a/module/core/derive_tools_meta/src/derive/from.rs b/module/core/derive_tools_meta/src/derive/from.rs index e0877b372e..e3f330d149 100644 --- a/module/core/derive_tools_meta/src/derive/from.rs +++ b/module/core/derive_tools_meta/src/derive/from.rs @@ -372,7 +372,7 @@ impl FieldAttributes for attr in attrs { let key_ident = attr.path().get_ident() - .ok_or_else( || syn_err!( attr, "Expects an attribute of format #[ attribute( val ) ], but got:\n {}", qt!{ #attr } ) )?; + .ok_or_else( || syn_err!( attr, "Expects an attribute of format #[ attribute( key1 = val1, key2 = val2 ) ], but got:\n {}", qt!{ #attr } ) )?; let key_str = format!( "{}", key_ident ); if attr::is_standard( &key_str ) @@ -518,9 +518,9 @@ impl syn::parse::Parse for AttributeFrom } // Optional comma handling - if input.peek( syn::Token![,] ) + if input.peek( syn::Token![ , ] ) { - input.parse::< syn::Token![,] >()?; + input.parse::< syn::Token![ , ] >()?; } Ok( Self { enabled, hint } ) } diff --git a/module/core/former/Cargo.toml b/module/core/former/Cargo.toml index ed2bfed78f..645deff0c3 100644 --- a/module/core/former/Cargo.toml +++ b/module/core/former/Cargo.toml @@ -26,8 +26,12 @@ all-features = false [features] -no_std = [ "collection_tools/no_std" ] -use_alloc = [ "no_std", "collection_tools/use_alloc" ] +no_std = [ "former_types/no_std", "collection_tools/no_std" ] +use_alloc = [ "no_std", "former_types/use_alloc", "collection_tools/use_alloc" ] + + +# no_std = [ "collection_tools/no_std" ] +# use_alloc = [ "no_std", "collection_tools/use_alloc" ] default = [ "enabled", @@ -47,11 +51,11 @@ full = [ "derive_components_assign", "derive_from_components", ] -enabled = [ "former_meta/enabled", "collection_tools/enabled" ] +enabled = [ "former_meta/enabled", "former_types/enabled" ] -derive_former = [ "former_meta/derive_former" ] -derive_components = [ "former_meta/derive_components" ] -derive_component_assign = [ "derive_components", "former_meta/derive_component_assign" ] +derive_former = [ "former_meta/derive_former", "former_types/derive_former" ] +derive_components = [ "former_meta/derive_components", "former_types/types_components" ] +derive_component_assign = [ "derive_components", "former_meta/derive_component_assign", "former_types/types_component_assign" ] derive_components_assign = [ "derive_components", "derive_component_assign", "former_meta/derive_components_assign" ] derive_component_from = [ "derive_components", "former_meta/derive_component_from" ] derive_from_components = [ "derive_components", "former_meta/derive_from_components" ] @@ -59,8 +63,10 @@ derive_from_components = [ "derive_components", "former_meta/derive_from_compone [dependencies] former_meta = { workspace = true } -collection_tools = { workspace = true, features = [ "collection_constructors", "reexports" ] } +former_types = { workspace = true } +# collection_tools = { workspace = true, features = [ "collection_constructors" ] } [dev-dependencies] test_tools = { workspace = true, features = [ "full" ] } +collection_tools = { workspace = true, features = [ "collection_constructors" ] } diff --git a/module/core/former/src/lib.rs b/module/core/former/src/lib.rs index 85b73f8e13..2eb4e674d2 100644 --- a/module/core/former/src/lib.rs +++ b/module/core/former/src/lib.rs @@ -4,38 +4,39 @@ #![ doc( html_root_url = "https://docs.rs/former/latest/former/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -/// Axiomatic things. -#[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "derive_former" ) ] -mod axiomatic; -/// Forming process. -#[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "derive_former" ) ] -mod definition; -/// Forming process. -#[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "derive_former" ) ] -mod forming; -/// Storage. -#[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "derive_former" ) ] -mod storage; - -/// Interface for collections. -#[ cfg( feature = "enabled" ) ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ cfg( feature = "derive_former" ) ] -mod collection; - -/// Component-based forming. -#[ cfg( feature = "enabled" ) ] -#[ cfg( any( feature = "derive_component_from", feature = "derive_component_assign" ) ) ] -mod component; +// /// Axiomatic things. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( feature = "derive_former" ) ] +// mod axiomatic; +// /// Forming process. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( feature = "derive_former" ) ] +// mod definition; +// /// Forming process. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( feature = "derive_former" ) ] +// mod forming; +// /// Storage. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( feature = "derive_former" ) ] +// mod storage; +// +// /// Interface for collections. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// #[ cfg( feature = "derive_former" ) ] +// mod collection; +// +// /// Component-based forming. +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( any( feature = "derive_component_from", feature = "derive_component_assign" ) ) ] +// mod component; /// Namespace with dependencies. #[ cfg( feature = "enabled" ) ] pub mod dependency { + pub use former_types; pub use former_meta; } @@ -80,22 +81,24 @@ pub mod exposed #[ doc( inline ) ] #[ allow( unused_imports ) ] - #[ cfg( feature = "enabled" ) ] - #[ cfg( feature = "derive_former" ) ] - pub use super:: - { - axiomatic::*, - definition::*, - forming::*, - storage::*, - }; + pub use former_types::exposed::*; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - #[ cfg( feature = "enabled" ) ] - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - #[ cfg( feature = "derive_former" ) ] - pub use super::collection::*; +// #[ doc( inline ) ] +// #[ allow( unused_imports ) ] +// #[ cfg( feature = "derive_former" ) ] +// pub use super:: +// { +// axiomatic::*, +// definition::*, +// forming::*, +// storage::*, +// }; +// +// #[ doc( inline ) ] +// #[ allow( unused_imports ) ] +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// #[ cfg( feature = "derive_former" ) ] +// pub use super::collection::*; } @@ -103,9 +106,14 @@ pub mod exposed #[ cfg( feature = "enabled" ) ] pub mod prelude { + + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // #[ cfg( any( feature = "derive_component_from", feature = "derive_component_assign" ) ) ] + // pub use super::component::*; + #[ doc( inline ) ] #[ allow( unused_imports ) ] - #[ cfg( feature = "enabled" ) ] - #[ cfg( any( feature = "derive_component_from", feature = "derive_component_assign" ) ) ] - pub use super::component::*; + pub use former_types::prelude::*; + } diff --git a/module/core/former/tests/inc/components_tests/component_assign.rs b/module/core/former/tests/inc/components_tests/component_assign.rs index 9ca13cbcba..b2b1fdde8b 100644 --- a/module/core/former/tests/inc/components_tests/component_assign.rs +++ b/module/core/former/tests/inc/components_tests/component_assign.rs @@ -14,4 +14,4 @@ struct Person // -include!( "./only_test/components_component_assign.rs" ); +include!( "./only_test/component_assign.rs" ); diff --git a/module/core/former/tests/inc/components_tests/component_assign_manual.rs b/module/core/former/tests/inc/components_tests/component_assign_manual.rs index d2aff86d2c..d858c5f989 100644 --- a/module/core/former/tests/inc/components_tests/component_assign_manual.rs +++ b/module/core/former/tests/inc/components_tests/component_assign_manual.rs @@ -33,4 +33,4 @@ where // -include!( "./only_test/components_component_assign.rs" ); +include!( "./only_test/component_assign.rs" ); diff --git a/module/core/former/tests/inc/components_tests/component_from.rs b/module/core/former/tests/inc/components_tests/component_from.rs index 965218114f..2151d3d3d7 100644 --- a/module/core/former/tests/inc/components_tests/component_from.rs +++ b/module/core/former/tests/inc/components_tests/component_from.rs @@ -17,4 +17,4 @@ pub struct Options1 // -include!( "./only_test/components_component_from.rs" ); +include!( "./only_test/component_from.rs" ); diff --git a/module/core/former/tests/inc/components_tests/component_from_manual.rs b/module/core/former/tests/inc/components_tests/component_from_manual.rs index 215a1f6ff5..94e854b381 100644 --- a/module/core/former/tests/inc/components_tests/component_from_manual.rs +++ b/module/core/former/tests/inc/components_tests/component_from_manual.rs @@ -42,4 +42,4 @@ impl From< &Options1 > for f32 // -include!( "./only_test/components_component_from.rs" ); +include!( "./only_test/component_from.rs" ); diff --git a/module/core/former/tests/inc/components_tests/components_assign.rs b/module/core/former/tests/inc/components_tests/components_assign.rs index e2dbb6fda4..84f01db2a8 100644 --- a/module/core/former/tests/inc/components_tests/components_assign.rs +++ b/module/core/former/tests/inc/components_tests/components_assign.rs @@ -73,4 +73,4 @@ impl From< &Options2 > for String // -include!( "./only_test/components_components_assign.rs" ); +include!( "./only_test/components_assign.rs" ); diff --git a/module/core/former/tests/inc/components_tests/components_assign_manual.rs b/module/core/former/tests/inc/components_tests/components_assign_manual.rs index 182ad0dacf..9d7907d05f 100644 --- a/module/core/former/tests/inc/components_tests/components_assign_manual.rs +++ b/module/core/former/tests/inc/components_tests/components_assign_manual.rs @@ -192,4 +192,4 @@ where // -include!( "./only_test/components_components_assign.rs" ); +include!( "./only_test/components_assign.rs" ); diff --git a/module/core/former/tests/inc/components_tests/composite.rs b/module/core/former/tests/inc/components_tests/composite.rs index 7105ba0b0e..723f3be16c 100644 --- a/module/core/former/tests/inc/components_tests/composite.rs +++ b/module/core/former/tests/inc/components_tests/composite.rs @@ -72,4 +72,4 @@ pub struct Options2 // -include!( "./only_test/components_composite.rs" ); +include!( "./only_test/composite.rs" ); diff --git a/module/core/former/tests/inc/components_tests/composite_manual.rs b/module/core/former/tests/inc/components_tests/composite_manual.rs index 34e77f09af..03fc1f28f9 100644 --- a/module/core/former/tests/inc/components_tests/composite_manual.rs +++ b/module/core/former/tests/inc/components_tests/composite_manual.rs @@ -209,4 +209,4 @@ where // -include!( "./only_test/components_composite.rs" ); +include!( "./only_test/composite.rs" ); diff --git a/module/core/former/tests/inc/components_tests/from_components.rs b/module/core/former/tests/inc/components_tests/from_components.rs index 9f69aab624..2105667d9f 100644 --- a/module/core/former/tests/inc/components_tests/from_components.rs +++ b/module/core/former/tests/inc/components_tests/from_components.rs @@ -72,4 +72,4 @@ pub struct Options2 // -include!( "./only_test/components_from_components.rs" ); +include!( "./only_test/from_components.rs" ); diff --git a/module/core/former/tests/inc/components_tests/from_components_manual.rs b/module/core/former/tests/inc/components_tests/from_components_manual.rs index 6a6c29e323..edd26c9c80 100644 --- a/module/core/former/tests/inc/components_tests/from_components_manual.rs +++ b/module/core/former/tests/inc/components_tests/from_components_manual.rs @@ -72,4 +72,4 @@ where // -include!( "./only_test/components_from_components.rs" ); +include!( "./only_test/from_components.rs" ); diff --git a/module/core/former/tests/inc/components_tests/only_test/components_component_assign.rs b/module/core/former/tests/inc/components_tests/only_test/component_assign.rs similarity index 100% rename from module/core/former/tests/inc/components_tests/only_test/components_component_assign.rs rename to module/core/former/tests/inc/components_tests/only_test/component_assign.rs diff --git a/module/core/former/tests/inc/components_tests/only_test/components_component_from.rs b/module/core/former/tests/inc/components_tests/only_test/component_from.rs similarity index 100% rename from module/core/former/tests/inc/components_tests/only_test/components_component_from.rs rename to module/core/former/tests/inc/components_tests/only_test/component_from.rs diff --git a/module/core/former/tests/inc/components_tests/only_test/components_components_assign.rs b/module/core/former/tests/inc/components_tests/only_test/components_assign.rs similarity index 96% rename from module/core/former/tests/inc/components_tests/only_test/components_components_assign.rs rename to module/core/former/tests/inc/components_tests/only_test/components_assign.rs index ecba41850c..37d11147aa 100644 --- a/module/core/former/tests/inc/components_tests/only_test/components_components_assign.rs +++ b/module/core/former/tests/inc/components_tests/only_test/components_assign.rs @@ -7,7 +7,7 @@ fn component_assign() let mut o2 = Options2::default(); o2.assign( 42 ); o2.assign( "Hello, world!" ); - println!( "field1: {}, field2: {}", o2.field1, o2.field2 ); + println!( "field1 : {}, field2 : {}", o2.field1, o2.field2 ); let exp = Options2 { field1 : 42, field2 : "Hello, world!".to_string() }; assert_eq!( o2, exp ); diff --git a/module/core/former/tests/inc/components_tests/only_test/components_composite.rs b/module/core/former/tests/inc/components_tests/only_test/composite.rs similarity index 100% rename from module/core/former/tests/inc/components_tests/only_test/components_composite.rs rename to module/core/former/tests/inc/components_tests/only_test/composite.rs diff --git a/module/core/former/tests/inc/components_tests/only_test/components_from_components.rs b/module/core/former/tests/inc/components_tests/only_test/from_components.rs similarity index 100% rename from module/core/former/tests/inc/components_tests/only_test/components_from_components.rs rename to module/core/former/tests/inc/components_tests/only_test/from_components.rs diff --git a/module/core/former_meta/Cargo.toml b/module/core/former_meta/Cargo.toml index 8eb10e870d..770817a07f 100644 --- a/module/core/former_meta/Cargo.toml +++ b/module/core/former_meta/Cargo.toml @@ -44,7 +44,7 @@ full = [ "derive_components_assign", "derive_from_components", ] -enabled = [ "macro_tools/enabled", "iter_tools/enabled" ] +enabled = [ "macro_tools/enabled", "iter_tools/enabled", "former_types/enabled" ] derive_former = [ "convert_case" ] derive_components = [] @@ -57,9 +57,12 @@ derive_from_components = [ "derive_components" ] proc-macro = true [dependencies] -macro_tools = { workspace = true } +macro_tools = { workspace = true } # qqq : optimize set of features +former_types = { workspace = true, features = [ "types_component_assign" ] } # qqq : optimize set of features iter_tools = { workspace = true } convert_case = { version = "0.6.0", default-features = false, optional = true, features = [] } +const_format = { version = "0.2.32" } +# zzz : reexport const_format [dev-dependencies] test_tools = { workspace = true, features = [ "full" ] } diff --git a/module/core/former_meta/src/component/components_assign.rs b/module/core/former_meta/src/component/components_assign.rs index 8ceb0f3ad3..4a69425dd3 100644 --- a/module/core/former_meta/src/component/components_assign.rs +++ b/module/core/former_meta/src/component/components_assign.rs @@ -21,7 +21,7 @@ pub fn components_assign( input : proc_macro::TokenStream ) -> Result< proc_macr let trait_ident = syn::Ident::new( &trait_name, item_name.span() ); let method_name = format!( "{}_assign", item_name.to_string().to_case( Case::Snake ) ); let method_ident = syn::Ident::new( &method_name, item_name.span() ); - // xxx : make a macro ident_format!() + // xxx : use macro ident_format!() // fields let ( bounds1, bounds2, component_assigns ) : ( Vec< _ >, Vec< _ >, Vec< _ > ) = parsed.fields.iter().map( | field | diff --git a/module/core/former_meta/src/derive_former.rs b/module/core/former_meta/src/derive_former.rs index 0a584b0db2..d3093505cd 100644 --- a/module/core/former_meta/src/derive_former.rs +++ b/module/core/former_meta/src/derive_former.rs @@ -6,11 +6,10 @@ use proc_macro2::TokenStream; // qqq : implement interfaces for other collections - -mod field; -use field::*; mod field_attrs; use field_attrs::*; +mod field; +use field::*; mod struct_attrs; use struct_attrs::*; @@ -54,7 +53,7 @@ pub fn mutator ) -> Result< TokenStream > { - let former_mutator_code = if mutator.custom + let former_mutator_code = if mutator.custom.into() { qt!{} } @@ -71,7 +70,7 @@ pub fn mutator } }; - if mutator.hint + if mutator.hint.into() { let hint = format! ( diff --git a/module/core/former_meta/src/derive_former/field.rs b/module/core/former_meta/src/derive_former/field.rs index b6bf5a9ce3..015eba4a1c 100644 --- a/module/core/former_meta/src/derive_former/field.rs +++ b/module/core/former_meta/src/derive_former/field.rs @@ -184,7 +184,7 @@ scalar_setter_required let ident = self.ident; let ty = self.ty; let default : Option< &syn::Expr > = self.attrs.config.as_ref() - .and_then( | attr | attr.default.as_ref() ); + .and_then( | attr | attr.default.ref_internal() ); let tokens = if self.is_optional { @@ -456,7 +456,7 @@ scalar_setter_required let setter_name = self.scalar_setter_name(); let attr = self.attrs.scalar.as_ref(); - if attr.is_some() && attr.unwrap().hint + if attr.is_some() && attr.unwrap().hint.into() { let hint = format! ( @@ -682,7 +682,7 @@ field : {field_ident}"#, qt!{} }; - if attr.hint + if attr.hint.into() { let hint = format! ( @@ -725,7 +725,7 @@ field : {field_ident}"#, }; // example : `former::VectorDefinition`` - let subformer_definition = &self.attrs.subform_collection.as_ref().unwrap().definition; + let subformer_definition = self.attrs.subform_collection.as_ref().unwrap().definition.ref_internal(); let subform_collection_end_doc = format! ( @@ -975,7 +975,7 @@ allowing for dynamic and flexible construction of the `{stru}` entity's {field_i setters_code }; - if attr.hint + if attr.hint.into() { let hint = format! ( @@ -1285,7 +1285,7 @@ former and end action types, ensuring a seamless developer experience when formi setters_code }; - if attr.hint + if attr.hint.into() { let hint = format! ( @@ -1451,7 +1451,7 @@ Essentially, this end action integrates the individually formed scalar value bac { if let Some( ref attr ) = self.attrs.scalar { - if let Some( ref name ) = attr.name + if let Some( ref name ) = attr.name.ref_internal() { return name } @@ -1466,7 +1466,7 @@ Essentially, this end action integrates the individually formed scalar value bac { if attr.setter() { - if let Some( ref name ) = attr.name + if let Some( ref name ) = attr.name.ref_internal() { return Some( &name ) } @@ -1486,7 +1486,7 @@ Essentially, this end action integrates the individually formed scalar value bac { if attr.setter() { - if let Some( ref name ) = attr.name + if let Some( ref name ) = attr.name.ref_internal() { return Some( &name ) } @@ -1506,7 +1506,7 @@ Essentially, this end action integrates the individually formed scalar value bac { if attr.setter() { - if let Some( ref name ) = attr.name + if let Some( ref name ) = attr.name.as_ref() { return Some( &name ) } @@ -1526,7 +1526,7 @@ Essentially, this end action integrates the individually formed scalar value bac let mut explicit = false; if let Some( ref attr ) = self.attrs.scalar { - if let Some( setter ) = attr.setter + if let Some( setter ) = attr.setter.internal() { if setter == false { @@ -1534,7 +1534,7 @@ Essentially, this end action integrates the individually formed scalar value bac } explicit = true; } - if let Some( ref _name ) = attr.name + if let Some( ref _name ) = attr.name.ref_internal() { explicit = true; } diff --git a/module/core/former_meta/src/derive_former/field_attrs.rs b/module/core/former_meta/src/derive_former/field_attrs.rs index b6104b3551..a4f7a1357f 100644 --- a/module/core/former_meta/src/derive_former/field_attrs.rs +++ b/module/core/former_meta/src/derive_former/field_attrs.rs @@ -1,72 +1,114 @@ +//! Attributes of a field. use super::*; -use macro_tools::{ attr, Result }; +use macro_tools:: +{ + attr, + Result, + AttributeComponent, + AttributePropertyComponent, + AttributePropertyBoolean, + AttributePropertyOptionalBoolean, + AttributePropertyOptionalSyn, +}; +use former_types::{ ComponentAssign }; /// /// Attributes of a field. /// +#[ derive( Debug, Default ) ] pub struct FieldAttributes { + /// Configuration attribute for a field. pub config : Option< AttributeConfig >, + + /// Scalar setter attribute for a field. pub scalar : Option< AttributeScalarSetter >, + + /// Subform scalar setter attribute for a field. pub subform_scalar : Option< AttributeSubformScalarSetter >, + + /// Subform collection setter attribute for a field. pub subform_collection : Option< AttributeSubformCollectionSetter >, + + /// Subform entry setter attribute for a field. pub subform_entry : Option< AttributeSubformEntrySetter >, } impl FieldAttributes { + + /// Creates an instance of `FieldAttributes` from a list of attributes. + /// + /// # Parameters + /// + /// * `attrs`: An iterator over references to `syn::Attribute`. + /// + /// # Returns + /// + /// * `Result< Self >`: A result containing an instance of `FieldAttributes` on success, + /// or a `syn::Error` on failure. + /// + /// This function processes each attribute in the provided iterator and assigns the + /// appropriate attribute type to the respective field in the `FieldAttributes` struct. + /// pub fn from_attrs< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> Result< Self > { - let mut config = None; - let mut scalar = None; - let mut subform_scalar = None; - let mut subform_collection = None; - let mut subform_entry = None; + let mut result = Self::default(); + // Known attributes for error reporting + let known_attributes = const_format::concatcp! + ( + "Known attributes are : ", + "debug", + ", ", AttributeConfig::KEYWORD, + ", ", AttributeScalarSetter::KEYWORD, + ", ", AttributeSubformScalarSetter::KEYWORD, + ", ", AttributeSubformCollectionSetter::KEYWORD, + ", ", AttributeSubformEntrySetter::KEYWORD, + ".", + ); + + // Helper closure to create a syn::Error for unknown attributes + let error = | attr : &syn::Attribute | -> syn::Error + { + syn_err! + ( + attr, + "Expects an attribute of format `#[ attribute( key1 = val1, key2 = val2 ) ]`\n {known_attributes}\n But got:\n `{}`", + qt!{ #attr } + ) + }; + + // Iterate over the provided attributes for attr in attrs { - let key_ident = attr.path().get_ident() - .ok_or_else( || syn_err!( attr, "Expects an attribute of format #[ attribute( val ) ], but got:\n {}", qt!{ #attr } ) )?; + // Get the attribute key as a string + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; let key_str = format!( "{}", key_ident ); + // Skip standard attributes if attr::is_standard( &key_str ) { continue; } + // Match the attribute key and assign to the appropriate field match key_str.as_ref() { - AttributeConfig::KEYWORD => - { - config.replace( AttributeConfig::from_meta( &attr )? ); - } - AttributeScalarSetter::KEYWORD => - { - // qqq : move this part of parsing into attribute. do that for all attributes -- done - scalar.replace( AttributeScalarSetter::from_meta( &attr )? ); - } - AttributeSubformScalarSetter::KEYWORD => - { - subform_scalar.replace( AttributeSubformScalarSetter::from_meta( &attr )? ); - } - AttributeSubformCollectionSetter::KEYWORD => - { - subform_collection.replace( AttributeSubformCollectionSetter::from_meta( &attr )? ); - } - AttributeSubformEntrySetter::KEYWORD => - { - subform_entry.replace( AttributeSubformEntrySetter::from_meta( &attr )? ); - } - _ => - { - return Err( syn_err!( attr, "Unknown field attribute {}", qt!{ #attr } ) ); - } + AttributeConfig::KEYWORD => result.assign( AttributeConfig::from_meta( attr )? ), + AttributeScalarSetter::KEYWORD => result.assign( AttributeScalarSetter::from_meta( attr )? ), + AttributeSubformScalarSetter::KEYWORD => result.assign( AttributeSubformScalarSetter::from_meta( attr )? ), + AttributeSubformCollectionSetter::KEYWORD => result.assign( AttributeSubformCollectionSetter::from_meta( attr )? ), + AttributeSubformEntrySetter::KEYWORD => result.assign( AttributeSubformEntrySetter::from_meta( attr )? ), + "debug" => {} + _ => return Err( error( attr ) ), } } - Ok( FieldAttributes { config, scalar, subform_scalar, subform_collection, subform_entry } ) + Ok( result ) } + } /// @@ -75,20 +117,21 @@ impl FieldAttributes /// `#[ default( 13 ) ]` /// +#[ derive( Debug, Default ) ] pub struct AttributeConfig { - /// Default value to use for the field. - pub default : Option< syn::Expr >, + /// Default value to use for a field. + pub default : AttributePropertyDefault, } -impl AttributeConfig +impl AttributeComponent for AttributeConfig { - const KEYWORD: &'static str = "former"; + const KEYWORD : &'static str = "former"; - pub fn from_meta( attr : &syn::Attribute ) -> Result< Self > + fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta { @@ -106,12 +149,52 @@ impl AttributeConfig } +impl< IntoT > ComponentAssign< AttributeConfig, IntoT > for FieldAttributes +where + IntoT : Into< AttributeConfig >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.config = Some( component.into() ); + } +} + +impl< IntoT > ComponentAssign< AttributePropertyDefault, IntoT > for AttributeConfig +where + IntoT : Into< AttributePropertyDefault >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.default = component.into(); + } +} + impl syn::parse::Parse for AttributeConfig { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut default : Option< syn::Expr > = None; - // let mut only_storage : Option< bool > = None; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeConfig::KEYWORD, " are : ", + AttributePropertyDefault::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ former( default = 13 ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; while !input.is_empty() { @@ -119,23 +202,17 @@ impl syn::parse::Parse for AttributeConfig if lookahead.peek( syn::Ident ) { let ident : syn::Ident = input.parse()?; + + input.parse::< syn::Token![=] >()?; match ident.to_string().as_str() { - "default" => - { - input.parse::< syn::Token![ = ] >()?; - default = Some( input.parse()? ); - } - _ => - { - return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'default'. For example: `former( default = 13 )`", ident ) ) ); - } + AttributePropertyDefault::KEYWORD => result.assign( AttributePropertyDefault::parse( input )? ), + _ => return Err( error( &ident ) ), } } - else { - return Err( syn::Error::new( input.span(), "Expected 'default'. For example: `former( default = 13 )`" ) ); + return Err( lookahead.error() ); } // Optional comma handling @@ -145,40 +222,40 @@ impl syn::parse::Parse for AttributeConfig } } - Ok( Self { default } ) + Ok( result ) } } -/// -/// Attribute to enable/disable scalar setter generation. -/// -/// ## Example Input -/// -/// A typical input to parse might look like the following: -/// -/// ```ignore -/// name = field_name, setter = true -/// ``` -/// - +#[ derive( Debug, Default ) ] pub struct AttributeScalarSetter { /// Optional identifier for naming the setter. - pub name : Option< syn::Ident >, + pub name : AttributePropertyName, /// Controls the generation of a setter method. If false, a setter method is not generated. - pub setter : Option< bool >, + pub setter : AttributePropertySetter, /// Specifies whether to provide a sketch of the subform setter as a hint. /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub hint : bool, + pub hint : AttributePropertyHint, } -#[ allow( dead_code ) ] impl AttributeScalarSetter +{ + + /// Should setter be generated or not? + #[ allow( dead_code ) ] + pub fn setter( &self ) -> bool + { + self.setter.is_none() || self.setter.unwrap() + } + +} + +impl AttributeComponent for AttributeScalarSetter { const KEYWORD : &'static str = "scalar"; - pub fn from_meta( attr : &syn::Attribute ) -> Result< Self > + fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta { @@ -194,21 +271,78 @@ impl AttributeScalarSetter } } - /// Should setter be generated or not? - pub fn setter( &self ) -> bool +} + +impl< IntoT > ComponentAssign< AttributeScalarSetter, IntoT > for FieldAttributes +where + IntoT : Into< AttributeScalarSetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) { - self.setter.is_none() || self.setter.unwrap() + self.scalar = Some( component.into() ); + } +} + +impl< IntoT > ComponentAssign< AttributePropertyName, IntoT > for AttributeScalarSetter +where + IntoT : Into< AttributePropertyName >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } +} + +impl< IntoT > ComponentAssign< AttributePropertySetter, IntoT > for AttributeScalarSetter +where + IntoT : Into< AttributePropertySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.setter = component.into(); } +} +impl< IntoT > ComponentAssign< AttributePropertyHint, IntoT > for AttributeScalarSetter +where + IntoT : Into< AttributePropertyHint >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.hint = component.into(); + } } impl syn::parse::Parse for AttributeScalarSetter { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut name : Option< syn::Ident > = None; - let mut setter : Option< bool > = None; - let mut hint = false; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeScalarSetter::KEYWORD, " are : ", + AttributePropertyName::KEYWORD, + ", ", AttributePropertySetter::KEYWORD, + ", ", AttributePropertyHint::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ scalar( name = myName, setter = true, hint = false ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; while !input.is_empty() { @@ -216,44 +350,29 @@ impl syn::parse::Parse for AttributeScalarSetter if lookahead.peek( syn::Ident ) { let ident : syn::Ident = input.parse()?; + + input.parse::< syn::Token![=] >()?; match ident.to_string().as_str() { - "name" => - { - input.parse::< syn::Token![ = ] >()?; - name = Some( input.parse()? ); - } - "setter" => - { - input.parse::< syn::Token![ = ] >()?; - let value : syn::LitBool = input.parse()?; - setter = Some( value.value() ); - } - "hint" => - { - input.parse::< syn::Token![ = ] >()?; - let value : syn::LitBool = input.parse()?; - hint = value.value; - } - _ => - { - return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'name', 'setter', or 'definition'. For example: `scalar( name = myName, setter = true )`", ident ) ) ); - } + AttributePropertyName::KEYWORD => result.assign( AttributePropertyName::parse( input )? ), + AttributePropertySetter::KEYWORD => result.assign( AttributePropertySetter::parse( input )? ), + AttributePropertyHint::KEYWORD => result.assign( AttributePropertyHint::parse( input )? ), + _ => return Err( error( &ident ) ), } } else { - return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `scalar( name = myName, setter = true )`" ) ); + return Err( lookahead.error() ); } // Optional comma handling - if input.peek( syn::Token![,] ) + if input.peek( syn::Token![ , ] ) { - input.parse::< syn::Token![,] >()?; + input.parse::< syn::Token![ , ] >()?; } } - Ok( Self { name, setter, hint } ) + Ok( result ) } } @@ -269,24 +388,36 @@ impl syn::parse::Parse for AttributeScalarSetter /// ``` /// +#[ derive( Debug, Default ) ] + pub struct AttributeSubformScalarSetter { /// Optional identifier for naming the setter. - pub name : Option< syn::Ident >, + pub name : AttributePropertyName, /// Controls the generation of a setter method. If false, a setter method is not generated. - pub setter : Option< bool >, + pub setter : AttributePropertySetter, /// Specifies whether to provide a sketch of the subform setter as a hint. /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub hint : bool, + pub hint : AttributePropertyHint, } -#[ allow( dead_code ) ] impl AttributeSubformScalarSetter { - const KEYWORD: &'static str = "subform_scalar"; + /// Should setter be generated or not? + pub fn setter( &self ) -> bool + { + self.setter.is_none() || self.setter.unwrap() + } + +} + +impl AttributeComponent for AttributeSubformScalarSetter +{ + + const KEYWORD : &'static str = "subform_scalar"; - pub fn from_meta( attr : &syn::Attribute ) -> Result< Self > + fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta { @@ -302,21 +433,78 @@ impl AttributeSubformScalarSetter } } - /// Should setter be generated or not? - pub fn setter( &self ) -> bool +} + +impl< IntoT > ComponentAssign< AttributeSubformScalarSetter, IntoT > for FieldAttributes +where + IntoT : Into< AttributeSubformScalarSetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) { - self.setter.is_none() || self.setter.unwrap() + self.subform_scalar = Some( component.into() ); } +} + +impl< IntoT > ComponentAssign< AttributePropertyName, IntoT > for AttributeSubformScalarSetter +where + IntoT : Into< AttributePropertyName >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } +} + +impl< IntoT > ComponentAssign< AttributePropertySetter, IntoT > for AttributeSubformScalarSetter +where + IntoT : Into< AttributePropertySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.setter = component.into(); + } +} +impl< IntoT > ComponentAssign< AttributePropertyHint, IntoT > for AttributeSubformScalarSetter +where + IntoT : Into< AttributePropertyHint >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.hint = component.into(); + } } impl syn::parse::Parse for AttributeSubformScalarSetter { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut name : Option< syn::Ident > = None; - let mut setter : Option< bool > = None; - let mut hint = false; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeSubformScalarSetter::KEYWORD, " are : ", + AttributePropertyName::KEYWORD, + ", ", AttributePropertySetter::KEYWORD, + ", ", AttributePropertyHint::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ subform_scalar( name = myName, setter = true, hint = false ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; while !input.is_empty() { @@ -324,47 +512,87 @@ impl syn::parse::Parse for AttributeSubformScalarSetter if lookahead.peek( syn::Ident ) { let ident : syn::Ident = input.parse()?; + + input.parse::< syn::Token![=] >()?; match ident.to_string().as_str() { - "name" => - { - input.parse::< syn::Token![ = ] >()?; - name = Some( input.parse()? ); - } - "setter" => - { - input.parse::< syn::Token![ = ] >()?; - let value : syn::LitBool = input.parse()?; - setter = Some( value.value() ); - } - "hint" => - { - input.parse::< syn::Token![ = ] >()?; - let value : syn::LitBool = input.parse()?; - hint = value.value; - } - _ => - { - return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'name', 'setter', or 'definition'. For example: `subform_scalar( name = myName, setter = true )`", ident ) ) ); - } + AttributePropertyName::KEYWORD => result.assign( AttributePropertyName::parse( input )? ), + AttributePropertySetter::KEYWORD => result.assign( AttributePropertySetter::parse( input )? ), + AttributePropertyHint::KEYWORD => result.assign( AttributePropertyHint::parse( input )? ), + _ => return Err( error( &ident ) ), } } else { - return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `subform_scalar( name = myName, setter = true )`" ) ); + return Err( lookahead.error() ); } // Optional comma handling - if input.peek( syn::Token![,] ) + if input.peek( syn::Token![ , ] ) { - input.parse::< syn::Token![,] >()?; + input.parse::< syn::Token![ , ] >()?; } } - Ok( Self { name, setter, hint } ) + Ok( result ) } } +// impl syn::parse::Parse for AttributeSubformScalarSetter +// { +// fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > +// { +// let mut name : Option< syn::Ident > = None; +// let mut setter : Option< bool > = None; +// let mut hint = false; +// +// while !input.is_empty() +// { +// let lookahead = input.lookahead1(); +// if lookahead.peek( syn::Ident ) +// { +// let ident : syn::Ident = input.parse()?; +// match ident.to_string().as_str() +// { +// "name" => +// { +// input.parse::< syn::Token![ = ] >()?; +// name = Some( input.parse()? ); +// } +// "setter" => +// { +// input.parse::< syn::Token![ = ] >()?; +// let value : syn::LitBool = input.parse()?; +// setter = Some( value.value() ); +// } +// "hint" => +// { +// input.parse::< syn::Token![ = ] >()?; +// let value : syn::LitBool = input.parse()?; +// hint = value.value; +// } +// _ => +// { +// return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'name', 'setter', or 'definition'. For example: `subform_scalar( name = myName, setter = true )`", ident ) ) ); +// } +// } +// } +// else +// { +// return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `subform_scalar( name = myName, setter = true )`" ) ); +// } +// +// // Optional comma handling +// if input.peek( syn::Token![ , ] ) +// { +// input.parse::< syn::Token![ , ] >()?; +// } +// } +// +// Ok( Self { name : name.into(), setter : setter.into(), hint : hint.into() } ) +// } +// } + /// Represents an attribute for configuring collection setter generation. /// /// This struct is part of a meta-programming approach to enable detailed configuration of nested structs or collections such as `Vec< E >, HashMap< K, E >` and so on. @@ -378,25 +606,37 @@ impl syn::parse::Parse for AttributeSubformScalarSetter /// ``` /// +#[ derive( Debug, Default ) ] pub struct AttributeSubformCollectionSetter { /// Optional identifier for naming the setter. - pub name : Option< syn::Ident >, + pub name : AttributePropertyName, /// Controls the generation of a setter method. If false, a setter method is not generated. - pub setter : Option< bool >, - /// Definition of the collection former to use, e.g., `former::VectorFormer`. - pub definition : Option< syn::Type >, + pub setter : AttributePropertySetter, /// Specifies whether to provide a sketch of the subform setter as a hint. /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub hint : bool, + pub hint : AttributePropertyHint, + /// Definition of the collection former to use, e.g., `former::VectorFormer`. + pub definition : AttributePropertyDefinition, } impl AttributeSubformCollectionSetter { - const KEYWORD: &'static str = "subform_collection"; + /// Should setter be generated or not? + pub fn setter( &self ) -> bool + { + self.setter.is_none() || self.setter.unwrap() + } + +} + +impl AttributeComponent for AttributeSubformCollectionSetter +{ + + const KEYWORD : &'static str = "subform_collection"; - pub fn from_meta( attr : &syn::Attribute ) -> Result< Self > + fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta { @@ -412,22 +652,90 @@ impl AttributeSubformCollectionSetter } } - /// Should setter be generated or not? - pub fn setter( &self ) -> bool +} + +impl< IntoT > ComponentAssign< AttributeSubformCollectionSetter, IntoT > for FieldAttributes +where + IntoT : Into< AttributeSubformCollectionSetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) { - self.setter.is_none() || self.setter.unwrap() + self.subform_collection = Some( component.into() ); + } +} + +impl< IntoT > ComponentAssign< AttributePropertyName, IntoT > for AttributeSubformCollectionSetter +where + IntoT : Into< AttributePropertyName >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } +} + +impl< IntoT > ComponentAssign< AttributePropertySetter, IntoT > for AttributeSubformCollectionSetter +where + IntoT : Into< AttributePropertySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.setter = component.into(); + } +} + +impl< IntoT > ComponentAssign< AttributePropertyDefinition, IntoT > for AttributeSubformCollectionSetter +where + IntoT : Into< AttributePropertyDefinition >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.definition = component.into(); } +} +impl< IntoT > ComponentAssign< AttributePropertyHint, IntoT > for AttributeSubformCollectionSetter +where + IntoT : Into< AttributePropertyHint >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.hint = component.into(); + } } impl syn::parse::Parse for AttributeSubformCollectionSetter { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut name : Option< syn::Ident > = None; - let mut setter : Option< bool > = None; // Default is to generate a setter - let mut hint = false; - let mut definition : Option< syn::Type > = None; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeSubformCollectionSetter::KEYWORD, " are : ", + AttributePropertyName::KEYWORD, + ", ", AttributePropertySetter::KEYWORD, + ", ", AttributePropertyHint::KEYWORD, + ", ", AttributePropertyDefinition::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ subform_collection( name = myName, setter = true, hint = false, definition = MyDefinition ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; while !input.is_empty() { @@ -435,39 +743,20 @@ impl syn::parse::Parse for AttributeSubformCollectionSetter if lookahead.peek( syn::Ident ) { let ident : syn::Ident = input.parse()?; + + input.parse::< syn::Token![=] >()?; match ident.to_string().as_str() { - "name" => - { - input.parse::< syn::Token![ = ] >()?; - name = Some( input.parse()? ); - } - "setter" => - { - input.parse::< syn::Token![ = ] >()?; - let value : syn::LitBool = input.parse()?; - setter = Some( value.value ); - } - "hint" => - { - input.parse::< syn::Token![ = ] >()?; - let value : syn::LitBool = input.parse()?; - hint = value.value; - } - "definition" => - { - input.parse::< syn::Token![ = ] >()?; - definition = Some( input.parse()? ); - } - _ => - { - return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'name', 'setter', or 'definition'. For example: `collection( name = myName, setter = true, definition = MyDefinition )`", ident ) ) ); - } + AttributePropertyName::KEYWORD => result.assign( AttributePropertyName::parse( input )? ), + AttributePropertySetter::KEYWORD => result.assign( AttributePropertySetter::parse( input )? ), + AttributePropertyHint::KEYWORD => result.assign( AttributePropertyHint::parse( input )? ), + AttributePropertyDefinition::KEYWORD => result.assign( AttributePropertyDefinition::parse( input )? ), + _ => return Err( error( &ident ) ), } } else { - return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `collection( name = myName, setter = true, definition = MyDefinition )`" ) ); + return Err( lookahead.error() ); } // Optional comma handling @@ -477,7 +766,7 @@ impl syn::parse::Parse for AttributeSubformCollectionSetter } } - Ok( Self { name, setter, hint, definition } ) + Ok( result ) } } @@ -499,25 +788,37 @@ impl syn::parse::Parse for AttributeSubformCollectionSetter /// mame = field_name /// ``` +#[ derive( Debug, Default ) ] pub struct AttributeSubformEntrySetter { /// An optional identifier that names the setter. It is parsed from inputs /// like `name = my_field`. - pub name : Option< syn::Ident >, + pub name : AttributePropertyName, /// Disable generation of setter. /// It still generate `_field_subform_entry` method, so it could be used to make a setter with custom arguments. - pub setter : Option< bool >, + pub setter : AttributePropertySetter, /// Specifies whether to provide a sketch of the subform setter as a hint. /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub hint : bool, + pub hint : AttributePropertyHint, } impl AttributeSubformEntrySetter { - const KEYWORD: &'static str = "subform_entry"; + /// Should setter be generated or not? + pub fn setter( &self ) -> bool + { + self.setter.as_ref().is_none() || self.setter.as_ref().unwrap() + } + +} + +impl AttributeComponent for AttributeSubformEntrySetter +{ + + const KEYWORD : &'static str = "subform_entry"; - pub fn from_meta( attr : &syn::Attribute ) -> Result< Self > + fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta { @@ -533,66 +834,188 @@ impl AttributeSubformEntrySetter } } - /// Should setter be generated or not? - pub fn setter( &self ) -> bool +} + +impl< IntoT > ComponentAssign< AttributeSubformEntrySetter, IntoT > for FieldAttributes +where + IntoT : Into< AttributeSubformEntrySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) { - self.setter.is_none() || self.setter.unwrap() + self.subform_entry = Some( component.into() ); + } +} + +impl< IntoT > ComponentAssign< AttributePropertyName, IntoT > for AttributeSubformEntrySetter +where + IntoT : Into< AttributePropertyName >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } +} + +impl< IntoT > ComponentAssign< AttributePropertySetter, IntoT > for AttributeSubformEntrySetter +where + IntoT : Into< AttributePropertySetter >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.setter = component.into(); } +} +impl< IntoT > ComponentAssign< AttributePropertyHint, IntoT > for AttributeSubformEntrySetter +where + IntoT : Into< AttributePropertyHint >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.hint = component.into(); + } } impl syn::parse::Parse for AttributeSubformEntrySetter { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut name : Option< syn::Ident > = None; - let mut setter : Option< bool > = None; - let mut hint = false; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeSubformEntrySetter::KEYWORD, " are : ", + AttributePropertyName::KEYWORD, + ", ", AttributePropertySetter::KEYWORD, + ", ", AttributePropertyHint::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ subform( name = myName, setter = true ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; - // xxx : qqq for Anton : use match here and for all attributes -- done while !input.is_empty() { let lookahead = input.lookahead1(); if lookahead.peek( syn::Ident ) { let ident : syn::Ident = input.parse()?; + + input.parse::< syn::Token![=] >()?; match ident.to_string().as_str() { - "name" => - { - input.parse::< syn::Token![ = ] >()?; - name = Some( input.parse()? ); - } - "setter" => - { - input.parse::< syn::Token![ = ] >()?; - let value : syn::LitBool = input.parse()?; - setter = Some( value.value() ); - } - "hint" => - { - input.parse::< syn::Token![ = ] >()?; - let value : syn::LitBool = input.parse()?; - hint = value.value; - } - _ => - { - return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'name', 'setter', or 'definition'. For example: `subform( name = myName, setter = true )`", ident ) ) ); - } + AttributePropertyName::KEYWORD => result.assign( AttributePropertyName::parse( input )? ), + AttributePropertySetter::KEYWORD => result.assign( AttributePropertySetter::parse( input )? ), + AttributePropertyHint::KEYWORD => result.assign( AttributePropertyHint::parse( input )? ), + _ => return Err( error( &ident ) ), } } else { - return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `subform( name = myName, setter = true )`" ) ); + return Err( lookahead.error() ); } // Optional comma handling - if input.peek( syn::Token![,] ) + if input.peek( syn::Token![ , ] ) { - input.parse::< syn::Token![,] >()?; + input.parse::< syn::Token![ , ] >()?; } } - Ok( Self { name, setter, hint } ) + 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 AttributePropertyHintMarker; + +/// Specifies whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +impl AttributePropertyComponent for AttributePropertyHintMarker +{ + const KEYWORD : &'static str = "hint"; +} + +/// Specifies whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +pub type AttributePropertyHint = AttributePropertyBoolean< AttributePropertyHintMarker >; + +// = + +/// Disable generation of setter. +/// Attributes still might generate some helper methods to reuse by custom setter. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertySetterMarker; + +impl AttributePropertyComponent for AttributePropertySetterMarker +{ + const KEYWORD : &'static str = "setter"; +} + +/// Disable generation of setter. +/// Attributes still might generate some helper methods to reuse by custom setter. +pub type AttributePropertySetter = AttributePropertyOptionalBoolean< AttributePropertySetterMarker >; + +// = + +/// Marker type for attribute property of optional identifier that names the setter. It is parsed from inputs +/// like `name = my_field`. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyNameMarker; + +impl AttributePropertyComponent for AttributePropertyNameMarker +{ + const KEYWORD : &'static str = "name"; +} + +/// An optional identifier that names the setter. It is parsed from inputs +/// like `name = my_field`. +pub type AttributePropertyName = AttributePropertyOptionalSyn< syn::Ident, AttributePropertyNameMarker >; + +// = + +/// Marker type for default value to use for a field. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyDefaultMarker; + +impl AttributePropertyComponent for AttributePropertyDefaultMarker +{ + const KEYWORD : &'static str = "default"; +} + +/// An optional identifier that names the setter. It is parsed from inputs +/// like `name = my_field`. +pub type AttributePropertyDefault = AttributePropertyOptionalSyn< syn::Expr, AttributePropertyDefaultMarker >; + +// = + +/// Marker type for definition of the collection former to use, e.g., `former::VectorFormer`. +#[ derive( Debug, Default, Clone, Copy ) ] +pub struct AttributePropertyDefinitionMarker; + +impl AttributePropertyComponent for AttributePropertyDefinitionMarker +{ + const KEYWORD : &'static str = "definition"; +} + +/// Definition of the collection former to use, e.g., `former::VectorFormer`. +pub type AttributePropertyDefinition = AttributePropertyOptionalSyn< syn::Type, AttributePropertyDefinitionMarker >; diff --git a/module/core/former_meta/src/derive_former/struct_attrs.rs b/module/core/former_meta/src/derive_former/struct_attrs.rs index f40dd61d61..73e11f9cb0 100644 --- a/module/core/former_meta/src/derive_former/struct_attrs.rs +++ b/module/core/former_meta/src/derive_former/struct_attrs.rs @@ -1,14 +1,33 @@ +//! Attributes of the whole item. use super::*; -use macro_tools::{ attr, Result }; -/// -/// Attributes of a struct. -/// +use macro_tools:: +{ + attr, + Result, + AttributeComponent, + AttributePropertyComponent, + AttributePropertyBoolean, +}; + +use former_types::{ ComponentAssign }; + +/// Represents the attributes of a struct, including storage fields, mutator, and perform attributes. + +#[ derive( Debug, Default ) ] pub struct StructAttributes { + /// Optional attribute for storage-specific fields. + /// This field is used to specify fields that should be part of the storage but not the final formed structure. pub storage_fields : Option< AttributeStorageFields >, + + /// Attribute for customizing the mutation process in a forming operation. + /// The `mutator` attribute allows for specifying whether a custom mutator should be used or if a sketch should be provided as a hint. pub mutator : AttributeMutator, + + /// Optional attribute for specifying a method to call after forming. + /// This attribute can hold information about a method that should be invoked after the form operation is complete. pub perform : Option< AttributePerform >, } @@ -17,14 +36,31 @@ impl StructAttributes pub fn from_attrs< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> Result< Self > { - let mut storage_fields = None; - let mut mutator : AttributeMutator = Default::default(); - let mut perform = None; + let mut result = Self::default(); + + let error = | attr : &syn::Attribute | -> syn::Error + { + let known_attributes = const_format::concatcp! + ( + "Known attirbutes are : ", + "debug", + ", ", AttributeStorageFields::KEYWORD, + ", ", AttributeMutator::KEYWORD, + ", ", AttributePerform::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( || syn_err!( attr, "Expects an attribute of format #[ attribute( val ) ], but got:\n {}", qt!{ #attr } ) )?; + + let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; let key_str = format!( "{}", key_ident ); if attr::is_standard( &key_str ) @@ -32,34 +68,63 @@ impl StructAttributes continue; } - // qqq : qqq for Anton : xxx : refactor field_attrs::FieldAttributes::from_attrs to make it similar to this function -- done match key_str.as_ref() { - AttributeStorageFields::KEYWORD => - { - storage_fields.replace( AttributeStorageFields::from_meta( attr )? ); - } - AttributeMutator::KEYWORD => - { - mutator = AttributeMutator::from_meta( attr )?; - } - AttributePerform::KEYWORD => - { - perform.replace( AttributePerform::from_meta( attr )? ); - } - "debug" => - { - } - _ => - { - return Err( syn_err!( attr, "Known structure attirbutes are : `storage_fields`, `perform`, `debug`.\nUnknown structure attribute : {}", qt!{ #attr } ) ); - } + AttributeStorageFields::KEYWORD => result.assign( AttributeStorageFields::from_meta( attr )? ), + AttributeMutator::KEYWORD => result.assign( AttributeMutator::from_meta( attr )? ), + AttributePerform::KEYWORD => result.assign( AttributePerform::from_meta( attr )? ), + "debug" => {} + _ => return Err( error( attr ) ), } } - Ok( StructAttributes { perform, storage_fields, mutator } ) + Ok( result ) } +// pub fn from_attrs< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> Result< Self > +// { +// let mut storage_fields = None; +// let mut mutator : AttributeMutator = Default::default(); +// let mut perform = None; +// +// for attr in attrs +// { +// let key_ident = attr.path().get_ident() +// .ok_or_else( || syn_err!( attr, "Expects an attribute of format #[ attribute( key1 = val1, key2 = val2 ) ], but got:\n {}", qt!{ #attr } ) )?; +// let key_str = format!( "{}", key_ident ); +// +// if attr::is_standard( &key_str ) +// { +// continue; +// } +// +// match key_str.as_ref() +// { +// AttributeStorageFields::KEYWORD => +// { +// storage_fields.replace( AttributeStorageFields::from_meta( attr )? ); +// } +// AttributeMutator::KEYWORD => +// { +// mutator = AttributeMutator::from_meta( attr )?; +// } +// AttributePerform::KEYWORD => +// { +// perform.replace( AttributePerform::from_meta( attr )? ); +// } +// "debug" => +// { +// } +// _ => +// { +// return Err( syn_err!( attr, "Known structure attirbutes are : `storage_fields`, `mutator`, `perform`, `debug`.\nUnknown structure attribute : {}", qt!{ #attr } ) ); +// } +// } +// } +// +// Ok( StructAttributes { perform, storage_fields, mutator } ) +// } + /// /// Generate parts, used for generating `perform()`` method. /// @@ -147,17 +212,18 @@ impl StructAttributes /// `#[ storage_fields( a : i32, b : Option< String > ) ]` /// +#[ derive( Debug, Default ) ] pub struct AttributeStorageFields { pub fields : syn::punctuated::Punctuated< syn::Field, syn::token::Comma >, } -impl AttributeStorageFields +impl AttributeComponent for AttributeStorageFields { const KEYWORD : &'static str = "storage_fields"; - pub fn from_meta( attr : &syn::Attribute ) -> Result< Self > + fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta { @@ -172,13 +238,24 @@ impl AttributeStorageFields } +impl< IntoT > ComponentAssign< AttributeStorageFields, IntoT > for StructAttributes +where + IntoT : Into< AttributeStorageFields >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.storage_fields = Some( component.into() ); + } +} + impl syn::parse::Parse for AttributeStorageFields { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let fields : syn::punctuated::Punctuated< syn::Field, syn::Token![,] > = - input.parse_terminated( syn::Field::parse_named, Token![,] )?; + let fields : syn::punctuated::Punctuated< syn::Field, syn::Token![ , ] > = + input.parse_terminated( syn::Field::parse_named, Token![ , ] )?; Ok( Self { @@ -203,17 +280,17 @@ pub struct AttributeMutator { /// Indicates whether a custom mutator should be generated. /// Defaults to `false`, meaning no custom mutator is generated unless explicitly requested. - pub custom : bool, + pub custom : AttributePropertyCustom, /// Specifies whether to provide a sketch of the mutator as a hint. /// Defaults to `false`, which means no hint is provided unless explicitly requested. - pub hint : bool, + pub hint : AttributePropertyHint, } -impl AttributeMutator +impl AttributeComponent for AttributeMutator { const KEYWORD : &'static str = "mutator"; - pub fn from_meta( attr : &syn::Attribute ) -> Result< Self > + fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta { @@ -231,38 +308,78 @@ impl AttributeMutator } +impl< IntoT > ComponentAssign< AttributeMutator, IntoT > for StructAttributes +where + IntoT : Into< AttributeMutator >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.mutator = component.into(); + } +} + +impl< IntoT > ComponentAssign< AttributePropertyHint, IntoT > for AttributeMutator +where + IntoT : Into< AttributePropertyHint >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.hint = component.into(); + } +} + +impl< IntoT > ComponentAssign< AttributePropertyCustom, IntoT > for AttributeMutator +where + IntoT : Into< AttributePropertyCustom >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.custom = component.into(); + } +} impl syn::parse::Parse for AttributeMutator { fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let mut custom = false; - let mut hint = false; + let mut result = Self::default(); + + let error = | ident : &syn::Ident | -> syn::Error + { + let known = const_format::concatcp! + ( + "Known entries of attribute ", AttributeMutator::KEYWORD, " are : ", + AttributePropertyCustom::KEYWORD, + ", ", AttributePropertyHint::KEYWORD, + ".", + ); + syn_err! + ( + ident, + r#"Expects an attribute of format '#[ mutator( custom = false, hint = false ) ]' + {known} + But got: '{}' +"#, + qt!{ #ident } + ) + }; - // xxx : qqq for Anton : use match here and for all attributes -- done while !input.is_empty() { let lookahead = input.lookahead1(); if lookahead.peek( syn::Ident ) { let ident : syn::Ident = input.parse()?; + input.parse::< syn::Token![=] >()?; match ident.to_string().as_str() { - "custom" => - { - let value : syn::LitBool = input.parse()?; - custom = value.value; - } - "hint" => - { - let value : syn::LitBool = input.parse()?; - hint = value.value; - } - _ => - { - return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'custom' or 'hint'.", ident ) ) ); - } + AttributePropertyCustom::KEYWORD => result.assign( AttributePropertyCustom::parse( input )? ), + AttributePropertyHint::KEYWORD => result.assign( AttributePropertyHint::parse( input )? ), + _ => return Err( error( &ident ) ), } } else @@ -271,17 +388,13 @@ impl syn::parse::Parse for AttributeMutator } // Optional comma handling - if input.peek( syn::Token![,] ) + if input.peek( syn::Token![ , ] ) { - input.parse::< syn::Token![,] >()?; + input.parse::< syn::Token![ , ] >()?; } } - Ok( Self - { - custom, - hint, - }) + Ok( result ) } } @@ -291,16 +404,17 @@ impl syn::parse::Parse for AttributeMutator /// `#[ perform( fn after1< 'a >() -> Option< &'a str > ) ]` /// +#[ derive( Debug ) ] pub struct AttributePerform { pub signature : syn::Signature, } -impl AttributePerform +impl AttributeComponent for AttributePerform { const KEYWORD : &'static str = "perform"; - pub fn from_meta( attr : &syn::Attribute ) -> Result< Self > + fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta @@ -320,12 +434,52 @@ impl syn::parse::Parse for AttributePerform { fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > { - // let input2; Ok( Self { - // paren_token : syn::parenthesized!( input2 in input ), - // signature : input2.parse()?, signature : input.parse()?, }) } } + +impl< IntoT > ComponentAssign< AttributePerform, IntoT > for StructAttributes +where + IntoT : Into< AttributePerform >, +{ + #[ inline( always ) ] + fn assign( &mut self, component : IntoT ) + { + self.perform = Some( 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 AttributePropertyHintMarker; + +impl AttributePropertyComponent for AttributePropertyHintMarker +{ + const KEYWORD : &'static str = "hint"; +} + +/// Specifies whether to provide a sketch as a hint. +/// Defaults to `false`, which means no hint is provided unless explicitly requested. +pub type AttributePropertyHint = AttributePropertyBoolean< AttributePropertyHintMarker >; + +// = + +/// Marker type for attribute property to indicates 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 AttributePropertyCustomMarker; + +impl AttributePropertyComponent for AttributePropertyCustomMarker +{ + const KEYWORD : &'static str = "custom"; +} + +/// Indicates whether a custom code should be generated. +/// Defaults to `false`, meaning no custom code is generated unless explicitly requested. +pub type AttributePropertyCustom = AttributePropertyBoolean< AttributePropertyCustomMarker >; diff --git a/module/core/former_meta/src/lib.rs b/module/core/former_meta/src/lib.rs index f8fbcaef86..e9d2e50279 100644 --- a/module/core/former_meta/src/lib.rs +++ b/module/core/former_meta/src/lib.rs @@ -3,8 +3,14 @@ #![ doc( html_root_url = "https://docs.rs/former_derive_meta/latest/former_derive_meta/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] +#[ allow( unused_imports ) ] +use macro_tools::prelude::*; + +#[ cfg( feature = "derive_former" ) ] +mod derive_former; + #[ cfg( feature = "enabled" ) ] -// #[ cfg( feature = "derive_component_from" ) ] +#[ cfg( feature = "derive_components" ) ] mod component { @@ -26,11 +32,6 @@ mod component } -#[ allow( unused_imports ) ] -use macro_tools::prelude::*; -#[ cfg( feature = "derive_former" ) ] -mod derive_former; - /// Derive macro for generating a `Former` struct, applying a Builder Pattern to the annotated struct. /// /// This macro simplifies the construction of complex objects by automatically generating a builder (former) for @@ -613,4 +614,3 @@ pub fn from_components( input : proc_macro::TokenStream ) -> proc_macro::TokenSt Err( err ) => err.to_compile_error().into(), } } - diff --git a/module/core/former_types/Cargo.toml b/module/core/former_types/Cargo.toml new file mode 100644 index 0000000000..95ca657ed3 --- /dev/null +++ b/module/core/former_types/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "former_types" +version = "2.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/former" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/former" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/former" +description = """ +A flexible and extensible implementation of the builder pattern. Its compile-time structures and traits that are not generated but reused. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose", "builder-pattern" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] + +no_std = [ "collection_tools/no_std" ] +use_alloc = [ "no_std", "collection_tools/use_alloc" ] + +default = [ + "enabled", + "derive_former", + "types_components", + "types_component_assign", +] +full = [ + "enabled", + "derive_former", + "types_components", + "types_component_assign", +] +enabled = [ "collection_tools/enabled" ] + +derive_former = [] +types_components = [] +types_component_assign = [ "types_components" ] + + +[dependencies] +collection_tools = { workspace = true, features = [ "collection_constructors" ] } +# qqq : optimize also make sure collection_tools expose enough features + + +[dev-dependencies] +test_tools = { workspace = true, features = [ "full" ] } diff --git a/module/core/former_types/License b/module/core/former_types/License new file mode 100644 index 0000000000..e3e9e057cf --- /dev/null +++ b/module/core/former_types/License @@ -0,0 +1,23 @@ +Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/core/former_types/Readme.md b/module/core/former_types/Readme.md new file mode 100644 index 0000000000..105cb237a2 --- /dev/null +++ b/module/core/former_types/Readme.md @@ -0,0 +1,70 @@ + + +# Module :: former_types + + + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_former_types_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_former_types_push.yml) [![docs.rs](https://img.shields.io/docsrs/former_types?color=e3e8f0&logo=docs.rs)](https://docs.rs/former_types) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fformer_types%2Fexamples%2Fformer_types_trivial.rs,RUN_POSTFIX=--example%20former_types_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + + +A flexible and extensible implementation of the builder pattern. Its compile-time structures and traits that are not generated but reused. + +## Example: Using Trait ComponentAssign + +Demonstrates setting various components (fields) of a struct. + +The `former_types` crate provides a generic interface for setting components on an object. This example defines a `Person` struct +and implements the `ComponentAssign` trait for its fields. It shows how to use these implementations to set the fields of a `Person` +instance using different types that can be converted into the required types. + +```rust +#[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +fn main() {} + +#[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +fn main() +{ + use former_types::ComponentAssign; + + #[ derive( Default, PartialEq, Debug ) ] + struct Person + { + age : i32, + name : String, + } + + impl< IntoT > ComponentAssign< i32, IntoT > for Person + where + IntoT : Into< i32 >, + { + fn assign( &mut self, component : IntoT ) + { + self.age = component.into(); + } + } + + impl< IntoT > ComponentAssign< String, IntoT > for Person + where + IntoT : Into< String >, + { + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } + } + + let mut got : Person = Default::default(); + got.assign( 13 ); + got.assign( "John" ); + assert_eq!( got, Person { age : 13, name : "John".to_string() } ); + dbg!( got ); + // > Person { + // > age: 13, + // > name: "John", + // > } + +} +``` + +Try out `cargo run --example former_types_trivial`. +
+[See code](./examples/former_types_trivial.rs). diff --git a/module/core/former_types/examples/former_types_trivial.rs b/module/core/former_types/examples/former_types_trivial.rs new file mode 100644 index 0000000000..70d226686d --- /dev/null +++ b/module/core/former_types/examples/former_types_trivial.rs @@ -0,0 +1,68 @@ +//! +//! ## Example: Using Trait ComponentAssign +//! +//! Demonstrates setting various components (fields) of a struct. +//! +//! The `former_types` crate provides a generic interface for setting components on an object. This example defines a `Person` struct +//! and implements the `ComponentAssign` trait for its fields. It shows how to use these implementations to set the fields of a `Person` +//! instance using different types that can be converted into the required types. +//! +//! ## Explanation +//! +//! - **Person Struct**: The `Person` struct has two fields: `age` (an integer) and `name` (a string). The `Default` and `PartialEq` traits are derived to facilitate default construction and comparison. +//! +//! - **ComponentAssign Implementations**: The `ComponentAssign` trait is implemented for the `age` and `name` fields of the `Person` struct. +//! - For `age`: The trait is implemented for any type that can be converted into an `i32`. +//! - For `name`: The trait is implemented for any type that can be converted into a `String`. +//! +//! - **Usage**: An instance of `Person` is created using the default constructor, and then the `assign` method is used to set the `age` and `name` fields. +//! - `got.assign( 13 )`: Assigns the integer `13` to the `age` field. +//! - `got.assign( "John" )`: Assigns the string `"John"` to the `name` field. +//! + +#[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +fn main() {} + +#[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +fn main() +{ + use former_types::ComponentAssign; + + #[ derive( Default, PartialEq, Debug ) ] + struct Person + { + age : i32, + name : String, + } + + impl< IntoT > ComponentAssign< i32, IntoT > for Person + where + IntoT : Into< i32 >, + { + fn assign( &mut self, component : IntoT ) + { + self.age = component.into(); + } + } + + impl< IntoT > ComponentAssign< String, IntoT > for Person + where + IntoT : Into< String >, + { + fn assign( &mut self, component : IntoT ) + { + self.name = component.into(); + } + } + + let mut got : Person = Default::default(); + got.assign( 13 ); + got.assign( "John" ); + assert_eq!( got, Person { age : 13, name : "John".to_string() } ); + dbg!( got ); + // > Person { + // > age: 13, + // > name: "John", + // > } + +} diff --git a/module/core/former/src/axiomatic.rs b/module/core/former_types/src/axiomatic.rs similarity index 100% rename from module/core/former/src/axiomatic.rs rename to module/core/former_types/src/axiomatic.rs diff --git a/module/core/former/src/collection.rs b/module/core/former_types/src/collection.rs similarity index 96% rename from module/core/former/src/collection.rs rename to module/core/former_types/src/collection.rs index a17521e277..eac724c018 100644 --- a/module/core/former/src/collection.rs +++ b/module/core/former_types/src/collection.rs @@ -66,7 +66,7 @@ pub( crate ) mod private /// /// # Example /// ``` - /// use former::CollectionValToEntry; + /// use former_types::CollectionValToEntry; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly /// /// struct PairMap; /// @@ -104,7 +104,7 @@ pub( crate ) mod private /// /// # Example /// ``` - /// use former::ValToEntry; + /// use former_types::ValToEntry; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly /// /// struct PairMap; /// @@ -184,7 +184,7 @@ pub( crate ) mod private /// /// ```rust /// - /// use former::{ Collection, CollectionAdd }; + /// use former_types::{ Collection, CollectionAdd }; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly /// /// struct MyCollection /// { @@ -255,7 +255,7 @@ pub( crate ) mod private /// # Examples /// /// ```rust - /// use former::{ Collection, CollectionAssign }; + /// use former_types::{ Collection, CollectionAssign }; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly /// /// struct MyCollection /// { diff --git a/module/core/former/src/collection/binary_heap.rs b/module/core/former_types/src/collection/binary_heap.rs similarity index 100% rename from module/core/former/src/collection/binary_heap.rs rename to module/core/former_types/src/collection/binary_heap.rs diff --git a/module/core/former/src/collection/btree_map.rs b/module/core/former_types/src/collection/btree_map.rs similarity index 100% rename from module/core/former/src/collection/btree_map.rs rename to module/core/former_types/src/collection/btree_map.rs diff --git a/module/core/former/src/collection/btree_set.rs b/module/core/former_types/src/collection/btree_set.rs similarity index 100% rename from module/core/former/src/collection/btree_set.rs rename to module/core/former_types/src/collection/btree_set.rs diff --git a/module/core/former/src/collection/hash_map.rs b/module/core/former_types/src/collection/hash_map.rs similarity index 100% rename from module/core/former/src/collection/hash_map.rs rename to module/core/former_types/src/collection/hash_map.rs diff --git a/module/core/former/src/collection/hash_set.rs b/module/core/former_types/src/collection/hash_set.rs similarity index 100% rename from module/core/former/src/collection/hash_set.rs rename to module/core/former_types/src/collection/hash_set.rs diff --git a/module/core/former/src/collection/linked_list.rs b/module/core/former_types/src/collection/linked_list.rs similarity index 100% rename from module/core/former/src/collection/linked_list.rs rename to module/core/former_types/src/collection/linked_list.rs diff --git a/module/core/former/src/collection/vector.rs b/module/core/former_types/src/collection/vector.rs similarity index 100% rename from module/core/former/src/collection/vector.rs rename to module/core/former_types/src/collection/vector.rs diff --git a/module/core/former/src/collection/vector_deque.rs b/module/core/former_types/src/collection/vector_deque.rs similarity index 100% rename from module/core/former/src/collection/vector_deque.rs rename to module/core/former_types/src/collection/vector_deque.rs diff --git a/module/core/former/src/component.rs b/module/core/former_types/src/component.rs similarity index 89% rename from module/core/former/src/component.rs rename to module/core/former_types/src/component.rs index 67ea2fdabb..68a5479fce 100644 --- a/module/core/former/src/component.rs +++ b/module/core/former_types/src/component.rs @@ -1,3 +1,4 @@ + /// Provides a generic interface for setting a component of a certain type on an object. /// /// This trait abstracts the action of setting or replacing a component, where a component @@ -19,7 +20,7 @@ /// Implementing `ComponentAssign` to set a name string on a struct : /// /// ```rust -/// use former::ComponentAssign; +/// use former_types::ComponentAssign; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly /// /// struct MyStruct /// { @@ -38,7 +39,7 @@ /// obj.assign( "New Name" ); /// assert_eq!( obj.name, "New Name" ); /// ``` -#[ cfg( any( feature = "derive_component_assign", feature = "derive_components_assign" ) ) ] +#[ cfg( any( feature = "types_component_assign" ) ) ] pub trait ComponentAssign< T, IntoT > where IntoT : Into< T >, @@ -66,7 +67,7 @@ where /// ### Example /// /// ```rust -/// use former::{ ComponentAssign, AssignWithType }; +/// use former_types::{ ComponentAssign, AssignWithType }; // use crate `former` instead of crate `former_types` unless you need to use crate `former_types` directly /// /// struct UserProfile /// { @@ -89,7 +90,7 @@ where /// ``` /// -#[ cfg( any( feature = "derive_component_assign", feature = "derive_components_assign" ) ) ] +#[ cfg( any( feature = "types_component_assign" ) ) ] pub trait AssignWithType { /// Function to set value of a component by its type. @@ -99,7 +100,7 @@ pub trait AssignWithType Self : ComponentAssign< T, IntoT >; } -#[ cfg( any( feature = "derive_component_assign", feature = "derive_components_assign" ) ) ] +#[ cfg( any( feature = "types_component_assign" ) ) ] impl< S > AssignWithType for S { diff --git a/module/core/former/src/definition.rs b/module/core/former_types/src/definition.rs similarity index 100% rename from module/core/former/src/definition.rs rename to module/core/former_types/src/definition.rs diff --git a/module/core/former/src/forming.rs b/module/core/former_types/src/forming.rs similarity index 100% rename from module/core/former/src/forming.rs rename to module/core/former_types/src/forming.rs diff --git a/module/core/former_types/src/lib.rs b/module/core/former_types/src/lib.rs new file mode 100644 index 0000000000..90b2af5410 --- /dev/null +++ b/module/core/former_types/src/lib.rs @@ -0,0 +1,101 @@ +#![ cfg_attr( feature = "no_std", no_std ) ] +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc( html_root_url = "https://docs.rs/former_types/latest/former_types/" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Axiomatic things. +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "derive_former" ) ] +mod axiomatic; +/// Forming process. +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "derive_former" ) ] +mod definition; +/// Forming process. +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "derive_former" ) ] +mod forming; +/// Storage. +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "derive_former" ) ] +mod storage; + +/// Interface for collections. +#[ cfg( feature = "enabled" ) ] +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +#[ cfg( feature = "derive_former" ) ] +mod collection; + +/// Component-based forming. +#[ cfg( feature = "enabled" ) ] +#[ cfg( any( feature = "types_component_assign" ) ) ] +mod component; + +/// Namespace with dependencies. +#[ cfg( feature = "enabled" ) ] +pub mod dependency +{ + pub use ::collection_tools; +} + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +#[ cfg( feature = "enabled" ) ] +pub use protected::*; + +/// Protected namespace of the module. +#[ cfg( feature = "enabled" ) ] +pub mod protected +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::orphan::*; +} + +/// Parented namespace of the module. +#[ cfg( feature = "enabled" ) ] +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; +} + +/// Exposed namespace of the module. +#[ cfg( feature = "enabled" ) ] +pub mod exposed +{ + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::prelude::*; + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #[ cfg( feature = "derive_former" ) ] + pub use super:: + { + axiomatic::*, + definition::*, + forming::*, + storage::*, + }; + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + #[ cfg( feature = "derive_former" ) ] + pub use super::collection::*; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] +pub mod prelude +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #[ cfg( any( feature = "types_component_assign" ) ) ] + pub use super::component::*; +} diff --git a/module/core/former/src/storage.rs b/module/core/former_types/src/storage.rs similarity index 100% rename from module/core/former/src/storage.rs rename to module/core/former_types/src/storage.rs diff --git a/module/core/former_types/tests/inc/mod.rs b/module/core/former_types/tests/inc/mod.rs new file mode 100644 index 0000000000..59fea4b027 --- /dev/null +++ b/module/core/former_types/tests/inc/mod.rs @@ -0,0 +1,51 @@ +// #![ deny( missing_docs ) ] + +#[ allow( unused_imports ) ] +use super::*; + +#[ cfg( feature = "derive_former" ) ] +#[ path = "../../../former/tests/inc/former_tests" ] +mod former_tests +{ + #[ allow( unused_imports ) ] + use super::*; + + // = basic + + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + mod a_basic_manual; + mod a_primitives_manual; + + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + mod subform_collection_basic_manual; + + // = parametrization + + #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] + mod parametrized_struct_manual; + mod parametrized_slice_manual; + +} + +#[ cfg( feature = "types_components" ) ] +#[ path = "../../../former/tests/inc/components_tests" ] +mod components_tests +{ + use super::*; + + #[ cfg( feature = "types_component_from" ) ] + mod component_from_manual; + + #[ cfg( feature = "types_component_assign" ) ] + mod component_assign_manual; + + #[ cfg( all( feature = "types_component_assign" ) ) ] + mod components_assign_manual; + + // #[ cfg( all( feature = "derive_from_components" ) ) ] + mod from_components_manual; + + #[ cfg( all( feature = "types_component_assign" ) ) ] + mod composite_manual; + +} diff --git a/module/core/former_types/tests/smoke_test.rs b/module/core/former_types/tests/smoke_test.rs new file mode 100644 index 0000000000..7fd288e61d --- /dev/null +++ b/module/core/former_types/tests/smoke_test.rs @@ -0,0 +1,14 @@ + +// #[ cfg( feature = "default" ) ] +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + +// #[ cfg( feature = "default" ) ] +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/core/former_types/tests/tests.rs b/module/core/former_types/tests/tests.rs new file mode 100644 index 0000000000..caea26275c --- /dev/null +++ b/module/core/former_types/tests/tests.rs @@ -0,0 +1,12 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use test_tools::exposed::*; +#[ allow( unused_imports ) ] +use former_types as the_module; +#[ allow( unused_imports ) ] +use former_types as former; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/core/macro_tools/src/attr.rs b/module/core/macro_tools/src/attr.rs index b39c9b703a..a0bb4fbd30 100644 --- a/module/core/macro_tools/src/attr.rs +++ b/module/core/macro_tools/src/attr.rs @@ -7,34 +7,6 @@ pub( crate ) mod private { use crate::*; - /// - /// For attribute like `#[former( default = 31 ) ]` return key `default` and value `31`, - /// as well as syn::Meta as the last element of result tuple. - /// - /// ### Basic use-case. - /// - /// ```rust - /// use macro_tools::exposed::*; - /// let attr : syn::Attribute = syn::parse_quote!( #[ former( default = 31 ) ] ); - /// // tree_print!( attr ); - /// let got = equation( &attr ).unwrap(); - /// assert_eq!( code_to_str!( got ), "default = 31".to_string() ); - /// ``` - - pub fn equation( attr : &syn::Attribute ) -> Result< tokens::Equation > - { - let meta = &attr.meta; - return match meta - { - syn::Meta::List( ref meta_list ) => - { - let eq : tokens::Equation = syn::parse2( meta_list.tokens.clone() )?; - Ok( eq ) - } - _ => return Err( syn::Error::new( attr.span(), "Unknown format of attribute, expected syn::Meta::List( meta_list )" ) ), - }; - } - /// Checks if the given iterator of attributes contains an attribute named `debug`. /// /// This function iterates over an input sequence of `syn::Attribute`, typically associated with a struct, @@ -349,29 +321,69 @@ pub( crate ) mod private } } -// /// -// /// Attribute and ident. -// /// -// -// // qqq : example? -// -// pub type AttributedIdent = Pair< Many< AttributesInner >, syn::Ident >; -// -// impl From< syn::Ident > for AttributedIdent -// { -// fn from( src : syn::Ident ) -> Self -// { -// Self( Vec::< AttributesInner >::new().into(), src ) -// } -// } -// -// impl From< AttributedIdent > for syn::Ident -// { -// fn from( src : AttributedIdent ) -> Self -// { -// src.1 -// } -// } + /// Trait for components of strcuture aggregating attributes that can be constructed from a meta attribute. + /// + /// The `AttributeComponent` trait defines the interface for components that can be created + /// from a `syn::Attribute` meta item. Implementors of this trait are required to define + /// a constant `KEYWORD` that identifies the type of the component and a method `from_meta` + /// that handles the construction of the component from the given attribute. + /// + /// # Example + /// + /// ```rust + /// use macro_tools::{ AttributeComponent, Result }; + /// + /// struct MyComponent; + /// + /// impl AttributeComponent for MyComponent + /// { + /// const KEYWORD : &'static str = "my_component"; + /// + /// fn from_meta( attr : &syn::Attribute ) -> Result< Self > + /// { + /// // Parsing logic here + /// Ok( MyComponent ) + /// } + /// } + /// ``` + /// xxx : improve documentation + pub trait AttributeComponent + where + Self : Sized, + { + /// The keyword that identifies the component. + /// + /// This constant is used to match the attribute to the corresponding component. + /// Each implementor of this trait must provide a unique keyword for its type. + const KEYWORD : &'static str; + + /// Constructs the component from the given meta attribute. + /// + /// This method is responsible for parsing the provided `syn::Attribute` and + /// returning an instance of the component. If the attribute cannot be parsed + /// into the component, an error should be returned. + /// + /// # Parameters + /// + /// - `attr` : A reference to the `syn::Attribute` from which the component is to be constructed. + /// + /// # Returns + /// + /// A `Result` containing the constructed component if successful, or an error if the parsing fails. + fn from_meta( attr : &syn::Attribute ) -> Result< Self >; + } + + /// xxx : write documentation + pub trait AttributePropertyComponent + where + Self : Sized, + { + /// The keyword that identifies the component. + /// + /// This constant is used to match the attribute to the corresponding component. + /// Each implementor of this trait must provide a unique keyword for its type. + const KEYWORD : &'static str; + } } @@ -385,6 +397,14 @@ pub mod protected #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::orphan::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + // equation, + has_debug, + is_standard, + }; } /// Orphan namespace of the module. @@ -406,12 +426,11 @@ pub mod exposed #[ allow( unused_imports ) ] pub use super::private:: { - equation, - has_debug, - is_standard, AttributesInner, AttributesOuter, - // AttributedIdent, + + AttributeComponent, + AttributePropertyComponent, }; } diff --git a/module/core/macro_tools/src/attr_prop.rs b/module/core/macro_tools/src/attr_prop.rs new file mode 100644 index 0000000000..0193a8653a --- /dev/null +++ b/module/core/macro_tools/src/attr_prop.rs @@ -0,0 +1,623 @@ +//! +//! Attribute's properties. Reuse them to define how to parse properties of an attribute. +//! +//! +//! # Example +//! +//! ```rust +//! use macro_tools::AttributePropertyBoolean; +//! +//! #[ derive( Debug, Default, Clone, Copy ) ] +//! pub struct DebugMarker; +//! +//! #[ derive( Debug, Default, Clone, Copy ) ] +//! pub struct EnabledMarker; +//! +//! pub trait AttributePropertyComponent +//! { +//! const KEYWORD : &'static str; +//! } +//! +//! impl AttributePropertyComponent for DebugMarker +//! { +//! const KEYWORD : &'static str = "debug"; +//! } +//! +//! impl AttributePropertyComponent for EnabledMarker +//! { +//! const KEYWORD : &'static str = "enabled"; +//! } +//! +//! #[ derive( Debug, Default ) ] +//! struct MyAttributes +//! { +//! pub debug : AttributePropertyBoolean< DebugMarker >, +//! pub enabled : AttributePropertyBoolean< EnabledMarker >, +//! } +//! +//! impl syn::parse::Parse for MyAttributes +//! { +//! fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > +//! { +//! let mut debug = AttributePropertyBoolean::< DebugMarker >::default(); +//! let mut enabled = AttributePropertyBoolean::< EnabledMarker >::default(); +//! +//! while !input.is_empty() +//! { +//! let lookahead = input.lookahead1(); +//! if lookahead.peek( syn::Ident ) +//! { +//! let ident : syn::Ident = input.parse()?; +//! input.parse::< syn::Token![=] >()?; +//! match ident.to_string().as_str() +//! { +//! DebugMarker::KEYWORD => debug = input.parse()?, +//! EnabledMarker::KEYWORD => enabled = input.parse()?, +//! _ => return Err( lookahead.error() ), +//! } +//! } +//! else +//! { +//! return Err( lookahead.error() ); +//! } +//! +//! // Optional comma handling +//! if input.peek( syn::Token![,] ) +//! { +//! input.parse::< syn::Token![,] >()?; +//! } +//! } +//! +//! Ok( MyAttributes { debug, enabled } ) +//! } +//! } +//! +//! let input : syn::Attribute = syn::parse_quote!( #[ attribute( enabled = true, debug = false ) ] ); +//! let meta = match input.meta +//! { +//! syn::Meta::List( meta_list ) => meta_list, +//! _ => panic!( "Expected a Meta::List" ), +//! }; +//! +//! let nested_meta_stream : proc_macro2::TokenStream = meta.tokens; +//! let attrs : MyAttributes = syn::parse2( nested_meta_stream ).unwrap(); +//! println!( "{:?}", attrs ); +//! ``` +//! +//! In this example, the `AttributePropertyBoolean` struct is used to define attributes with boolean properties. +//! The `DebugMarker` and `EnabledMarker` structs act as markers to distinguish between different boolean attributes. +//! The `MyAttributes` struct aggregates these boolean attributes. +//! +//! The `Parse` implementation for `MyAttributes` iterates through the attribute's key-value pairs, +//! identifying each by its marker's keyword and parsing the boolean value. +//! It uses the `ParseStream` to parse identifiers and their associated values, +//! matching them to the appropriate marker's keyword. +//! If an unrecognized identifier is encountered, it returns an error. +//! +//! The `parse_quote!` macro is used to create a `syn::Attribute` instance with the attribute syntax, +//! which is then parsed into the `MyAttributes` struct. The resulting `MyAttributes` instance is printed to the console. + +// xxx : qqq : improve documentation, add examples + +/// Internal namespace. +pub( crate ) mod private +{ + use crate::*; + + // = AttributePropertyBoolean + + /// A generic boolean attribute property. + /// Defaults to `false`. + /// + /// # Example + /// + /// ```rust + /// use macro_tools::AttributePropertyBoolean; + /// + /// #[ derive( Debug, Default, Clone, Copy ) ] + /// pub struct DebugMarker; + /// + /// #[ derive( Debug, Default, Clone, Copy ) ] + /// pub struct EnabledMarker; + /// + /// pub trait AttributePropertyComponent + /// { + /// const KEYWORD : &'static str; + /// } + /// + /// impl AttributePropertyComponent for DebugMarker + /// { + /// const KEYWORD : &'static str = "debug"; + /// } + /// + /// impl AttributePropertyComponent for EnabledMarker + /// { + /// const KEYWORD : &'static str = "enabled"; + /// } + /// + /// #[ derive( Debug, Default ) ] + /// struct MyAttributes + /// { + /// pub debug : AttributePropertyBoolean< DebugMarker >, + /// pub enabled : AttributePropertyBoolean< EnabledMarker >, + /// } + /// + /// impl syn::parse::Parse for MyAttributes + /// { + /// fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + /// { + /// let mut debug = AttributePropertyBoolean::< DebugMarker >::default(); + /// let mut enabled = AttributePropertyBoolean::< EnabledMarker >::default(); + /// + /// while !input.is_empty() + /// { + /// let lookahead = input.lookahead1(); + /// if lookahead.peek( syn::Ident ) + /// { + /// let ident : syn::Ident = input.parse()?; + /// input.parse::< syn::Token![=] >()?; + /// match ident.to_string().as_str() + /// { + /// DebugMarker::KEYWORD => debug = input.parse()?, + /// EnabledMarker::KEYWORD => enabled = input.parse()?, + /// _ => return Err( lookahead.error() ), + /// } + /// } + /// else + /// { + /// return Err( lookahead.error() ); + /// } + /// + /// // Optional comma handling + /// if input.peek( syn::Token![,] ) + /// { + /// input.parse::< syn::Token![,] >()?; + /// } + /// } + /// + /// Ok( MyAttributes { debug, enabled } ) + /// } + /// } + /// + /// let input : syn::Attribute = syn::parse_quote!( #[ attribute( enabled = true, debug = false ) ] ); + /// let meta = match input.meta + /// { + /// syn::Meta::List( meta_list ) => meta_list, + /// _ => panic!( "Expected a Meta::List" ), + /// }; + /// + /// let nested_meta_stream : proc_macro2::TokenStream = meta.tokens; + /// let attrs : MyAttributes = syn::parse2( nested_meta_stream ).unwrap(); + /// println!( "{:?}", attrs ); + /// ``` + /// + /// In this example, the `AttributePropertyBoolean` struct is used to define attributes with boolean properties. + /// The `DebugMarker` and `EnabledMarker` structs act as markers to distinguish between different boolean attributes. + /// The `MyAttributes` struct aggregates these boolean attributes. + /// + /// The `Parse` implementation for `MyAttributes` iterates through the attribute's key-value pairs, + /// identifying each by its marker's keyword and parsing the boolean value. + /// It uses the `ParseStream` to parse identifiers and their associated values, + /// matching them to the appropriate marker's keyword. + /// If an unrecognized identifier is encountered, it returns an error. + /// + /// The `parse_quote!` macro is used to create a `syn::Attribute` instance with the attribute syntax, + /// which is then parsed into the `MyAttributes` struct. The resulting `MyAttributes` instance is printed to the console. + + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct AttributePropertyBoolean< Marker >( bool, ::core::marker::PhantomData< Marker > ); + + impl< Marker > AttributePropertyBoolean< Marker > + { + /// Just unwraps and returns the internal data. + pub fn internal( self ) -> bool + { + self.0 + } + + /// Returns a reference to the internal boolean value. + pub fn ref_internal( &self ) -> &bool + { + &self.0 + } + } + + impl< Marker > AttributePropertyComponent for AttributePropertyBoolean< Marker > + where + Marker : AttributePropertyComponent, + { + const KEYWORD : &'static str = Marker::KEYWORD; + } + + impl< Marker > syn::parse::Parse for AttributePropertyBoolean< Marker > + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let value : syn::LitBool = input.parse()?; + Ok( value.value.into() ) + } + } + + impl< Marker > From< bool > for AttributePropertyBoolean< Marker > + { + #[ inline( always ) ] + fn from( src : bool ) -> Self + { + Self( src, Default::default() ) + } + } + + impl< Marker > From< AttributePropertyBoolean< Marker > > for bool + { + #[ inline( always ) ] + fn from( src : AttributePropertyBoolean< Marker > ) -> Self + { + src.0 + } + } + + impl< Marker > core::ops::Deref for AttributePropertyBoolean< Marker > + { + type Target = bool; + + #[ inline( always ) ] + fn deref( &self ) -> &bool + { + &self.0 + } + } + + impl< Marker > AsRef< bool > for AttributePropertyBoolean< Marker > + { + #[ inline( always ) ] + fn as_ref( &self ) -> &bool + { + &self.0 + } + } + + // = AttributePropertyOptionalBoolean + + /// A generic optional boolean attribute property: `Option< bool >`. + /// Defaults to `false`. + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct AttributePropertyOptionalBoolean< Marker >( Option< bool >, ::core::marker::PhantomData< Marker > ); + + impl< Marker > AttributePropertyOptionalBoolean< Marker > + { + /// Just unwraps and returns the internal data. + pub fn internal( self ) -> Option< bool > + { + self.0 + } + + /// Returns a reference to the internal optional boolean value. + pub fn ref_internal( &self ) -> Option< &bool > + { + self.0.as_ref() + } + } + + impl< Marker > AttributePropertyComponent for AttributePropertyOptionalBoolean< Marker > + where + Marker : AttributePropertyComponent, + { + const KEYWORD : &'static str = Marker::KEYWORD; + } + + impl< Marker > syn::parse::Parse for AttributePropertyOptionalBoolean< Marker > + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let value : syn::LitBool = input.parse()?; + Ok( value.value.into() ) + } + } + + impl< Marker > From< bool > for AttributePropertyOptionalBoolean< Marker > + { + #[ inline( always ) ] + fn from( src : bool ) -> Self + { + Self( Some( src ), Default::default() ) + } + } + + impl< Marker > From< Option< bool > > for AttributePropertyOptionalBoolean< Marker > + { + #[ inline( always ) ] + fn from( src : Option< bool > ) -> Self + { + Self( src, Default::default() ) + } + } + + impl< Marker > From< AttributePropertyOptionalBoolean< Marker > > for Option< bool > + { + #[ inline( always ) ] + fn from( src : AttributePropertyOptionalBoolean< Marker > ) -> Self + { + src.0 + } + } + + impl< Marker > core::ops::Deref for AttributePropertyOptionalBoolean< Marker > + { + type Target = Option< bool >; + #[ inline( always ) ] + fn deref( &self ) -> &Option< bool > + { + &self.0 + } + } + + impl< Marker > AsRef< Option< bool > > for AttributePropertyOptionalBoolean< Marker > + { + #[ inline( always ) ] + fn as_ref( &self ) -> &Option< bool > + { + &self.0 + } + } + + // = AttributePropertySyn + + /// Property of an attribute which simply wraps one of the standard `syn` types. + #[ derive( Debug, Clone ) ] + pub struct AttributePropertySyn< T, Marker >( T, ::core::marker::PhantomData< Marker > ) + where + T : syn::parse::Parse + quote::ToTokens; + + impl< T, Marker > AttributePropertySyn< T, Marker > + where + T : syn::parse::Parse + quote::ToTokens, + { + /// Just unwraps and returns the internal data. + #[ allow( dead_code ) ] + pub fn internal( self ) -> T + { + self.0 + } + + /// Returns a reference to the internal data. + #[ allow( dead_code ) ] + pub fn ref_internal( &self ) -> &T + { + &self.0 + } + } + + impl< T, Marker > AttributePropertyComponent for AttributePropertySyn< T, Marker > + where + T : syn::parse::Parse + quote::ToTokens, + Marker : AttributePropertyComponent, + { + const KEYWORD : &'static str = Marker::KEYWORD; + } + + impl< T, Marker > syn::parse::Parse for AttributePropertySyn< T, Marker > + where + T : syn::parse::Parse + quote::ToTokens, + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let value : T = input.parse()?; + Ok( value.into() ) + } + } + + impl< T, Marker > quote::ToTokens for AttributePropertySyn< T, Marker > + where + T : syn::parse::Parse + quote::ToTokens, + { + fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) + { + self.0.to_tokens( tokens ); + } + } + + impl< T, Marker > core::ops::Deref for AttributePropertySyn< T, Marker > + where T : syn::parse::Parse + quote::ToTokens + { + type Target = T; + #[ inline( always ) ] + fn deref( &self ) -> &T + { + &self.0 + } + } + + impl< T, Marker > AsRef< T > for AttributePropertySyn< T, Marker > + where T : syn::parse::Parse + quote::ToTokens + { + #[ inline( always ) ] + fn as_ref( &self ) -> &T + { + &self.0 + } + } + + impl< T, Marker > From< T > for AttributePropertySyn< T, Marker > + where T : syn::parse::Parse + quote::ToTokens + { + #[ inline( always ) ] + fn from( src : T ) -> Self + { + Self( src, Default::default() ) + } + } + + // = AttributePropertyOptionalSyn + + /// Property of an attribute which simply wraps one of the standard `syn` types and keeps it optional. + #[ derive( Debug, Clone ) ] + pub struct AttributePropertyOptionalSyn< T, Marker >( Option< T >, ::core::marker::PhantomData< Marker > ) + where + T : syn::parse::Parse + quote::ToTokens; + + impl< T, Marker > AttributePropertyOptionalSyn< T, Marker > + where + T : syn::parse::Parse + quote::ToTokens, + { + /// Just unwraps and returns the internal data. + pub fn internal( self ) -> Option< T > + { + self.0 + } + + /// Returns an Option reference to the internal data. + pub fn ref_internal( &self ) -> Option< &T > + { + self.0.as_ref() + } + } + + impl< T, Marker > AttributePropertyComponent for AttributePropertyOptionalSyn< T, Marker > + where + T : syn::parse::Parse + quote::ToTokens, + Marker : AttributePropertyComponent, + { + const KEYWORD : &'static str = Marker::KEYWORD; + } + + impl< T, Marker > Default for AttributePropertyOptionalSyn< T, Marker > + where + T : syn::parse::Parse + quote::ToTokens, + { + fn default() -> Self + { + Self( None, Default::default() ) + } + } + + impl< T, Marker > syn::parse::Parse for AttributePropertyOptionalSyn< T, Marker > + where + T : syn::parse::Parse + quote::ToTokens, + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let value : T = input.parse()?; + Ok( value.into() ) + } + } + + impl< T, Marker > quote::ToTokens for AttributePropertyOptionalSyn< T, Marker > + where + T : syn::parse::Parse + quote::ToTokens, + { + fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) + { + self.0.to_tokens( tokens ); + } + } + + impl< T, Marker > core::ops::Deref for AttributePropertyOptionalSyn< T, Marker > + where T : syn::parse::Parse + quote::ToTokens + { + type Target = Option< T >; + #[ inline( always ) ] + fn deref( &self ) -> &Option< T > + { + &self.0 + } + } + + impl< T, Marker > AsRef< Option< T > > for AttributePropertyOptionalSyn< T, Marker > + where T : syn::parse::Parse + quote::ToTokens + { + #[ inline( always ) ] + fn as_ref( &self ) -> &Option< T > + { + &self.0 + } + } + + impl< T, Marker > From< T > for AttributePropertyOptionalSyn< T, Marker > + where T : syn::parse::Parse + quote::ToTokens + { + #[ inline( always ) ] + fn from( src : T ) -> Self + { + Self( Some( src ), Default::default() ) + } + } + + impl< T, Marker > From< Option< T > > for AttributePropertyOptionalSyn< T, Marker > + where T : syn::parse::Parse + quote::ToTokens + { + #[ inline( always ) ] + fn from( src : Option< T > ) -> Self + { + Self( src, Default::default() ) + } + } + + impl< T, Marker > From< AttributePropertyOptionalSyn< T, Marker > > for Option< T > + where T : syn::parse::Parse + quote::ToTokens + { + #[ inline( always ) ] + fn from( src : AttributePropertyOptionalSyn< T, Marker > ) -> Self + { + src.0 + } + } + + impl< 'a, T, Marker > From< &'a AttributePropertyOptionalSyn< T, Marker > > for Option< &'a T > + where T : syn::parse::Parse + quote::ToTokens + { + #[ inline( always ) ] + fn from( src : &'a AttributePropertyOptionalSyn< T, Marker > ) -> Self + { + src.0.as_ref() + } + } + +} + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use protected::*; + +/// Protected namespace of the module. +pub mod protected +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::orphan::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + }; +} + +/// Orphan namespace of the module. +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; +} + +/// Exposed namespace of the module. +pub mod exposed +{ + pub use super::protected as attr_prop; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::prelude::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + AttributePropertyBoolean, + AttributePropertyOptionalBoolean, + AttributePropertySyn, + AttributePropertyOptionalSyn, + }; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +pub mod prelude +{ +} diff --git a/module/core/macro_tools/src/equation.rs b/module/core/macro_tools/src/equation.rs new file mode 100644 index 0000000000..36bab1ccab --- /dev/null +++ b/module/core/macro_tools/src/equation.rs @@ -0,0 +1,160 @@ +//! +//! Attributes analyzys and manipulation. +//! + +/// Internal namespace. +pub( crate ) mod private +{ + use crate::*; + + /// Represents an equation parsed from a procedural macro input. + /// + /// This struct models an equation consisting of a left-hand side, an operator, + /// and a right-hand side. The `Equation` is typically constructed during the + /// parsing process of macro input, where the `left` and `op` fields are expected + /// to be syntactically represented by `syn::Path` and `syn::BinOp` respectively, + /// indicating the variable and operation involved. The `right` field is a + /// `proc_macro2::TokenStream`, which can represent more complex expressions + /// including, but not limited to, literals, function calls, or further operations. + /// + /// # Fields + /// - `left`: The left-hand side of the equation, represented as a path. + /// This could be a variable or a more complex path in the code being + /// processed by the macro. + /// + /// - `op`: The binary operator used in the equation, such as addition, + /// subtraction, multiplication, etc. + /// + /// - `right`: The right-hand side of the equation. Given the potential + /// complexity of expressions on this side, it is represented as a + /// `proc_macro2::TokenStream` to accommodate any valid Rust expression. + /// + /// # Examples + /// + /// Parsing an equation from macro input: + /// + /// ```rust + /// use macro_tools::equation; + /// let got : equation::Equation = syn::parse_quote!( default = 31 ); + /// macro_tools::tree_print!( got ); + /// assert_eq!( macro_tools::code_to_str!( got ), "default = 31".to_string() ); + /// ``` + #[ derive( Debug ) ] + pub struct Equation + { + /// The LHS of the equation, represented by a syntactic path. + pub left : syn::Path, + // /// The binary operator (e.g., +, -, *, /) of the equation. + // pub op : syn::BinOp, + /// Equality token. + pub op : syn::Token![ = ], + /// The RHS of the equation, capable of holding complex expressions. + pub right : proc_macro2::TokenStream, + } + + impl syn::parse::Parse for Equation + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > + { + let left : syn::Path = input.parse()?; + let op : syn::Token![ = ] = input.parse()?; + let right : proc_macro2::TokenStream = input.parse()?; + Ok( Equation { left, op, right } ) + } + } + + impl quote::ToTokens for Equation + { + fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) + { + self.left.to_tokens( tokens ); + self.op.to_tokens( tokens ); + self.right.to_tokens( tokens ); + } + } + + // impl core::fmt::Display for Equation + // { + // fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result + // { + // write!( f, "{}", self.left.to_string() ); + // write!( f, "{}", self.op.to_string() ); + // write!( f, "{}", self.right.to_string() ) + // } + // } + + /// + /// For attribute like `#[former( default = 31 ) ]` return key `default` and value `31`, + /// as well as syn::Meta as the last element of result tuple. + /// + /// ### Basic use-case. + /// + /// ```rust + /// use macro_tools::equation; + /// let attr : syn::Attribute = syn::parse_quote!( #[ former( default = 31 ) ] ); + /// // tree_print!( attr ); + /// let got = equation::from_meta( &attr ).unwrap(); + /// assert_eq!( macro_tools::code_to_str!( got ), "default = 31".to_string() ); + /// ``` + + pub fn from_meta( attr : &syn::Attribute ) -> Result< Equation > + { + let meta = &attr.meta; + return match meta + { + syn::Meta::List( ref meta_list ) => + { + let eq : Equation = syn::parse2( meta_list.tokens.clone() )?; + Ok( eq ) + } + _ => return Err( syn::Error::new( attr.span(), "Unknown format of attribute, expected syn::Meta::List( meta_list )" ) ), + }; + } + +} + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use protected::*; + +/// Protected namespace of the module. +pub mod protected +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::orphan::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + from_meta, + }; +} + +/// Orphan namespace of the module. +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; +} + +/// Exposed namespace of the module. +pub mod exposed +{ + pub use super::protected as equation; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::prelude::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + Equation, + }; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +pub mod prelude +{ +} diff --git a/module/core/macro_tools/src/lib.rs b/module/core/macro_tools/src/lib.rs index 67635b450b..b1ababf64b 100644 --- a/module/core/macro_tools/src/lib.rs +++ b/module/core/macro_tools/src/lib.rs @@ -27,11 +27,12 @@ mod file { // use super::*; pub mod attr; + pub mod attr_prop; pub mod container_kind; pub mod derive; pub mod diag; pub mod drop; - // pub mod generic_analyze; + pub mod equation; pub mod generic_args; pub mod generic_params; pub mod item; @@ -79,11 +80,12 @@ pub mod protected pub use super::file:: { attr::orphan::*, + attr_prop::orphan::*, container_kind::orphan::*, derive::orphan::*, diag::orphan::*, drop::orphan::*, - // generic_analyze::orphan::*, + equation::orphan::*, generic_args::orphan::*, generic_params::orphan::*, item::orphan::*, @@ -134,11 +136,12 @@ pub mod exposed pub use super::file:: { attr::exposed::*, + attr_prop::exposed::*, container_kind::exposed::*, derive::orphan::*, diag::exposed::*, drop::exposed::*, - // generic_analyze::exposed::*, + equation::exposed::*, generic_args::exposed::*, generic_params::exposed::*, item::exposed::*, @@ -204,11 +207,12 @@ pub mod prelude pub use super::file:: { attr::prelude::*, + attr_prop::prelude::*, container_kind::prelude::*, derive::orphan::*, diag::prelude::*, drop::prelude::*, - // generic_analyze::prelude::*, + equation::prelude::*, generic_args::prelude::*, generic_params::prelude::*, item::prelude::*, diff --git a/module/core/macro_tools/src/struct_like.rs b/module/core/macro_tools/src/struct_like.rs index a2a80e6c5d..55b90ba0eb 100644 --- a/module/core/macro_tools/src/struct_like.rs +++ b/module/core/macro_tools/src/struct_like.rs @@ -245,9 +245,7 @@ pub( crate ) mod private impl StructLike { - // xxx2 : continue /// Returns an iterator over elements of the item. - // pub fn elements< 'a >( &'a self ) -> impl Iterator< Item = FieldOrVariant< 'a > > + 'a pub fn elements< 'a >( &'a self ) -> impl IterTrait< 'a, FieldOrVariant< 'a > > + 'a { match self diff --git a/module/core/macro_tools/src/tokens.rs b/module/core/macro_tools/src/tokens.rs index 35c866d2ac..7a09fc4689 100644 --- a/module/core/macro_tools/src/tokens.rs +++ b/module/core/macro_tools/src/tokens.rs @@ -71,82 +71,6 @@ pub( crate ) mod private } } - /// Represents an equation parsed from a procedural macro input. - /// - /// This struct models an equation consisting of a left-hand side, an operator, - /// and a right-hand side. The `Equation` is typically constructed during the - /// parsing process of macro input, where the `left` and `op` fields are expected - /// to be syntactically represented by `syn::Path` and `syn::BinOp` respectively, - /// indicating the variable and operation involved. The `right` field is a - /// `proc_macro2::TokenStream`, which can represent more complex expressions - /// including, but not limited to, literals, function calls, or further operations. - /// - /// # Fields - /// - `left`: The left-hand side of the equation, represented as a path. - /// This could be a variable or a more complex path in the code being - /// processed by the macro. - /// - /// - `op`: The binary operator used in the equation, such as addition, - /// subtraction, multiplication, etc. - /// - /// - `right`: The right-hand side of the equation. Given the potential - /// complexity of expressions on this side, it is represented as a - /// `proc_macro2::TokenStream` to accommodate any valid Rust expression. - /// - /// # Examples - /// - /// Parsing an equation from macro input: - /// - /// ```rust - /// use macro_tools::exposed::*; - /// let got : tokens::Equation = syn::parse_quote!( default = 31 ); - /// tree_print!( got ); - /// assert_eq!( code_to_str!( got ), "default = 31".to_string() ); - /// ``` - #[ derive( Debug ) ] - pub struct Equation - { - /// The LHS of the equation, represented by a syntactic path. - pub left : syn::Path, - // /// The binary operator (e.g., +, -, *, /) of the equation. - // pub op : syn::BinOp, - /// Equality token. - pub op : syn::Token![ = ], - /// The RHS of the equation, capable of holding complex expressions. - pub right : proc_macro2::TokenStream, - } - - impl syn::parse::Parse for Equation - { - fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > - { - let left : syn::Path = input.parse()?; - let op : syn::Token![ = ] = input.parse()?; - let right : proc_macro2::TokenStream = input.parse()?; - Ok( Equation { left, op, right } ) - } - } - - impl quote::ToTokens for Equation - { - fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) - { - self.left.to_tokens( tokens ); - self.op.to_tokens( tokens ); - self.right.to_tokens( tokens ); - } - } - - // impl core::fmt::Display for Equation - // { - // fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result - // { - // write!( f, "{}", self.left.to_string() ); - // write!( f, "{}", self.op.to_string() ); - // write!( f, "{}", self.right.to_string() ) - // } - // } - } #[ doc( inline ) ] @@ -181,7 +105,6 @@ pub mod exposed pub use super::private:: { Tokens, - Equation, }; } diff --git a/module/core/macro_tools/tests/inc/attr.rs b/module/core/macro_tools/tests/inc/attr.rs deleted file mode 100644 index 6bba6e98fc..0000000000 --- a/module/core/macro_tools/tests/inc/attr.rs +++ /dev/null @@ -1,53 +0,0 @@ - -use super::*; - -// - -#[ test ] -fn parse() -{ - - let attr : syn::Attribute = syn::parse_quote!( #[ default( 31 ) ] ); - tree_print!( attr ); - - let attr : syn::Attribute = syn::parse_quote!( #[ default[ 31 ] ] ); - tree_print!( attr ); - - let attr : syn::Attribute = syn::parse_quote!( #[ former( default = 31 ) ] ); - // tree_print!( attr ); - let got = equation( &attr ).unwrap(); - a_id!( code_to_str!( got ), "default = 31".to_string() ); - a_id!( got.left, syn::parse_quote!( default ) ); - a_id!( got.op, syn::token::Eq::default() ); - a_id!( code_to_str!( got.right ), "31".to_string() ); - -} - -#[ test ] -fn is_standard_standard() -{ - // Test a selection of attributes known to be standard - assert!( is_standard( "cfg" ), "Expected 'cfg' to be a standard attribute." ); - assert!( is_standard( "derive" ), "Expected 'derive' to be a standard attribute." ); - assert!( is_standard( "inline" ), "Expected 'inline' to be a standard attribute." ); - assert!( is_standard( "test" ), "Expected 'test' to be a standard attribute." ); - assert!( is_standard( "doc" ), "Expected 'doc' to be a standard attribute." ); -} - -#[ test ] -fn is_standard_non_standard() -{ - // Test some made-up attributes that should not be standard - assert!( !is_standard( "custom_attr" ), "Expected 'custom_attr' to not be a standard attribute." ); - assert!( !is_standard( "my_attribute" ), "Expected 'my_attribute' to not be a standard attribute." ); - assert!( !is_standard( "special_feature" ), "Expected 'special_feature' to not be a standard attribute." ); -} - -#[ test ] -fn is_standard_edge_cases() -{ - // Test edge cases like empty strings or unusual input - assert!( !is_standard( "" ), "Expected empty string to not be a standard attribute." ); - assert!( !is_standard( " " ), "Expected a single space to not be a standard attribute." ); - assert!( !is_standard( "cfg_attr_extra" ), "Expected 'cfg_attr_extra' to not be a standard attribute." ); -} diff --git a/module/core/macro_tools/tests/inc/attr_prop_test.rs b/module/core/macro_tools/tests/inc/attr_prop_test.rs new file mode 100644 index 0000000000..8d2ff6c559 --- /dev/null +++ b/module/core/macro_tools/tests/inc/attr_prop_test.rs @@ -0,0 +1,102 @@ +use super::*; +use quote::ToTokens; + +#[ test ] +fn test_attribute_property_boolean() +{ + + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct DebugMarker; + + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct EnabledMarker; + + pub trait AttributePropertyComponent + { + const KEYWORD : &'static str; + } + + impl AttributePropertyComponent for DebugMarker + { + const KEYWORD : &'static str = "debug"; + } + + impl AttributePropertyComponent for EnabledMarker + { + const KEYWORD : &'static str = "enabled"; + } + + #[ derive( Debug, Default ) ] + struct MyAttributes + { + pub debug : AttributePropertyBoolean< DebugMarker >, + pub enabled : AttributePropertyBoolean< EnabledMarker >, + } + + impl syn::parse::Parse for MyAttributes + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let mut debug = AttributePropertyBoolean::< DebugMarker >::default(); + let mut enabled = AttributePropertyBoolean::< EnabledMarker >::default(); + + while !input.is_empty() + { + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Ident ) + { + let ident : syn::Ident = input.parse()?; + input.parse::< syn::Token![=] >()?; + match ident.to_string().as_str() + { + DebugMarker::KEYWORD => debug = input.parse()?, + EnabledMarker::KEYWORD => enabled = input.parse()?, + _ => return Err( lookahead.error() ), + } + } + else + { + return Err( lookahead.error() ); + } + + // Optional comma handling + if input.peek( syn::Token![,] ) + { + input.parse::< syn::Token![,] >()?; + } + } + + Ok( MyAttributes { debug, enabled } ) + } + } + + let input : syn::Attribute = syn::parse_quote!( #[ attribute( enabled = true, debug = false ) ] ); + let meta = match input.meta + { + syn::Meta::List( meta_list ) => meta_list, + _ => panic!( "Expected a Meta::List" ), + }; + + let nested_meta_stream : proc_macro2::TokenStream = meta.tokens; + let attrs : MyAttributes = syn::parse2( nested_meta_stream ).unwrap(); + println!( "{:?}", attrs ); + + let attr : AttributePropertyBoolean< DebugMarker > = AttributePropertyBoolean::default(); + assert_eq!( attr.internal(), false ); + let attr : AttributePropertyBoolean< DebugMarker > = true.into(); + assert_eq!( attr.internal(), true ); + let attr : AttributePropertyBoolean< DebugMarker > = false.into(); + assert_eq!( attr.internal(), false ); + + let input : syn::Attribute = syn::parse_quote!( #[ attribute( enabled = true, debug = false ) ] ); + let meta = match input.meta + { + syn::Meta::List( meta_list ) => meta_list, + _ => panic!( "Expected a Meta::List" ), + }; + + let nested_meta_stream : proc_macro2::TokenStream = meta.tokens; + let parsed : MyAttributes = syn::parse2( nested_meta_stream ).unwrap(); + assert_eq!( parsed.enabled.internal(), true ); + assert_eq!( parsed.debug.internal(), false ); +} diff --git a/module/core/macro_tools/tests/inc/attr_test.rs b/module/core/macro_tools/tests/inc/attr_test.rs new file mode 100644 index 0000000000..dab489f65d --- /dev/null +++ b/module/core/macro_tools/tests/inc/attr_test.rs @@ -0,0 +1,76 @@ + +use super::*; + +// + +#[ test ] +fn is_standard_standard() +{ + // Test a selection of attributes known to be standard + assert!( attr::is_standard( "cfg" ), "Expected 'cfg' to be a standard attribute." ); + assert!( attr::is_standard( "derive" ), "Expected 'derive' to be a standard attribute." ); + assert!( attr::is_standard( "inline" ), "Expected 'inline' to be a standard attribute." ); + assert!( attr::is_standard( "test" ), "Expected 'test' to be a standard attribute." ); + assert!( attr::is_standard( "doc" ), "Expected 'doc' to be a standard attribute." ); +} + +#[ test ] +fn is_standard_non_standard() +{ + // Test some made-up attributes that should not be standard + assert!( !attr::is_standard( "custom_attr" ), "Expected 'custom_attr' to not be a standard attribute." ); + assert!( !attr::is_standard( "my_attribute" ), "Expected 'my_attribute' to not be a standard attribute." ); + assert!( !attr::is_standard( "special_feature" ), "Expected 'special_feature' to not be a standard attribute." ); +} + +#[ test ] +fn is_standard_edge_cases() +{ + // Test edge cases like empty strings or unusual input + assert!( !attr::is_standard( "" ), "Expected empty string to not be a standard attribute." ); + assert!( !attr::is_standard( " " ), "Expected a single space to not be a standard attribute." ); + assert!( !attr::is_standard( "cfg_attr_extra" ), "Expected 'cfg_attr_extra' to not be a standard attribute." ); +} + +#[ test ] +fn attribute_component_from_meta() +{ + struct MyComponent; + + impl AttributeComponent for MyComponent + { + const KEYWORD : &'static str = "my_component"; + + fn from_meta( attr : &syn::Attribute ) -> Result< Self > + { + match &attr.meta + { + syn::Meta::NameValue( meta_name_value ) if meta_name_value.path.is_ident( Self::KEYWORD ) => + { + Ok( MyComponent ) + } + _ => Err( syn::Error::new_spanned( attr, "Failed to parse attribute as MyComponent" ) ), + } + } + } + + // Define a sample attribute + let attr : syn::Attribute = syn::parse_quote!( #[ my_component = "value" ] ); + + // Attempt to construct MyComponent from the attribute + let result = MyComponent::from_meta( &attr ); + + // Assert that the construction was successful + assert!( result.is_ok() ); + + // Negative testing + + // Define a sample invalid attribute + let attr : syn::Attribute = syn::parse_quote!( #[ other_component = "value" ] ); + + // Attempt to construct MyComponent from the invalid attribute + let result = MyComponent::from_meta( &attr ); + + // Assert that the construction failed + assert!( result.is_err() ); +} diff --git a/module/core/macro_tools/tests/inc/basic.rs b/module/core/macro_tools/tests/inc/basic.rs deleted file mode 100644 index 6b7500c43b..0000000000 --- a/module/core/macro_tools/tests/inc/basic.rs +++ /dev/null @@ -1,417 +0,0 @@ - -use super::*; - -// - -tests_impls! -{ - - fn tree_diagnostics_str_basic() - { - - let exp = r#"code : std :: collections :: HashMap < i32 , i32 > : -TokenStream [ - Ident { - sym: std, - }, - Punct { - char: ':', - spacing: Joint, - }, - Punct { - char: ':', - spacing: Alone, - }, - Ident { - sym: collections, - }, - Punct { - char: ':', - spacing: Joint, - }, - Punct { - char: ':', - spacing: Alone, - }, - Ident { - sym: HashMap, - }, - Punct { - char: '<', - spacing: Alone, - }, - Ident { - sym: i32, - }, - Punct { - char: ',', - spacing: Alone, - }, - Ident { - sym: i32, - }, - Punct { - char: '>', - spacing: Alone, - }, -]"#; - let code = qt!( std::collections::HashMap< i32, i32 > ); - let got = the_module::tree_diagnostics_str!( code ); - // println!( "{}", got ); - a_id!( got, exp ); - let got = the_module::tree_print!( code ); - // println!( "{}", got ); - a_id!( got, exp ); - - } - - // - - fn syn_err_basic() - { - - // test.case( "basic" ); - let err = the_module::syn_err!( "abc" ); - a_id!( err.to_string(), "abc" ); - - // test.case( "basic, trailing comma" ); - let err = the_module::syn_err!( "abc", ); - a_id!( err.to_string(), "abc" ); - - // test.case( "with span" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let err = the_module::syn_err!( tree_type, "abc" ); - a_id!( err.to_string(), "abc" ); - // a_id!( err.span(), syn::spanned::Spanned::span( &tree_type ) ); - - // test.case( "with span, trailing comma" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let err = the_module::syn_err!( tree_type, "abc", ); - a_id!( err.to_string(), "abc" ); - - // test.case( "with span and args" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let err = the_module::syn_err!( tree_type, "abc{}{}", "def", "ghi" ); - a_id!( err.to_string(), "abcdefghi" ); - // a_id!( err.span(), syn::spanned::Spanned::span( &tree_type ) ); - - // test.case( "with span and args, trailing comma" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let err = the_module::syn_err!( tree_type, "abc{}{}", "def", "ghi", ); - a_id!( err.to_string(), "abcdefghi" ); - - // test.case( "without span" ); - let err = the_module::syn_err!( _, "abc" ); - a_id!( err.to_string(), "abc" ); - - // test.case( "without span, trailing comma" ); - let err = the_module::syn_err!( _, "abc", ); - a_id!( err.to_string(), "abc" ); - - // test.case( "without span, but with args" ); - let err = the_module::syn_err!( _, "abc{}{}", "def", "ghi" ); - a_id!( err.to_string(), "abcdefghi" ); - - // test.case( "without span, trailing comma" ); - let err = the_module::syn_err!( _, "abc{}{}", "def", "ghi", ); - a_id!( err.to_string(), "abcdefghi" ); - - } - - // - - fn type_container_kind_basic() - { - use the_module::exposed::container_kind; - - // test.case( "core::option::Option< i32 >" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::No ); - - // test.case( "core::option::Option< Vec >" ); - let code = qt!( core::option::Option< Vec > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::No ); - - // test.case( "alloc::vec::Vec< i32 >" ); - let code = qt!( alloc::vec::Vec< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "alloc::vec::Vec" ); - let code = qt!( alloc::vec::Vec ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "std::vec::Vec< i32 >" ); - let code = qt!( std::vec::Vec< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "std::vec::Vec" ); - let code = qt!( std::vec::Vec ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "std::Vec< i32 >" ); - let code = qt!( std::Vec< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "std::Vec" ); - let code = qt!( std::Vec ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::Vector ); - - // test.case( "not vector" ); - let code = qt!( std::SomeVector< i32, i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::No ); - - // test.case( "hash map" ); - let code = qt!( std::collections::HashMap< i32, i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::HashMap ); - - // test.case( "hash set" ); - let code = qt!( std::collections::HashSet< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = container_kind::of_type( &tree_type ); - a_id!( got, the_module::container_kind::ContainerKind::HashSet ); - - } - - // - - fn type_optional_container_kind_basic() - { - - // test.case( "non optional not container" ); - let code = qt!( i32 ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::No, false ) ); - - // test.case( "optional not container" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::No, true ) ); - - // test.case( "optional not container" ); - let code = qt!( Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::No, true ) ); - - - // test.case( "optional vector" ); - let code = qt!( core::option::Option< Vec > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::Vector, true ) ); - - // test.case( "optional vector" ); - let code = qt!( Option< Vec > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::Vector, true ) ); - - // test.case( "non optional vector" ); - let code = qt!( std::Vec< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::Vector, false ) ); - - - // test.case( "optional vector" ); - let code = qt!( core::option::Option< std::collections::HashMap< i32, i32 > > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, true ) ); - - // test.case( "optional vector" ); - let code = qt!( Option< HashMap > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, true ) ); - - // test.case( "non optional vector" ); - let code = qt!( HashMap< i32, i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, false ) ); - - - // test.case( "optional vector" ); - let code = qt!( core::option::Option< std::collections::HashSet< i32, i32 > > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, true ) ); - - // test.case( "optional vector" ); - let code = qt!( Option< HashSet > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, true ) ); - - // test.case( "non optional vector" ); - let code = qt!( HashSet< i32, i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::container_kind::of_optional( &tree_type ); - a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, false ) ); - - } - - // - - fn type_rightmost_basic() - { - - // test.case( "core::option::Option< i32 >" ); - let code = qt!( core::option::Option< i32 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - let got = the_module::typ::type_rightmost( &tree_type ); - a_id!( got, Some( "Option".to_string() ) ); - - } - - // - - fn type_parameters_basic() - { - - macro_rules! q - { - ( $( $Src : tt )+ ) => - { - syn::parse2::< syn::Type >( qt!( $( $Src )+ ) ).unwrap() - } - } - - // test.case( "core::option::Option< i8, i16, i32, i64 >" ); - let code = qt!( core::option::Option< i8, i16, i32, i64 > ); - let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); - - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=0 ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ) ]; - a_id!( got, exp ); - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=1 ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ) ]; - a_id!( got, exp ); - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=2 ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ) ]; - a_id!( got, exp ); - - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..0 ).into_iter().cloned().collect(); - let exp : Vec< syn::Type > = vec![]; - a_id!( got, exp ); - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..1 ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ) ]; - a_id!( got, exp ); - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..2 ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ) ]; - a_id!( got, exp ); - - // unbound - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; - a_id!( got, exp ); - - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; - a_id!( got, exp ); - - let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); - let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; - a_id!( got, exp ); - - } - - // - - #[ test ] - fn attr_pair_single_basic() -> Result< () > - { - use syn::spanned::Spanned; - - // test.case( "basic" ); - let input = qt! - { - #[ derive( Former ) ] - pub struct Struct1 - { - #[former( default = 31 ) ] - pub int_1 : i32, - } - }; - - let ast = match syn::parse2::< syn::DeriveInput >( input ) - { - Ok( syntax_tree ) => syntax_tree, - Err( err ) => return Err( err ), - }; - - let fields = match ast.data - { - syn::Data::Struct( ref data_struct ) => match data_struct.fields - { - syn::Fields::Named( ref fields_named ) => - { - &fields_named.named - }, - _ => return Err( syn::Error::new( ast.span(), "Unknown format of data, expected syn::Fields::Named( ref fields_named )" ) ), - }, - _ => return Err( syn::Error::new( ast.span(), "Unknown format of data, expected syn::Data::Struct( ref data_struct )" ) ), - }; - - let attr = fields.first().ok_or_else( || err( "No field" ) )?.attrs.first().ok_or_else( || err( "No attr" ) )?; - - let exp = Equation - { - left : parse_quote!{ default }, - op : parse_quote!{ = }, - right : parse_quote!{ 31 }, - }; - let got = the_module::equation( &attr )?; - a_id!( got.left, exp.left ); - a_id!( format!( "{:?}", got ), format!( "{:?}", exp ) ); - // a_id!( got.right, exp.right ); - - return Ok( () ); - - fn err( src : &str ) -> syn::Error - { - syn::Error::new( proc_macro2::Span::call_site(), src ) - } - } - -} - -// - -tests_index! -{ - tree_diagnostics_str_basic, - syn_err_basic, - type_container_kind_basic, - type_optional_container_kind_basic, - type_rightmost_basic, - type_parameters_basic, - attr_pair_single_basic, -} diff --git a/module/core/macro_tools/tests/inc/basic_test.rs b/module/core/macro_tools/tests/inc/basic_test.rs new file mode 100644 index 0000000000..78e3dc4460 --- /dev/null +++ b/module/core/macro_tools/tests/inc/basic_test.rs @@ -0,0 +1,14 @@ + +use super::*; + +// + +tests_impls! +{ +} + +// + +tests_index! +{ +} diff --git a/module/core/macro_tools/tests/inc/container_kind_test.rs b/module/core/macro_tools/tests/inc/container_kind_test.rs new file mode 100644 index 0000000000..e6669410a1 --- /dev/null +++ b/module/core/macro_tools/tests/inc/container_kind_test.rs @@ -0,0 +1,160 @@ + +use super::*; + +// + +#[ test ] +fn type_container_kind_basic() +{ + use the_module::exposed::container_kind; + + // test.case( "core::option::Option< i32 >" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::No ); + + // test.case( "core::option::Option< Vec >" ); + let code = qt!( core::option::Option< Vec > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::No ); + + // test.case( "alloc::vec::Vec< i32 >" ); + let code = qt!( alloc::vec::Vec< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "alloc::vec::Vec" ); + let code = qt!( alloc::vec::Vec ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "std::vec::Vec< i32 >" ); + let code = qt!( std::vec::Vec< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "std::vec::Vec" ); + let code = qt!( std::vec::Vec ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "std::Vec< i32 >" ); + let code = qt!( std::Vec< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "std::Vec" ); + let code = qt!( std::Vec ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::Vector ); + + // test.case( "not vector" ); + let code = qt!( std::SomeVector< i32, i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::No ); + + // test.case( "hash map" ); + let code = qt!( std::collections::HashMap< i32, i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::HashMap ); + + // test.case( "hash set" ); + let code = qt!( std::collections::HashSet< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = container_kind::of_type( &tree_type ); + a_id!( got, the_module::container_kind::ContainerKind::HashSet ); + +} + +// + +#[ test ] +fn type_optional_container_kind_basic() +{ + + // test.case( "non optional not container" ); + let code = qt!( i32 ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::No, false ) ); + + // test.case( "optional not container" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::No, true ) ); + + // test.case( "optional not container" ); + let code = qt!( Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::No, true ) ); + + + // test.case( "optional vector" ); + let code = qt!( core::option::Option< Vec > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::Vector, true ) ); + + // test.case( "optional vector" ); + let code = qt!( Option< Vec > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::Vector, true ) ); + + // test.case( "non optional vector" ); + let code = qt!( std::Vec< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::Vector, false ) ); + + + // test.case( "optional vector" ); + let code = qt!( core::option::Option< std::collections::HashMap< i32, i32 > > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, true ) ); + + // test.case( "optional vector" ); + let code = qt!( Option< HashMap > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, true ) ); + + // test.case( "non optional vector" ); + let code = qt!( HashMap< i32, i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashMap, false ) ); + + + // test.case( "optional vector" ); + let code = qt!( core::option::Option< std::collections::HashSet< i32, i32 > > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, true ) ); + + // test.case( "optional vector" ); + let code = qt!( Option< HashSet > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, true ) ); + + // test.case( "non optional vector" ); + let code = qt!( HashSet< i32, i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::container_kind::of_optional( &tree_type ); + a_id!( got, ( the_module::container_kind::ContainerKind::HashSet, false ) ); + +} diff --git a/module/core/macro_tools/tests/inc/derive.rs b/module/core/macro_tools/tests/inc/derive_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/derive.rs rename to module/core/macro_tools/tests/inc/derive_test.rs diff --git a/module/core/macro_tools/tests/inc/diag_test.rs b/module/core/macro_tools/tests/inc/diag_test.rs new file mode 100644 index 0000000000..e39db7d824 --- /dev/null +++ b/module/core/macro_tools/tests/inc/diag_test.rs @@ -0,0 +1,133 @@ + +use super::*; + +// + +tests_impls! +{ + + fn tree_diagnostics_str_basic() + { + + let exp = r#"code : std :: collections :: HashMap < i32 , i32 > : +TokenStream [ + Ident { + sym: std, + }, + Punct { + char: ':', + spacing: Joint, + }, + Punct { + char: ':', + spacing: Alone, + }, + Ident { + sym: collections, + }, + Punct { + char: ':', + spacing: Joint, + }, + Punct { + char: ':', + spacing: Alone, + }, + Ident { + sym: HashMap, + }, + Punct { + char: '<', + spacing: Alone, + }, + Ident { + sym: i32, + }, + Punct { + char: ',', + spacing: Alone, + }, + Ident { + sym: i32, + }, + Punct { + char: '>', + spacing: Alone, + }, +]"#; + let code = qt!( std::collections::HashMap< i32, i32 > ); + let got = the_module::tree_diagnostics_str!( code ); + // println!( "{}", got ); + a_id!( got, exp ); + let got = the_module::tree_print!( code ); + // println!( "{}", got ); + a_id!( got, exp ); + + } + + // + + fn syn_err_basic() + { + + // test.case( "basic" ); + let err = the_module::syn_err!( "abc" ); + a_id!( err.to_string(), "abc" ); + + // test.case( "basic, trailing comma" ); + let err = the_module::syn_err!( "abc", ); + a_id!( err.to_string(), "abc" ); + + // test.case( "with span" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let err = the_module::syn_err!( tree_type, "abc" ); + a_id!( err.to_string(), "abc" ); + // a_id!( err.span(), syn::spanned::Spanned::span( &tree_type ) ); + + // test.case( "with span, trailing comma" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let err = the_module::syn_err!( tree_type, "abc", ); + a_id!( err.to_string(), "abc" ); + + // test.case( "with span and args" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let err = the_module::syn_err!( tree_type, "abc{}{}", "def", "ghi" ); + a_id!( err.to_string(), "abcdefghi" ); + // a_id!( err.span(), syn::spanned::Spanned::span( &tree_type ) ); + + // test.case( "with span and args, trailing comma" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let err = the_module::syn_err!( tree_type, "abc{}{}", "def", "ghi", ); + a_id!( err.to_string(), "abcdefghi" ); + + // test.case( "without span" ); + let err = the_module::syn_err!( _, "abc" ); + a_id!( err.to_string(), "abc" ); + + // test.case( "without span, trailing comma" ); + let err = the_module::syn_err!( _, "abc", ); + a_id!( err.to_string(), "abc" ); + + // test.case( "without span, but with args" ); + let err = the_module::syn_err!( _, "abc{}{}", "def", "ghi" ); + a_id!( err.to_string(), "abcdefghi" ); + + // test.case( "without span, trailing comma" ); + let err = the_module::syn_err!( _, "abc{}{}", "def", "ghi", ); + a_id!( err.to_string(), "abcdefghi" ); + + } + +} + +// + +tests_index! +{ + tree_diagnostics_str_basic, + syn_err_basic, +} diff --git a/module/core/macro_tools/tests/inc/drop.rs b/module/core/macro_tools/tests/inc/drop_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/drop.rs rename to module/core/macro_tools/tests/inc/drop_test.rs diff --git a/module/core/macro_tools/tests/inc/equation_test.rs b/module/core/macro_tools/tests/inc/equation_test.rs new file mode 100644 index 0000000000..735d8261fc --- /dev/null +++ b/module/core/macro_tools/tests/inc/equation_test.rs @@ -0,0 +1,108 @@ + +use super::*; + +// + +tests_impls! +{ + + #[ test ] + fn equation_test() -> Result< () > + { + use syn::spanned::Spanned; + + // test.case( "basic" ); + let input = qt! + { + #[ derive( Former ) ] + pub struct Struct1 + { + #[former( default = 31 ) ] + pub int_1 : i32, + } + }; + + let ast = match syn::parse2::< syn::DeriveInput >( input ) + { + Ok( syntax_tree ) => syntax_tree, + Err( err ) => return Err( err ), + }; + + let fields = match ast.data + { + syn::Data::Struct( ref data_struct ) => match data_struct.fields + { + syn::Fields::Named( ref fields_named ) => + { + &fields_named.named + }, + _ => return Err( syn::Error::new( ast.span(), "Unknown format of data, expected syn::Fields::Named( ref fields_named )" ) ), + }, + _ => return Err( syn::Error::new( ast.span(), "Unknown format of data, expected syn::Data::Struct( ref data_struct )" ) ), + }; + + let attr = fields.first().ok_or_else( || err( "No field" ) )?.attrs.first().ok_or_else( || err( "No attr" ) )?; + + let exp = equation::Equation + { + left : parse_quote!{ default }, + op : parse_quote!{ = }, + right : parse_quote!{ 31 }, + }; + let got = equation::from_meta( &attr )?; + a_id!( got.left, exp.left ); + a_id!( format!( "{:?}", got ), format!( "{:?}", exp ) ); + // a_id!( got.right, exp.right ); + + return Ok( () ); + + fn err( src : &str ) -> syn::Error + { + syn::Error::new( proc_macro2::Span::call_site(), src ) + } + } + + fn equation_parse_test() + { + + let got : the_module::Equation = syn::parse_quote!( default = 31 ); + tree_print!( got ); + a_id!( code_to_str!( got ), "default = 31".to_string() ); + + a_id!( got.left, syn::parse_quote!( default ) ); + a_id!( got.op, syn::token::Eq::default() ); + a_id!( code_to_str!( got.right ), "31".to_string() ); + + } + + fn equation_from_meta_test() + { + + let attr1 : syn::Attribute = syn::parse_quote!( #[ default( 31 ) ] ); + tree_print!( attr1 ); + + let attr1 : syn::Attribute = syn::parse_quote!( #[ default[ 31 ] ] ); + tree_print!( attr1 ); + + let attr1 : syn::Attribute = syn::parse_quote!( #[ former( default = 31 ) ] ); + // tree_print!( attr1 ); + let got = equation::from_meta( &attr1 ).unwrap(); + a_id!( code_to_str!( got ), "default = 31".to_string() ); + a_id!( got.left, syn::parse_quote!( default ) ); + a_id!( got.op, syn::token::Eq::default() ); + a_id!( code_to_str!( got.right ), "31".to_string() ); + + } + +} + +// + +// + +tests_index! +{ + equation_test, + equation_parse_test, + equation_from_meta_test, +} diff --git a/module/core/macro_tools/tests/inc/generic_args.rs b/module/core/macro_tools/tests/inc/generic_args_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/generic_args.rs rename to module/core/macro_tools/tests/inc/generic_args_test.rs diff --git a/module/core/macro_tools/tests/inc/generic_params.rs b/module/core/macro_tools/tests/inc/generic_params_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/generic_params.rs rename to module/core/macro_tools/tests/inc/generic_params_test.rs diff --git a/module/core/macro_tools/tests/inc/item_struct.rs b/module/core/macro_tools/tests/inc/item_struct_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/item_struct.rs rename to module/core/macro_tools/tests/inc/item_struct_test.rs diff --git a/module/core/macro_tools/tests/inc/item.rs b/module/core/macro_tools/tests/inc/item_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/item.rs rename to module/core/macro_tools/tests/inc/item_test.rs diff --git a/module/core/macro_tools/tests/inc/mod.rs b/module/core/macro_tools/tests/inc/mod.rs index 4968d3f52f..9ed0a80bee 100644 --- a/module/core/macro_tools/tests/inc/mod.rs +++ b/module/core/macro_tools/tests/inc/mod.rs @@ -3,6 +3,8 @@ use super::*; #[ allow( unused_imports ) ] use test_tools::exposed::*; +#[ allow( unused_imports ) ] +use the_module::protected::*; #[ allow( unused_imports ) ] #[ cfg( feature = "enabled" ) ] @@ -11,21 +13,24 @@ mod if_enabled { use super::*; - use the_module::exposed::*; - mod attr; - mod basic; - mod derive; - mod drop; - mod generic_args; - mod generic_params; - mod item; - mod item_struct; - mod phantom; - mod quantifier; - mod struct_like; - mod syntax; - mod tokens; - mod typ; + mod attr_test; + mod attr_prop_test; + mod basic_test; + mod container_kind_test; + mod derive_test; + mod diag_test; + mod drop_test; + mod equation_test; + mod generic_args_test; + mod generic_params_test; + mod item_test; + mod item_struct_test; + mod phantom_test; + mod quantifier_test; + mod struct_like_test; + mod syntax_test; + mod tokens_test; + mod typ_test; } diff --git a/module/core/macro_tools/tests/inc/phantom.rs b/module/core/macro_tools/tests/inc/phantom_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/phantom.rs rename to module/core/macro_tools/tests/inc/phantom_test.rs diff --git a/module/core/macro_tools/tests/inc/quantifier.rs b/module/core/macro_tools/tests/inc/quantifier_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/quantifier.rs rename to module/core/macro_tools/tests/inc/quantifier_test.rs diff --git a/module/core/macro_tools/tests/inc/struct_like.rs b/module/core/macro_tools/tests/inc/struct_like_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/struct_like.rs rename to module/core/macro_tools/tests/inc/struct_like_test.rs diff --git a/module/core/macro_tools/tests/inc/syntax.rs b/module/core/macro_tools/tests/inc/syntax_test.rs similarity index 100% rename from module/core/macro_tools/tests/inc/syntax.rs rename to module/core/macro_tools/tests/inc/syntax_test.rs diff --git a/module/core/macro_tools/tests/inc/tokens.rs b/module/core/macro_tools/tests/inc/tokens_test.rs similarity index 51% rename from module/core/macro_tools/tests/inc/tokens.rs rename to module/core/macro_tools/tests/inc/tokens_test.rs index aef2707fae..fcae746f5d 100644 --- a/module/core/macro_tools/tests/inc/tokens.rs +++ b/module/core/macro_tools/tests/inc/tokens_test.rs @@ -16,19 +16,3 @@ fn tokens() a_id!( got.to_string(), "# [former (default = 31)]".to_string() ); } - -// - -#[ test ] -fn equation() -{ - - let got : the_module::Equation = syn::parse_quote!( default = 31 ); - tree_print!( got ); - a_id!( code_to_str!( got ), "default = 31".to_string() ); - - a_id!( got.left, syn::parse_quote!( default ) ); - a_id!( got.op, syn::token::Eq::default() ); - a_id!( code_to_str!( got.right ), "31".to_string() ); - -} diff --git a/module/core/macro_tools/tests/inc/typ.rs b/module/core/macro_tools/tests/inc/typ_Test.rs similarity index 63% rename from module/core/macro_tools/tests/inc/typ.rs rename to module/core/macro_tools/tests/inc/typ_Test.rs index e993156bab..174c2c243b 100644 --- a/module/core/macro_tools/tests/inc/typ.rs +++ b/module/core/macro_tools/tests/inc/typ_Test.rs @@ -126,3 +126,70 @@ fn parameter_first_with_deeply_nested_generics() let expected_type : Type = parse_str( "HashMap< String, Option< i32 > >" ).expect( "Expected type to parse correctly" ); assert_eq!( format!( "{:?}", expected_type ), format!( "{:?}", first_param ), "Extracted type does not match expected" ); } + +// + +#[ test ] +fn type_rightmost_basic() +{ + + // test.case( "core::option::Option< i32 >" ); + let code = qt!( core::option::Option< i32 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + let got = the_module::typ::type_rightmost( &tree_type ); + a_id!( got, Some( "Option".to_string() ) ); + +} + +// + +#[ test ] +fn type_parameters_basic() +{ + + macro_rules! q + { + ( $( $Src : tt )+ ) => + { + syn::parse2::< syn::Type >( qt!( $( $Src )+ ) ).unwrap() + } + } + + // test.case( "core::option::Option< i8, i16, i32, i64 >" ); + let code = qt!( core::option::Option< i8, i16, i32, i64 > ); + let tree_type = syn::parse2::< syn::Type >( code ).unwrap(); + + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=0 ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ) ]; + a_id!( got, exp ); + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=1 ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ) ]; + a_id!( got, exp ); + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..=2 ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ) ]; + a_id!( got, exp ); + + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..0 ).into_iter().cloned().collect(); + let exp : Vec< syn::Type > = vec![]; + a_id!( got, exp ); + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..1 ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ) ]; + a_id!( got, exp ); + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, 0..2 ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ) ]; + a_id!( got, exp ); + + // unbound + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; + a_id!( got, exp ); + + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; + a_id!( got, exp ); + + let got : Vec< syn::Type > = the_module::typ::type_parameters( &tree_type, .. ).into_iter().cloned().collect(); + let exp = vec![ q!( i8 ), q!( i16 ), q!( i32 ), q!( i64 ) ]; + a_id!( got, exp ); + +}