diff --git a/module/core/former/tests/inc/former_tests/subformer_container_manual.rs b/module/core/former/tests/inc/former_tests/subformer_container_manual.rs index c680267e6e..85faa40942 100644 --- a/module/core/former/tests/inc/former_tests/subformer_container_manual.rs +++ b/module/core/former/tests/inc/former_tests/subformer_container_manual.rs @@ -27,7 +27,6 @@ pub struct Parent impl< Definition, > ParentFormer< Definition, > where Definition : former::FormerDefinition< Storage = ParentFormerStorage< > >, - // Definition::Types : former::FormerDefinitionTypes< Storage = ParentFormerStorage< > >, { #[ inline( always ) ] @@ -37,10 +36,13 @@ where { Former2::former_begin( None, Some( self ), ParentFormerAssignChildrenEnd::< Definition >::default() ) } - #[ doc = - "Subformer setter for the 'children' field. Method _children_assign unlike method children accept custom container subformer." ] + #[ inline( always ) ] - pub fn children( self ) -> former::ContainerSubformer::< Child, former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > > + pub fn children( self ) -> former::ContainerSubformer:: + < + Child, + former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > + > { self._children_assign::< former::ContainerSubformer::< Child, former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > > >() } @@ -71,14 +73,14 @@ impl< Definition > Default for ParentFormerAssignChildrenEnd< Definition > #[ automatically_derived ] impl< Definition, > former::FormingEnd -// < former::VectorDefinition< Child, ParentFormer< Definition, >, ParentFormer< Definition, >, former::NoEnd >, > < - < Vec< Child > as former::EntityToDefinition< ParentFormer< Definition, >, ParentFormer< Definition, >, former::NoEnd > >::Definition + < + Vec< Child > as former::EntityToDefinition< ParentFormer< Definition, >, ParentFormer< Definition, >, former::NoEnd > + >::Definition > for ParentFormerAssignChildrenEnd< Definition > where Definition : former::FormerDefinition< Storage = ParentFormerStorage< > >, - // Definition::Types : former::FormerDefinitionTypes< Storage = ParentFormerStorage< > >, { #[ inline( always ) ] fn call diff --git a/module/core/former/tests/inc/former_tests/subformer_setter_off_container.rs b/module/core/former/tests/inc/former_tests/subformer_container_setter_off.rs similarity index 55% rename from module/core/former/tests/inc/former_tests/subformer_setter_off_container.rs rename to module/core/former/tests/inc/former_tests/subformer_container_setter_off.rs index bb3fa60b98..b763e6a464 100644 --- a/module/core/former/tests/inc/former_tests/subformer_setter_off_container.rs +++ b/module/core/former/tests/inc/former_tests/subformer_container_setter_off.rs @@ -37,4 +37,35 @@ where "# } + #[ inline( always ) ] + pub fn children2( self ) -> former::ContainerSubformer:: + < + Child, + former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > + > + { + self._children_assign::< _ >() + } + +} + +#[ test ] +fn basic() +{ + + let got = Parent::former() + .children2() + .add( Child::former().name( "a" ).form() ) + .add( Child::former().name( "b" ).form() ) + .end() + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), is_mandatory : false }, + Child { name : "b".to_string(), is_mandatory : false }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + } diff --git a/module/core/former/tests/inc/former_tests/subformer_setter_off_subform.rs b/module/core/former/tests/inc/former_tests/subformer_subform_setter_off.rs similarity index 60% rename from module/core/former/tests/inc/former_tests/subformer_setter_off_subform.rs rename to module/core/former/tests/inc/former_tests/subformer_subform_setter_off.rs index 2fe96eeb44..48f086e668 100644 --- a/module/core/former/tests/inc/former_tests/subformer_setter_off_subform.rs +++ b/module/core/former/tests/inc/former_tests/subformer_subform_setter_off.rs @@ -1,4 +1,5 @@ #![ allow( dead_code ) ] +// xxx : rename use super::*; @@ -38,4 +39,32 @@ where "# } + #[ inline( always ) ] + pub fn children2( self, name : &str ) -> + ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._children_add + ::< ChildFormer< _ >, _, >() + .name( name ) + } + +} + +#[ test ] +fn children() +{ + + let got = Parent::former() + .children2( "a" ).end() + .children2( "b" ).end() + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), is_mandatory : false }, + Child { name : "b".to_string(), is_mandatory : false }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + } diff --git a/module/core/former/tests/inc/mod.rs b/module/core/former/tests/inc/mod.rs index 52a5469e5a..77187d580d 100644 --- a/module/core/former/tests/inc/mod.rs +++ b/module/core/former/tests/inc/mod.rs @@ -7,91 +7,90 @@ mod former_tests #[ allow( unused_imports ) ] use super::*; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod container_former_common; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod container_former_vec; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod container_former_hashset; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod container_former_hashmap; - - mod a_basic_manual; - mod a_basic; - mod a_primitives_manual; - mod a_primitives; - - mod a_containers_without_subformer; - #[ cfg( not( feature = "no_std" ) ) ] - mod a_containers_with_subformer_manual; - #[ cfg( not( feature = "no_std" ) ) ] - mod a_containers_with_subformer; - - mod attribute_default_container; - mod attribute_default_primitive; - mod attribute_perform; - mod attribute_setter; - mod attribute_alias; - mod attribute_feature; - - mod string_slice_manual; - mod string_slice; - mod unsigned_primitive_types; - mod default_user_type; - mod user_type_no_default; - mod user_type_no_debug; - mod visibility; - - mod name_collision_former_hashmap_without_parameter; - mod name_collision_former_vector_without_parameter; - mod name_collisions; - mod name_collision_context; - mod name_collision_end; - mod name_collision_on_end; - - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod parametrized_struct_manual; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod parametrized_struct_imm; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod parametrized_struct_where; - mod parametrized_field; - mod parametrized_field_where; - - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod subformer_basic; - - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_container; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_container_manual; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_container_implicit; - - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_subform; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_subform_manual; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_subform_named; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_subform_named_manual; - +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// mod container_former_common; +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// mod container_former_vec; +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// mod container_former_hashset; +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// mod container_former_hashmap; +// +// mod a_basic_manual; +// mod a_basic; +// mod a_primitives_manual; +// mod a_primitives; +// +// mod a_containers_without_subformer; +// #[ cfg( not( feature = "no_std" ) ) ] +// mod a_containers_with_subformer_manual; +// #[ cfg( not( feature = "no_std" ) ) ] +// mod a_containers_with_subformer; +// +// mod attribute_default_container; +// mod attribute_default_primitive; +// mod attribute_perform; +// mod attribute_setter; +// mod attribute_alias; +// mod attribute_feature; +// +// mod string_slice_manual; +// mod string_slice; +// mod unsigned_primitive_types; +// mod default_user_type; +// mod user_type_no_default; +// mod user_type_no_debug; +// mod visibility; +// +// mod name_collision_former_hashmap_without_parameter; +// mod name_collision_former_vector_without_parameter; +// mod name_collisions; +// mod name_collision_context; +// mod name_collision_end; +// mod name_collision_on_end; +// +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// mod parametrized_struct_manual; +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// mod parametrized_struct_imm; +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// mod parametrized_struct_where; +// mod parametrized_field; +// mod parametrized_field_where; +// +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// mod subformer_basic; +// +// #[ cfg( any( not( feature = "no_std" ) ) ) ] +// mod subformer_container; +// #[ cfg( any( not( feature = "no_std" ) ) ) ] +// mod subformer_container_manual; +// #[ cfg( any( not( feature = "no_std" ) ) ) ] +// mod subformer_container_implicit; #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_subform_hashmap; + mod subformer_container_setter_off; + +// #[ cfg( any( not( feature = "no_std" ) ) ) ] +// mod subformer_subform; +// #[ cfg( any( not( feature = "no_std" ) ) ) ] +// mod subformer_subform_manual; +// #[ cfg( any( not( feature = "no_std" ) ) ) ] +// mod subformer_subform_named; +// #[ cfg( any( not( feature = "no_std" ) ) ) ] +// mod subformer_subform_named_manual; #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_subform_hashmap_explicit; + mod subformer_subform_setter_off; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_setter_off_subform; +// #[ cfg( any( not( feature = "no_std" ) ) ) ] +// mod subformer_subform_hashmap; +// #[ cfg( any( not( feature = "no_std" ) ) ) ] +// mod subformer_subform_hashmap_explicit; // #[ cfg( any( not( feature = "no_std" ) ) ) ] - // mod subformer_setter_off_container; - - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_subform_and_container; - #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_subform_and_container_parametrized; + // mod subformer_subform_and_container; + // #[ cfg( any( not( feature = "no_std" ) ) ) ] + // mod subformer_subform_and_container_parametrized; + // xxx } #[ cfg( feature = "derive_components" ) ] @@ -126,39 +125,40 @@ mod components_tests } -only_for_terminal_module! -{ - - // stable have different information about error - // that's why these tests are active only for nightly - #[ test_tools::nightly ] - #[ test ] - fn former_trybuild() - { - - println!( "current_dir : {:?}", std::env::current_dir().unwrap() ); - let t = test_tools::compiletime::TestCases::new(); - - // zzz : uncomment - t.compile_fail( "tests/inc/compiletime/former_bad_attr.rs" ); - t.pass( "tests/inc/compiletime/former_hashmap_without_parameter.rs" ); - t.pass( "tests/inc/compiletime/former_vector_without_parameter.rs" ); - - } - - // stable have different information about error - // that's why these tests are active only for nightly - #[ test_tools::nightly ] - #[ test ] - fn components_trybuild() - { - - println!( "current_dir : {:?}", std::env::current_dir().unwrap() ); - let _t = test_tools::compiletime::TestCases::new(); - - // zzz : make it working test - //t.run( "tests/inc/compiletime/components_component_from_debug.rs" ); - - } - -} +// only_for_terminal_module! +// { +// +// // stable have different information about error +// // that's why these tests are active only for nightly +// #[ test_tools::nightly ] +// #[ test ] +// fn former_trybuild() +// { +// +// println!( "current_dir : {:?}", std::env::current_dir().unwrap() ); +// let t = test_tools::compiletime::TestCases::new(); +// +// // zzz : uncomment +// t.compile_fail( "tests/inc/compiletime/former_bad_attr.rs" ); +// t.pass( "tests/inc/compiletime/former_hashmap_without_parameter.rs" ); +// t.pass( "tests/inc/compiletime/former_vector_without_parameter.rs" ); +// +// } +// +// // stable have different information about error +// // that's why these tests are active only for nightly +// #[ test_tools::nightly ] +// #[ test ] +// fn components_trybuild() +// { +// +// println!( "current_dir : {:?}", std::env::current_dir().unwrap() ); +// let _t = test_tools::compiletime::TestCases::new(); +// +// // zzz : make it working test +// //t.run( "tests/inc/compiletime/components_component_from_debug.rs" ); +// +// } +// +// } +// xxx \ No newline at end of file diff --git a/module/core/former_meta/src/derive/former.rs b/module/core/former_meta/src/derive/former.rs index 0d2ba744a1..00e4445807 100644 --- a/module/core/former_meta/src/derive/former.rs +++ b/module/core/former_meta/src/derive/former.rs @@ -8,7 +8,7 @@ use proc_macro2::TokenStream; /// Definition of a field. /// -#[ allow( dead_code ) ] + struct FormerField< 'a > { pub attrs : Attributes, @@ -25,7 +25,29 @@ struct FormerField< 'a > impl< 'a > FormerField< 'a > { - /// Get name of setter for subform. + /// Get name of setter for container if such setter should be generated. + pub fn container_setter_name( &self ) -> Option< &syn::Ident > + { + + if let Some( ref attr ) = self.attrs.container + { + if attr.setter.is_none() || attr.setter.unwrap() + { + if let Some( ref name ) = attr.name + { + return Some( &name ) + } + else + { + return Some( &self.ident ) + } + } + } + + return None; + } + + /// Get name of setter for subform if such setter should be generated. pub fn subform_setter_name( &self ) -> Option< &syn::Ident > { @@ -61,8 +83,7 @@ impl< 'a > FormerField< 'a > explicit = true; } - // xxx : container setter also could have custom name - if let Some( ref _attr ) = self.attrs.container + if self.attrs.container.is_some() && !explicit { return false; } @@ -72,22 +93,6 @@ impl< 'a > FormerField< 'a > return false; } - // let subform_name = self.subform_setter_name(); - // if let Some( name ) = subform_name - // { - // if self.ident == name - // { - // return false; - // } - // else - // { - // if !explicit - // { - // return false; - // } - // } - // } - return true; } @@ -162,7 +167,7 @@ impl Attributes { container.replace( syn::parse2::< AttributeContainer >( Default::default() )? ); }, - _ => return_syn_err!( attr, "Expects an attribute of format #[ container( former::VectorDefinition ) ] or #[ container ] if you want to use default container defition, but got:\n {}", qt!{ #attr } ), + _ => return_syn_err!( attr, "Expects an attribute of format #[ container ] or #[ container( definition = former::VectorDefinition ) ] if you want to use default container defition, but got:\n {}", qt!{ #attr } ), } } "subform" => @@ -210,7 +215,7 @@ impl Attributes /// `#[ perform( fn after1< 'a >() -> Option< &'a str > ) ]` /// -#[ allow( dead_code ) ] + struct AttributeFormAfter { // paren_token : syn::token::Paren, @@ -237,7 +242,7 @@ impl syn::parse::Parse for AttributeFormAfter /// `#[ default( 13 ) ]` /// -#[ allow( dead_code ) ] + struct AttributeDefault { // eq_token : syn::Token!{ = }, @@ -266,7 +271,7 @@ impl syn::parse::Parse for AttributeDefault /// `#[ scalar_setter( false ) ]` /// -#[ allow( dead_code ) ] + struct AttributeScalarSetter { // paren_token : syn::token::Paren, @@ -288,7 +293,8 @@ impl syn::parse::Parse for AttributeScalarSetter } /// -/// Attribute to enable/disable former generation. +/// Attribute to enable/disable/customize container setter generation. +/// /// Also known as subformers, used for aggregation relationship, when a struct holds another struct, which needs to be build by invoking multiple methods /// Typical example is a struct holding a `Vec` /// @@ -296,24 +302,93 @@ impl syn::parse::Parse for AttributeScalarSetter /// // qqq : update documentation -#[ allow( dead_code ) ] struct AttributeContainer { + /// Optional identifier for naming the setter. + name : Option< syn::Ident >, + /// Controls the generation of a setter method. If false, a setter method is not generated. + setter : Option< bool >, + /// Definition of the container former to use, e.g., `former::VectorSubformer`. expr : Option< syn::Type >, } impl syn::parse::Parse for AttributeContainer { - fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > { - let expr : Option< syn::Type > = input.parse().ok(); - Ok( Self + let mut name : Option< syn::Ident > = None; + let mut setter : Option< bool > = None; // Default is to generate a setter + let mut expr : Option< syn::Type > = None; + + while !input.is_empty() { - expr, - }) + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Ident ) + { + let ident : syn::Ident = input.parse()?; + if ident == "name" + { + input.parse::< syn::Token![ = ] >()?; + name = Some( input.parse()? ); + } + else if ident == "setter" + { + input.parse::< syn::Token![ = ] >()?; + let value : syn::LitBool = input.parse()?; + setter = Some( value.value ); + } + else if ident == "definition" + { + input.parse::< syn::Token![ = ] >()?; + expr = Some( input.parse()? ); + } + else + { + return Err( lookahead.error() ); + } + } + else + { + return Err( lookahead.error() ); + } + + // Optional comma handling + if input.peek( syn::Token![ , ] ) + { + input.parse::< syn::Token![ , ] >()?; + } + } + + Ok( Self { name, setter, expr } ) } } +// +// struct AttributeContainer +// { +// // /// An optional identifier that names the setter. It is parsed from inputs +// // /// like `name = my_field`. +// // name : Option< syn::Ident >, +// // /// Disable generation of setter. +// // /// It still generate `_field_add` method, so it could be used to make a setter with custom arguments. +// // setter : bool, +// /// Definition of container former to use. +// /// Look [`former::ContainerSubformer`] and [`former::VectorDefinition`]. +// expr : Option< syn::Type >, // xxx : rename +// } +// +// impl syn::parse::Parse for AttributeContainer +// { +// fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > +// { +// let expr : Option< syn::Type > = input.parse().ok(); +// Ok( Self +// { +// expr, +// }) +// } +// } + /// Represents a subform attribute with optional name flag. /// Used to specify extra options for using one former as subformer of another one. /// For example name of setter could be customized. @@ -331,10 +406,10 @@ impl syn::parse::Parse for AttributeContainer struct AttributeSubform { - /// - `name` : An optional identifier that names the subform. It is parsed from inputs - /// like `name = my_field`. + /// An optional identifier that names the setter. It is parsed from inputs + /// like `name = my_field`. name : Option< syn::Ident >, - /// - `setter` : Disable generation of setter. + /// Disable generation of setter. /// It still generate `_field_add` method, so it could be used to make a setter with custom arguments. setter : bool, } @@ -392,7 +467,7 @@ impl syn::parse::Parse for AttributeSubform /// `#[ alias( name ) ]` /// -#[ allow( dead_code ) ] + struct AttributeAlias { // paren_token : syn::token::Paren, @@ -956,47 +1031,55 @@ fn field_container_setter } }; - let setter2 = if params.len() > 1 + let setter_name = field.container_setter_name(); + let setter2 = if let Some( setter_name ) = setter_name { - qt! + if params.len() > 1 { - - #[ doc = #doc ] - #[ inline( always ) ] - pub fn #field_ident( self ) -> - former::ContainerSubformer:: - < - ( #( #params, )* ), #subformer_definition - > + qt! { - self.#field_assign::< former::ContainerSubformer:: + + #[ doc = #doc ] + #[ inline( always ) ] + pub fn #setter_name( self ) -> + former::ContainerSubformer:: < ( #( #params, )* ), #subformer_definition - >>() - } + > + { + self.#field_assign::< former::ContainerSubformer:: + < + ( #( #params, )* ), #subformer_definition + > >() + } + } } - } - else - { - qt! + else { - - #[ doc = #doc ] - #[ inline( always ) ] - pub fn #field_ident( self ) -> - former::ContainerSubformer:: - < - #( #params, )* #subformer_definition - > + qt! { - self.#field_assign::< former::ContainerSubformer:: + + #[ doc = #doc ] + #[ inline( always ) ] + pub fn #setter_name( self ) -> + former::ContainerSubformer:: < #( #params, )* #subformer_definition - >>() - } + > + { + self.#field_assign::< former::ContainerSubformer:: + < + #( #params, )* #subformer_definition + > >() + } + } } + } + else + { + qt!{} }; qt!