diff --git a/Cargo.toml b/Cargo.toml index d55e9210ad..b7e12b0f02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -200,12 +200,17 @@ path = "module/core/for_each" default-features = false [workspace.dependencies.former] -version = "~0.16.0" +version = "~1.0.0" path = "module/core/former" default-features = false +[workspace.dependencies.former_stable] +package = "former" +version = "=0.15.0" +default-features = false + [workspace.dependencies.former_meta] -version = "~0.14.0" +version = "~1.0.0" path = "module/core/former_meta" default-features = false @@ -324,6 +329,7 @@ default-features = false version = "~0.2.0" path = "module/alias/wstring_tools" + ## fs tools / path tools [workspace.dependencies.fs_tools] @@ -349,6 +355,11 @@ version = "~0.3.0" path = "module/core/process_tools" default-features = false +[workspace.dependencies.process_tools_published] +package = "process_tools" +version = "~0.2.0" +default-features = false + ## test diff --git a/module/blank/rustql/Cargo.toml b/module/blank/rustql/Cargo.toml new file mode 100644 index 0000000000..8d24519fb1 --- /dev/null +++ b/module/blank/rustql/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "rustql" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/rustql" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/rustql" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/rustql" +description = """ +Rust query language. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/rustql/License b/module/blank/rustql/License new file mode 100644 index 0000000000..6d5ef8559f --- /dev/null +++ b/module/blank/rustql/License @@ -0,0 +1,22 @@ +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/blank/rustql/Readme.md b/module/blank/rustql/Readme.md new file mode 100644 index 0000000000..162de54d71 --- /dev/null +++ b/module/blank/rustql/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: rustql +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/ModulerustqlPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/ModulerustqlPush.yml) [![docs.rs](https://img.shields.io/docsrs/rustql?color=e3e8f0&logo=docs.rs)](https://docs.rs/rustql) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Rust query language. + + diff --git a/module/blank/rustql/src/lib.rs b/module/blank/rustql/src/lib.rs new file mode 100644 index 0000000000..e8dceed08a --- /dev/null +++ b/module/blank/rustql/src/lib.rs @@ -0,0 +1,11 @@ +#![ 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/rustql/latest/rustql/" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/rustql/tests/inc/basic_test.rs b/module/blank/rustql/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/rustql/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/rustql/tests/inc/mod.rs b/module/blank/rustql/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/rustql/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/rustql/tests/smoke_test.rs b/module/blank/rustql/tests/smoke_test.rs new file mode 100644 index 0000000000..7fd288e61d --- /dev/null +++ b/module/blank/rustql/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/blank/rustql/tests/tests.rs b/module/blank/rustql/tests/tests.rs new file mode 100644 index 0000000000..5a21773934 --- /dev/null +++ b/module/blank/rustql/tests/tests.rs @@ -0,0 +1,10 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use rustql as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/core/clone_dyn/tests/inc/mod.rs b/module/core/clone_dyn/tests/inc/mod.rs index 6477b35c67..99cee0a3c6 100644 --- a/module/core/clone_dyn/tests/inc/mod.rs +++ b/module/core/clone_dyn/tests/inc/mod.rs @@ -119,7 +119,7 @@ tests_impls! #[ clone_dyn ] trait Trait2< T1 : Copy, T2 : Copy > where - T2 : Clone + std::fmt::Debug, + T2 : Clone + core::fmt::Debug, { } diff --git a/module/core/clone_dyn_meta/src/derive.rs b/module/core/clone_dyn_meta/src/derive.rs index 883d7b9bf6..023e852b39 100644 --- a/module/core/clone_dyn_meta/src/derive.rs +++ b/module/core/clone_dyn_meta/src/derive.rs @@ -16,7 +16,7 @@ pub fn clone_dyn( _attr : proc_macro::TokenStream, item : proc_macro::TokenStrea let name_ident = &item_parsed.ident; // let generics = &item_parsed.generics; let generics_analyzed = item_parsed.generics_analyze(); - let generics_params = &generics_analyzed.generics.params; + let generic_params = &generics_analyzed.generics.params; let generics_where = &generics_analyzed.generics.where_clause; let generics_names = &generics_analyzed.names; @@ -25,7 +25,7 @@ pub fn clone_dyn( _attr : proc_macro::TokenStream, item : proc_macro::TokenStrea #item_parsed #[ allow( non_local_definitions ) ] - impl < 'c, #generics_params > Clone + impl < 'c, #generic_params > Clone for Box< dyn #name_ident< #( #generics_names ),* > + 'c > // where #generics_where @@ -35,7 +35,7 @@ pub fn clone_dyn( _attr : proc_macro::TokenStream, item : proc_macro::TokenStrea } #[ allow( non_local_definitions ) ] - impl < 'c, #generics_params > Clone + impl < 'c, #generic_params > Clone for Box< dyn #name_ident< #( #generics_names ),* > + Send + 'c > // where #generics_where @@ -45,7 +45,7 @@ pub fn clone_dyn( _attr : proc_macro::TokenStream, item : proc_macro::TokenStrea } #[ allow( non_local_definitions ) ] - impl < 'c, #generics_params > Clone + impl < 'c, #generic_params > Clone for Box< dyn #name_ident< #( #generics_names ),* > + Sync + 'c > // where #generics_where @@ -55,7 +55,7 @@ pub fn clone_dyn( _attr : proc_macro::TokenStream, item : proc_macro::TokenStrea } #[ allow( non_local_definitions ) ] - impl < 'c, #generics_params > Clone + impl < 'c, #generic_params > Clone for Box< dyn #name_ident< #( #generics_names ),* > + Send + Sync + 'c > // where #generics_where diff --git a/module/core/collection_tools/Cargo.toml b/module/core/collection_tools/Cargo.toml index 4cc1addaca..197d4e2ca2 100644 --- a/module/core/collection_tools/Cargo.toml +++ b/module/core/collection_tools/Cargo.toml @@ -57,7 +57,7 @@ collection_constructors = [] collection_into_constructors = [] # STD collection for no_std. collection_std = [] - +# qqq : is this feature used? seems not. if yes, what is it responsible for? discuss [dependencies] diff --git a/module/core/collection_tools/Readme.md b/module/core/collection_tools/Readme.md index 8d2e831832..07893f301d 100644 --- a/module/core/collection_tools/Readme.md +++ b/module/core/collection_tools/Readme.md @@ -94,6 +94,10 @@ Instead of Click to see ```rust +# #[ cfg( all( feature = "enabled", feature = "collection_std" ) ) ] +# #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] +# { + #[ cfg( feature = "use_alloc" ) ] use hashbrown::HashSet; // a `no_std` replacement for `HashSet` #[ cfg( not( feature = "no_std" ) ) ] @@ -102,6 +106,8 @@ use std::collections::HashSet; let mut vec : HashSet< i32 > = HashSet::new(); vec.insert( 1 ); assert_eq!( vec.contains( &1 ), true ); + +# } ``` diff --git a/module/core/collection_tools/src/constructors.rs b/module/core/collection_tools/src/constructors.rs index c96770bd06..1ea42a5e62 100644 --- a/module/core/collection_tools/src/constructors.rs +++ b/module/core/collection_tools/src/constructors.rs @@ -199,7 +199,7 @@ macro_rules! heap /// Creates a `HashMap` from a list of key-value pairs. /// /// The `hmap` macro allows for convenient creation of a `HashMap` with initial elements. -/// +/// /// # Origin /// /// This collection can be reexported from different crates: @@ -276,9 +276,9 @@ macro_rules! hmap /// Creates a `HashSet` from a list of elements. /// /// The `hset` macro allows for convenient creation of a `HashSet` with initial elements. -/// +/// /// # Origin -/// +/// /// This collection can be reexported from different crates: /// - from `std`, if `no_std` flag if off /// - from `hashbrown`, if `use_alloc` flag if on @@ -354,7 +354,7 @@ macro_rules! hset /// /// The `list` macro facilitates the creation of a `LinkedList` with initial elements. /// -/// +/// /// # Origin /// /// This collection is reexported from `alloc`. diff --git a/module/core/collection_tools/src/lib.rs b/module/core/collection_tools/src/lib.rs index d87f6782ef..220d3689a6 100644 --- a/module/core/collection_tools/src/lib.rs +++ b/module/core/collection_tools/src/lib.rs @@ -4,6 +4,35 @@ #![ doc( html_root_url = "https://docs.rs/collection_tools/latest/collection_tools/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] +// qqq : make subdirectory for each container + +// qqq : move out of lib.rs file +/// Not meant to be called directly. +#[ doc( hidden ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! count +{ + ( @single $( $x : tt )* ) => ( () ); + + ( + @count $( $rest : expr ),* + ) + => + ( + < [ () ] >::len( &[ $( count!( @single $rest ) ),* ] ) + ); +} + +/// Macros to construct the collections. +/// Basically a tweaked version of `literally` crate but using `alloc` / `hashbrown` instead of `std` +#[ cfg( all( feature = "enabled", feature = "collection_constructors" ) ) ] +pub mod constructors; + +/// Macros to construct the collections, using `.into()` under the hood. +/// Often requires explicitly specifying type to cast to. +#[ cfg( all( feature = "enabled", feature = "collection_into_constructors" ) ) ] +pub mod into_constructors; + /// Namespace with dependencies. #[ cfg( feature = "enabled" ) ] pub mod dependency @@ -28,21 +57,30 @@ pub mod protected #[ allow( unused_imports ) ] pub use super::orphan::*; + // #[ cfg( feature = "use_alloc" ) ] extern crate alloc; + + // #[ cfg( feature = "use_alloc" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use alloc::vec::Vec; + + // #[ cfg( feature = "use_alloc" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use alloc::collections::{ BinaryHeap, BTreeMap, BTreeSet, LinkedList, VecDeque }; + + // qqq : what is comnination `use_alloc` + !`no_std` #[ cfg( feature = "use_alloc" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use hashbrown::{ HashMap, HashSet }; + #[ cfg( not( feature = "no_std" ) ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use std::collections::{ HashMap, HashSet }; + } /// Parented namespace of the module. @@ -76,30 +114,3 @@ pub mod prelude #[ allow( unused_imports ) ] pub use super::into_constructors::*; } - -/// Not meant to be called directly. -#[ doc( hidden ) ] -#[ macro_export( local_inner_macros ) ] -macro_rules! count -{ - ( @single $( $x : tt )* ) => ( () ); - - ( - @count $( $rest : expr ),* - ) - => - ( - < [ () ] >::len( &[ $( count!( @single $rest ) ),* ] ) - ); -} - - -/// Macros to construct the collections. -/// Basically a tweaked version of `literally` crate but using `alloc` / `hashbrown` instead of `std` -#[ cfg( all( feature = "enabled", feature = "collection_constructors" ) ) ] -pub mod constructors; - -/// Macros to construct the collections, using `.into()` under the hood. -/// Often requires explicitly specifying type to cast to. -#[ cfg( all( feature = "enabled", feature = "collection_into_constructors" ) ) ] -pub mod into_constructors; diff --git a/module/core/collection_tools/src/vec.rs b/module/core/collection_tools/src/vec.rs new file mode 100644 index 0000000000..f4e6502089 --- /dev/null +++ b/module/core/collection_tools/src/vec.rs @@ -0,0 +1,2 @@ + +pub use core::slice::Iter diff --git a/module/core/collection_tools/tests/inc/components.rs b/module/core/collection_tools/tests/inc/components.rs new file mode 100644 index 0000000000..ee19c86a6c --- /dev/null +++ b/module/core/collection_tools/tests/inc/components.rs @@ -0,0 +1,55 @@ +#[ allow( unused_imports ) ] +use super::*; + +// + +// qqq : implement similar test for all containers +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +#[ test ] +fn vec_iters() +{ + + struct MyContainer + { + entries : Vec< i32 >, + } + + impl IntoIterator for MyContainer + { + type Item = i32; + type IntoIter = std::vec::IntoIter< i32 >; + // type IntoIter = the_module::vec::IntoIter< i32 >; + // qqq : should work + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.into_iter() // Create an iterator from the internal HashSet. + } + } + + impl< 'a > IntoIterator for &'a MyContainer + { + type Item = &'a i32; + type IntoIter = std::slice::Iter< 'a, i32 >; + // type IntoIter = the_module::vec::Iter< 'a, i32 >; + // qqq : should work + + fn into_iter( self ) -> Self::IntoIter + { + self.entries.iter() // Borrow the elements via an iterator. + } + } + + let instance = MyContainer { entries : vec![ 1, 2, 3 ] }; + let got : Vec< _ > = ( &instance ).into_iter().cloned().collect(); + let exp = vec![ 1, 2, 3 ]; + a_id!( got, exp ); + + let instance = MyContainer { entries : vec![ 1, 2, 3 ] }; + let got : Vec< _ > = instance.into_iter().collect(); + let exp = vec![ 1, 2, 3 ]; + a_id!( got, exp ); + +} + +// qqq : implement VectorInterface diff --git a/module/core/collection_tools/tests/inc/constructors.rs b/module/core/collection_tools/tests/inc/constructors.rs index f910b900aa..dda241a1a4 100644 --- a/module/core/collection_tools/tests/inc/constructors.rs +++ b/module/core/collection_tools/tests/inc/constructors.rs @@ -137,7 +137,7 @@ fn vec() // test.case( "empty" ); let got : the_module::Vec< i32 > = the_module::vec!{}; - let exp = the_module::Vec::new(); + let exp = the_module::Vec::< i32 >::new(); assert_eq!( got, exp ); // test.case( "multiple entry" ); diff --git a/module/core/collection_tools/tests/inc/into_constructors.rs b/module/core/collection_tools/tests/inc/into_constructors.rs index bce1d6fc8b..7423159092 100644 --- a/module/core/collection_tools/tests/inc/into_constructors.rs +++ b/module/core/collection_tools/tests/inc/into_constructors.rs @@ -1,3 +1,5 @@ +// xxx : uncomment + #[ allow( unused_imports ) ] use super::*; @@ -52,7 +54,7 @@ fn binary_heap() // test.case( "empty" ); let got : the_module::BinaryHeap< i32 > = the_module::into_heap!{}; - let exp = the_module::BinaryHeap::new(); + let exp = the_module::BinaryHeap::< i32 >::new(); assert_eq!( got.into_vec(), exp.into_vec() ); // test.case( "multiple entry" ); @@ -137,7 +139,7 @@ fn vec() // test.case( "empty" ); let got : the_module::Vec< i32 > = the_module::into_vec!{}; - let exp = the_module::Vec::new(); + let exp = the_module::Vec::< i32 >::new(); assert_eq!( got, exp ); // test.case( "multiple entry" ); diff --git a/module/core/collection_tools/tests/inc/mod.rs b/module/core/collection_tools/tests/inc/mod.rs index 843c19925e..00cc188bc4 100644 --- a/module/core/collection_tools/tests/inc/mod.rs +++ b/module/core/collection_tools/tests/inc/mod.rs @@ -9,3 +9,8 @@ mod constructors; #[ cfg( any( feature = "collection_std" ) ) ] mod reexport; + +mod components; + +// qqq : make subdirectory for each container +// qqq : don't put tests otsude of directory `inc` diff --git a/module/core/collection_tools/tests/nostd/constructors.rs b/module/core/collection_tools/tests/nostd/constructors.rs deleted file mode 100644 index be3df1768d..0000000000 --- a/module/core/collection_tools/tests/nostd/constructors.rs +++ /dev/null @@ -1,171 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn b_tree_map() -{ - - // test.case( "empty" ); - let got : the_module::BTreeMap< i32, i32 > = the_module::bmap!{}; - let exp = the_module::BTreeMap::new(); - assert_eq!( got, exp ); - - // test.case( "multiple entry" ); - let got = the_module::bmap!{ 3 => 13, 4 => 1 }; - let mut exp = the_module::BTreeMap::new(); - exp.insert(3, 13); - exp.insert(4, 1); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn b_tree_set() -{ - - // test.case( "empty" ); - let got : the_module::BTreeSet< i32 > = the_module::bset!{}; - let exp = the_module::BTreeSet::new(); - assert_eq!( got, exp ); - - // test.case( "multiple entry" ); - let got = the_module::bset!{ 3, 13 }; - let mut exp = the_module::BTreeSet::new(); - exp.insert(3); - exp.insert(13); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn binary_heap() -{ - - // test.case( "empty" ); - let got : the_module::BinaryHeap< i32 > = the_module::heap!{}; - let exp = the_module::BinaryHeap::new(); - assert_eq!( got.into_vec(), exp.into_vec() ); - - // test.case( "multiple entry" ); - let got = the_module::heap!{ 3, 13 }; - let mut exp = the_module::BinaryHeap::new(); - exp.push(3); - exp.push(13); - assert_eq!( got.into_sorted_vec(), exp.into_sorted_vec() ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn hash_map() -{ - - // test.case( "empty" ); - let got : the_module::HashMap< i32, i32 > = the_module::hmap!{}; - let exp = the_module::HashMap::new(); - assert_eq!( got, exp ); - - - // test.case( "multiple entry" ); - let got = the_module::hmap!{ 3 => 13, 4 => 1 }; - let mut exp = the_module::HashMap::new(); - exp.insert( 3, 13 ); - exp.insert( 4, 1 ); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn hash_set() -{ - - // test.case( "empty" ); - let got : the_module::HashSet< i32 > = the_module::hset!{}; - let exp = the_module::HashSet::new(); - assert_eq!( got, exp ); - - // test.case( "multiple entry" ); - let got = the_module::hset!{ 13, 11 }; - let mut exp = the_module::HashSet::new(); - exp.insert( 11 ); - exp.insert( 13 ); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn linked_list() -{ - - // test.case( "empty" ); - let got : the_module::LinkedList< i32 > = the_module::list!{}; - let exp = the_module::LinkedList::new(); - assert_eq!( got, exp ); - - // test.case( "multiple entry" ); - let got = the_module::list!{ 13, 15 }; - let mut exp = the_module::LinkedList::new(); - exp.push_front( 15 ); - exp.push_front( 13 ); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn vec() -{ - - // test.case( "empty" ); - let got : the_module::Vec< i32 > = the_module::vec!{}; - let exp = the_module::Vec::new(); - assert_eq!( got, exp ); - - // test.case( "multiple entry" ); - let got = the_module::vec!{ 3, 13 }; - let mut exp = the_module::Vec::new(); - exp.push( 3 ); - exp.push( 13 ); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn vec_deque() -{ - - // test.case( "empty" ); - let got : the_module::VecDeque< i32 > = the_module::vecd!{}; - let exp = the_module::VecDeque::new(); - assert_eq!( got, exp ); - - // test.case( "multiple entry" ); - let got = the_module::vecd!{ 3, 13 }; - let mut exp = the_module::VecDeque::new(); - exp.push_front( 13 ); - exp.push_front( 3 ); - assert_eq!( got, exp ); - -} diff --git a/module/core/collection_tools/tests/nostd/into_constructors.rs b/module/core/collection_tools/tests/nostd/into_constructors.rs deleted file mode 100644 index 19569ef6e7..0000000000 --- a/module/core/collection_tools/tests/nostd/into_constructors.rs +++ /dev/null @@ -1,168 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn b_tree_map() -{ - - // test.case( "empty" ); - let got : the_module::BTreeMap< i32, i32 > = the_module::into_bmap!{}; - let exp : the_module::BTreeMap< i32, i32 > = the_module::BTreeMap::new(); - assert_eq!( got, exp ); - - // test.case( "single entry" ); - let got = the_module::into_bmap!{ 3 => 13 }; - let mut exp = the_module::BTreeMap::new(); - exp.insert(3, 13); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn b_tree_set() -{ - - // test.case( "empty" ); - let got : the_module::BTreeSet< i32 > = the_module::into_bset!{}; - let exp : the_module::BTreeSet< i32 > = the_module::BTreeSet::new(); - assert_eq!( got, exp ); - - // test.case( "single entry" ); - let got = the_module::into_bset!{ 3, 13 }; - let mut exp = the_module::BTreeSet::new(); - exp.insert(3); - exp.insert(13); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn binary_heap() -{ - - // test.case( "empty" ); - let got : the_module::BinaryHeap< i32 > = the_module::into_heap!{}; - let exp : the_module::BinaryHeap< i32 > = the_module::BinaryHeap::new(); - assert_eq!( got.into_vec(), exp.into_vec() ); - - // test.case( "single entry" ); - let got: the_module::BinaryHeap< i32 > = the_module::into_heap!{ 3, 13 }; - let mut exp = the_module::BinaryHeap::new(); - exp.push(3); - exp.push(13); - assert_eq!( got.into_sorted_vec(), exp.into_sorted_vec() ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn hash_map() -{ - - // test.case( "empty" ); - let got : the_module::HashMap< i32, i32 > = the_module::into_hmap!{}; - let exp = the_module::HashMap::new(); - assert_eq!( got, exp ); - - - // test.case( "single entry" ); - let got = the_module::into_hmap!{ 3 => 13 }; - let mut exp = the_module::HashMap::new(); - exp.insert( 3, 13 ); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn hash_set() -{ - - // test.case( "empty" ); - let got : the_module::HashSet< i32 > = the_module::into_hset!{}; - let exp = the_module::HashSet::new(); - assert_eq!( got, exp ); - - // test.case( "single entry" ); - let got = the_module::into_hset!{ 13 }; - let mut exp = the_module::HashSet::new(); - exp.insert( 13 ); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn linked_list() -{ - - // test.case( "empty" ); - let got : the_module::LinkedList< i32 > = the_module::into_list!{}; - let exp = the_module::LinkedList::new(); - assert_eq!( got, exp ); - - // test.case( "single entry" ); - let got = the_module::into_list!{ 13, 15 }; - let mut exp = the_module::LinkedList::new(); - exp.push_front( 15 ); - exp.push_front( 13 ); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn vec() -{ - - // test.case( "empty" ); - let got : the_module::Vec< i32 > = the_module::into_vec!{}; - let exp : the_module::Vec< i32 > = the_module::Vec::new(); - assert_eq!( got, exp ); - - // test.case( "single entry" ); - let got : the_module::Vec< i32 > = the_module::into_vec!{ 3, 13 }; - let mut exp = the_module::Vec::new(); - exp.push( 3 ); - exp.push( 13 ); - assert_eq!( got, exp ); - -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ test ] -fn vec_deque() -{ - - // test.case( "empty" ); - let got : the_module::VecDeque< i32 > = the_module::into_vecd!{}; - let exp = the_module::VecDeque::new(); - assert_eq!( got, exp ); - - // test.case( "single entry" ); - let got : the_module::VecDeque< i32 > = the_module::into_vecd!{ 3, 13 }; - let mut exp = the_module::VecDeque::new(); - exp.push_front( 13 ); - exp.push_front( 3 ); - assert_eq!( got, exp ); - -} diff --git a/module/core/collection_tools/tests/nostd/mod.rs b/module/core/collection_tools/tests/nostd/mod.rs deleted file mode 100644 index c83f7f2779..0000000000 --- a/module/core/collection_tools/tests/nostd/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -#[ cfg( any( feature = "collection_constructors" ) ) ] -mod constructors; - -// aaa : xxx : does not work for `use_alloc`, make it working -- Made by switching from std collections to alloc / hashbrown -// #[ cfg( not( feature = "use_alloc" ) ) ] -#[ cfg( any( feature = "collection_into_constructors" ) ) ] -mod into_constructors; - -#[ cfg( any( feature = "collection_std" ) ) ] -mod reexport; diff --git a/module/core/collection_tools/tests/nostd/reexport.rs b/module/core/collection_tools/tests/nostd/reexport.rs deleted file mode 100644 index 000c6bc3fd..0000000000 --- a/module/core/collection_tools/tests/nostd/reexport.rs +++ /dev/null @@ -1,105 +0,0 @@ -use super::*; - -// - -#[ test ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -fn b_tree_map() -{ - let mut map : the_module::BTreeMap< i32, i32 > = the_module::BTreeMap::new(); - map.insert( 1, 2 ); - let exp = 2; - let got = *map.get( &1 ).unwrap(); - assert_eq!( exp, got ); -} - -// - -#[ test ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -fn b_tree_set() -{ - let mut map : the_module::BTreeSet< i32 > = the_module::BTreeSet::new(); - map.insert( 1 ); - assert_eq!( map.contains( &1 ), true ); - assert_eq!( map.contains( &2 ), false ); -} - -// - -#[ test ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -fn binary_heap() -{ - let mut map : the_module::BinaryHeap< i32 > = the_module::BinaryHeap::new(); - map.push( 1 ); - let exp = Some(1).as_ref(); - let got = map.peek(); - assert_eq!( exp, got ); -} - -// - -#[ test ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -fn hash_map() -{ - let mut map : the_module::HashMap< i32, i32 > = the_module::HashMap::new(); - map.insert( 1, 2 ); - let exp = 2; - let got = *map.get( &1 ).unwrap(); - assert_eq!( exp, got ); -} - -// - -#[ test ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -fn hash_set() -{ - let mut map : the_module::HashSet< i32 > = the_module::HashSet::new(); - map.insert( 1 ); - assert_eq!( map.contains( &1 ), true ); - assert_eq!( map.contains( &2 ), false ); -} - -// - -#[ test ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -fn linked_list() -{ - let mut map : the_module::LinkedList< i32 > = the_module::LinkedList::new(); - map.push_back( 1 ); - assert_eq!( map.contains( &1 ), true ); - assert_eq!( map.contains( &2 ), false ); -} - -// - -#[ test ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -fn vec() -{ - - let mut map : the_module::Vec< i32 > = the_module::Vec::new(); - map.push( 1 ); - map.push( 2 ); - let got = map.first().unwrap().clone(); - assert_eq!( got, 1 ); - let got = map.last().unwrap().clone(); - assert_eq!( got, 2 ); - -} - -// - -#[ test ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -fn vec_deque() -{ - let mut map : the_module::VecDeque< i32 > = the_module::VecDeque::new(); - map.push_back( 1 ); - assert_eq!( map.contains( &1 ), true ); - assert_eq!( map.contains( &2 ), false ); -} diff --git a/module/core/collection_tools/tests/nostd_tests.rs b/module/core/collection_tools/tests/nostd_tests.rs deleted file mode 100644 index d22a19e7b1..0000000000 --- a/module/core/collection_tools/tests/nostd_tests.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![ cfg_attr( feature = "no_std", no_std ) ] -// tests without std - -#[ allow( unused_imports ) ] -use ::collection_tools as the_module; -// #[ allow( unused_imports ) ] -// use test_tools::exposed::*; -#[ path="../../../../module/step/meta/src/module/aggregating.rs" ] -mod aggregating; - -mod nostd; -// aaa : enable \ No newline at end of file diff --git a/module/core/collection_tools/tests/tests.rs b/module/core/collection_tools/tests/tests.rs index 00689894e0..b84a5dc030 100644 --- a/module/core/collection_tools/tests/tests.rs +++ b/module/core/collection_tools/tests/tests.rs @@ -1,11 +1,12 @@ // usual tests -#[ allow( unused_imports ) ] -use ::collection_tools as the_module; -// #[ allow( unused_imports ) ] -// use test_tools::exposed::*; #[ path="../../../../module/step/meta/src/module/aggregating.rs" ] mod aggregating; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ allow( unused_imports ) ] +use ::collection_tools as the_module; + mod inc; -// aaa diff --git a/module/core/former/Cargo.toml b/module/core/former/Cargo.toml index cf6e7e15f5..09489d9b38 100644 --- a/module/core/former/Cargo.toml +++ b/module/core/former/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former" -version = "0.16.0" +version = "1.0.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -59,7 +59,7 @@ derive_from_components = [ "derive_components", "former_meta/derive_from_compone [dependencies] former_meta = { workspace = true } -collection_tools = { workspace = true, features = [ "collection_std" ] } +collection_tools = { workspace = true, features = [ "collection_std", "collection_constructors" ] } [dev-dependencies] diff --git a/module/core/former/Readme.md b/module/core/former/Readme.md index 954789bb59..35aea33b92 100644 --- a/module/core/former/Readme.md +++ b/module/core/former/Readme.md @@ -19,44 +19,42 @@ It offers specialized subformers for common Rust collections like `Vec`, `HashMa This approach abstracts away the need for manually implementing a builder for each struct, making code more readable and maintainable. -## Basic use-case +## Example : Trivial -The provided code snippet illustrates a basic use-case of the Former crate in Rust, which is used to apply the builder pattern for structured and flexible object creation. Below is a detailed explanation of each part of the markdown chapter, aimed at clarifying how the Former trait simplifies struct instantiation. + + + + + + + +The provided code snippet illustrates a basic use-case of the Former, which is used to apply the builder pattern for structured and flexible object creation. Below is a detailed explanation of each part of the markdown chapter, aimed at clarifying how the Former trait simplifies struct instantiation. ```rust -#[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] -fn main() -{ +# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +# fn main() {} + +# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +# fn main() +# { + use former::Former; + // Use attribute debug to print expanded code. #[ derive( Debug, PartialEq, Former ) ] - #[ perform( fn greet_user() ) ] + // #[ debug ] pub struct UserProfile { - #[default(1)] age : i32, - username : String, - - #[alias(bio)] bio_optional : Option< String >, // Fields could be optional } - impl UserProfile - { - fn greet_user(self) -> Self - { - println!("Hello, {}", self.username); - self - } - } - let profile = UserProfile::former() .age( 30 ) .username( "JohnDoe".to_string() ) .bio_optional( "Software Developer".to_string() ) // Optionally provide a bio .form(); - // .perform(); // same as `form()` but will execute method passed to `perform` attribute dbg!( &profile ); // Expected output: @@ -66,228 +64,420 @@ fn main() // bio_optional: Some("Software Developer"), // } - } - ``` +# } +```
The code above will be expanded to this ```rust +# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +# fn main() {} -#[ derive( Debug, PartialEq ) ] -pub struct UserProfile -{ - age : i32, - username : String, - bio_optional : Option< String >, // Fields could be optional -} +# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +# fn main() +# { -impl UserProfile -{ - #[ inline( always ) ] - pub fn former() -> UserProfileFormer< UserProfile, former::ReturnFormed > + // Use attribute debug to print expanded code. + #[ derive( Debug, PartialEq ) ] + pub struct UserProfile { - UserProfileFormer::< UserProfile, former::ReturnFormed >::new() + age : i32, + username : String, + bio_optional : Option< String >, // Fields could be optional } -} -#[ derive( Debug, Default ) ] -pub struct UserProfileFormerStorage -{ - age : Option< i32 >, - username : Option< String >, - bio_optional : Option< String >, -} + impl UserProfile + where + { + #[ inline( always ) ] + pub fn former() -> UserProfileFormer< + UserProfileFormerDefinition< (), UserProfile, former::ReturnPreformed > + > + { + UserProfileFormer::< UserProfileFormerDefinition< (), UserProfile, former::ReturnPreformed > >:: + new_coercing(former::ReturnPreformed) + } + } -pub struct UserProfileFormer -< - Context = UserProfile, - End = former::ReturnFormed, -> -where - End : former::FormingEnd< UserProfile, Context >, -{ - storage : UserProfileFormerStorage, - context : Option< Context >, - on_end : Option< End >, -} + // = entity to -impl< Context, End > UserProfileFormer< Context, End > -where - End : former::FormingEnd< UserProfile, Context >, -{ - #[ inline( always ) ] - pub fn form( mut self ) -> UserProfile + impl< Definition > former::EntityToFormer< Definition > for UserProfile + where + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >, + { + type Former = UserProfileFormer< Definition >; + } + + impl former::EntityToStorage for UserProfile + where + { + type Storage = UserProfileFormerStorage; + } + + impl< Context, Formed, End > former::EntityToDefinition< Context, Formed, End > for UserProfile + where + End : former::FormingEnd< UserProfileFormerDefinitionTypes< Context, Formed > >, + { + type Definition = UserProfileFormerDefinition< Context, Formed, End >; + type Types = UserProfileFormerDefinitionTypes< Context, Formed >; + } + + // = definition + + #[derive(Debug)] + pub struct UserProfileFormerDefinitionTypes< Context = (), Formed = UserProfile, > + where { - let age = if self.storage.age.is_some() + _phantom : core::marker::PhantomData< (*const Context, *const Formed) >, + } + + impl< Context, Formed, > ::core::default::Default for UserProfileFormerDefinitionTypes< Context, Formed, > + where + { + fn default() -> Self { - self.storage.age.take().unwrap() + Self + { + _phantom : core::marker::PhantomData, + } } - else + } + + impl< Context, Formed, > former::FormerDefinitionTypes for UserProfileFormerDefinitionTypes< Context, Formed, > + where + { + type Storage = UserProfileFormerStorage; + type Formed = Formed; + type Context = Context; + } + + #[derive(Debug)] + pub struct UserProfileFormerDefinition< Context = (), Formed = UserProfile, End = former::ReturnPreformed, > + where + { + _phantom : core::marker::PhantomData< (*const Context, *const Formed, *const End) >, + } + + impl< Context, Formed, End, > ::core::default::Default for UserProfileFormerDefinition< Context, Formed, End, > + where + { + fn default() -> Self { - let val : i32 = + Self { - trait NotDefault< T > - { - fn maybe_default( self : &Self ) -> T { panic!( "Field 'age' isn't initialized" ) } - } - trait WithDefault< T > - { - fn maybe_default( self : &Self ) -> T; - } - impl< T > NotDefault< T > for &::core::marker::PhantomData< T > {} - impl< T > WithDefault< T > for ::core::marker::PhantomData< T > - where - T : ::core::default::Default, - { - fn maybe_default( self : &Self ) -> T - { - T::default() - } - } - ( &::core::marker::PhantomData::< i32 > ).maybe_default() - }; - val - }; - let username = if self.storage.username.is_some() + _phantom : core::marker::PhantomData, + } + } + } + + impl< Context, Formed, End, > former::FormerDefinition for UserProfileFormerDefinition< Context, Formed, End, > + where + End : former::FormingEnd< UserProfileFormerDefinitionTypes< Context, Formed, > >, + { + type Types = UserProfileFormerDefinitionTypes< Context, Formed, >; + type End = End; + type Storage = UserProfileFormerStorage; + type Formed = Formed; + type Context = Context; + } + + impl< Context, Formed, > former::FormerMutator for UserProfileFormerDefinitionTypes< Context, Formed, > + where + {} + + // = storage + + pub struct UserProfileFormerStorage + where + { + pub age : ::core::option::Option< i32 >, + pub username : ::core::option::Option< String >, + pub bio_optional : Option< String >, + } + + impl ::core::default::Default for UserProfileFormerStorage + where + { + #[ inline( always ) ] + fn default() -> Self { - self.storage.username.take().unwrap() + Self + { + age : ::core::option::Option::None, + username : ::core::option::Option::None, + bio_optional : ::core::option::Option::None, + } } - else + } + + impl former::Storage for UserProfileFormerStorage + where + { + type Preformed = UserProfile; + } + + impl former::StoragePreform for UserProfileFormerStorage + where + { + fn preform(mut self) -> Self::Preformed { - let val : String = + let age = if self.age.is_some() + { + self.age.take().unwrap() + } + else { - trait NotDefault< T > - { - fn maybe_default( self : &Self ) -> T { panic!( "Field 'username' isn't initialized" ) } - } - trait WithDefault< T > { - fn maybe_default( self : &Self ) -> T; + trait MaybeDefault< T > + { + fn maybe_default(self : &Self) -> T + { + panic!("Field 'age' isn't initialized") + } + } + impl< T > MaybeDefault< T > for &::core::marker::PhantomData< T > + {} + impl< T > MaybeDefault< T > for ::core::marker::PhantomData< T > + where T : ::core::default::Default, + { + fn maybe_default(self : &Self) -> T + { + T::default() + } + } + (&::core::marker::PhantomData::< i32 >).maybe_default() } - impl< T > NotDefault< T > for &::core::marker::PhantomData< T > {} - impl< T > WithDefault< T > for ::core::marker::PhantomData< T > - where - T : ::core::default::Default, + }; + let username = if self.username.is_some() + { + self.username.take().unwrap() + } + else + { { - fn maybe_default( self : &Self ) -> T + trait MaybeDefault< T > + { + fn maybe_default(self : &Self) -> T + { + panic!("Field 'username' isn't initialized") + } + } + impl< T > MaybeDefault< T > for &::core::marker::PhantomData< T > + {} + impl< T > MaybeDefault< T > for ::core::marker::PhantomData< T > + where T : ::core::default::Default, { - T::default() + fn maybe_default(self : &Self) -> T + { + T::default() + } } + (&::core::marker::PhantomData::< String >).maybe_default() } - ( &::core::marker::PhantomData::< String > ).maybe_default() }; - val - }; - let bio_optional = if self.storage.bio_optional.is_some() - { - Option::Some( self.storage.bio_optional.take().unwrap() ) + let bio_optional = if self.bio_optional.is_some() + { + ::core::option::Option::Some(self.bio_optional.take().unwrap()) + } + else + { + ::core::option::Option::None + }; + let result = UserProfile::<> + { + age, + username, + bio_optional, + }; + return result; } - else - { - Option::None - }; - let result = UserProfile - { - age, - username, - bio_optional, - }; - return result; } - #[ inline( always ) ] - pub fn perform( self ) -> UserProfile + pub struct UserProfileFormer< Definition = UserProfileFormerDefinition< (), UserProfile, former::ReturnPreformed >, > + where + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >, { - let result = self.form(); - return result; + pub storage : Definition::Storage, + pub context : core::option::Option< Definition::Context >, + pub on_end : core::option::Option< Definition::End >, } - #[ inline( always ) ] - pub fn new() -> UserProfileFormer< UserProfile, former::ReturnFormed > + impl< Definition, > UserProfileFormer< Definition, > + where + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >, Definition::Types : former::FormerDefinitionTypes< Storage = UserProfileFormerStorage >, { - UserProfileFormer::< UserProfile, former::ReturnFormed >::begin( None, former::ReturnFormed ) - } + #[ inline( always ) ] + pub fn new(on_end : Definition::End) -> Self + { + Self::begin_coercing(None, None, on_end) + } - #[ inline( always ) ] - pub fn begin - ( - context : Option< Context >, - on_end : End, - ) -> Self - { - Self + #[ inline( always ) ] + pub fn new_coercing< IntoEnd >(end : IntoEnd) -> Self + where IntoEnd : Into< Definition::End >, { - storage : core::default::Default::default(), - context : context, - on_end : Option::Some( on_end ), + Self::begin_coercing(None, None, end,) } - } - #[ inline( always ) ] - pub fn end( mut self ) -> Context - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) + #[ inline( always ) ] + pub fn begin(mut storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : ::End,) -> Self + { + if storage.is_none() + { + storage = Some(::core::default::Default::default()); + } + Self + { + storage : storage.unwrap(), + context : context, + on_end : ::core::option::Option::Some(on_end), + } + } + + #[ inline( always ) ] + pub fn begin_coercing< IntoEnd >(mut storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : IntoEnd,) -> Self + where IntoEnd : ::core::convert::Into< ::End >, + { + if storage.is_none() + { + storage = Some(::core::default::Default::default()); + } + Self + { + storage : storage.unwrap(), + context : context, + on_end : ::core::option::Option::Some(::core::convert::Into::into(on_end)), + } + } + + #[ inline( always ) ] + pub fn form(self) -> ::Formed + { + self.end() + } + + #[ inline( always ) ] + pub fn end(mut self) -> ::Formed + { + let on_end = self.on_end.take().unwrap(); + let mut context = self.context.take(); + ::form_mutation(&mut self.storage, &mut context); + former::FormingEnd::::call(&on_end, self.storage, context) + } + + #[ inline( always ) ] + pub fn age< Src >(mut self, src : Src) -> Self + where Src : ::core::convert::Into< i32 >, + { + debug_assert!(self.storage.age.is_none()); + self.storage.age = ::core::option::Option::Some(::core::convert::Into::into(src)); + self + } + + #[ inline( always ) ] + pub fn username< Src >(mut self, src : Src) -> Self + where Src : ::core::convert::Into< String >, + { + debug_assert!(self.storage.username.is_none()); + self.storage.username = ::core::option::Option::Some(::core::convert::Into::into(src)); + self + } + + #[ inline( always ) ] + pub fn bio_optional< Src >(mut self, src : Src) -> Self + where Src : ::core::convert::Into< String >, + { + debug_assert!(self.storage.bio_optional.is_none()); + self.storage.bio_optional = ::core::option::Option::Some(::core::convert::Into::into(src)); + self + } } - #[ inline ] - pub fn age< Src >( mut self, src : Src ) -> Self + impl< Definition, > UserProfileFormer< Definition, > where - Src : Into< i32 >, + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage, Formed = UserProfile >, { - debug_assert!( self.storage.age.is_none() ); - self.storage.age = Option::Some( src.into() ); - self + pub fn preform(self) -> ::Formed + { + former::StoragePreform::preform(self.storage) + } } - #[ inline ] - pub fn username< Src >( mut self, src : Src ) -> Self + impl< Definition, > UserProfileFormer< Definition, > where - Src : Into< String >, + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage, Formed = UserProfile, >, { - debug_assert!( self.storage.username.is_none() ); - self.storage.username = Option::Some( src.into() ); - self + #[ inline( always ) ] + pub fn perform(self) -> Definition::Formed + { + let result = self.form(); + return result; + } } - #[ inline ] - pub fn bio_optional< Src >( mut self, src : Src ) -> Self + impl< Definition > former::FormerBegin< Definition > for UserProfileFormer< Definition, > where - Src : Into< String >, + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >, { - debug_assert!( self.storage.bio_optional.is_none() ); - self.storage.bio_optional = Option::Some( src.into() ); - self + #[ inline( always ) ] + fn former_begin(storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : Definition::End,) -> Self + { + debug_assert!(storage.is_none()); + Self::begin(None, context, on_end) + } } -} -let profile = UserProfile::former() -.age( 30 ) -.username( "JohnDoe".to_string() ) -.bio_optional( "Software Developer".to_string() ) -.form(); + // = as subformer -dbg!( &profile ); -// Expected output: -// &profile = UserProfile { -// age: 30, -// username: "JohnDoe", -// bio_optional: Some("Software Developer"), -// } + pub type UserProfileAsSubformer< Superformer, End > = + UserProfileFormer< UserProfileFormerDefinition< Superformer, Superformer, End, >, >; + pub trait UserProfileAsSubformerEnd< SuperFormer > + where + Self : former::FormingEnd< UserProfileFormerDefinitionTypes< SuperFormer, SuperFormer >, >, {} + + impl< SuperFormer, T > UserProfileAsSubformerEnd< SuperFormer > for T + where + Self : former::FormingEnd< UserProfileFormerDefinitionTypes< SuperFormer, SuperFormer >, >, + {} + + // = end + + let profile = UserProfile::former() + .age( 30 ) + .username( "JohnDoe".to_string() ) + .bio_optional( "Software Developer".to_string() ) // Optionally provide a bio + .form(); + dbg!( &profile ); + + // Expected output: + // + // &profile = UserProfile { + // age: 30, + // username: "JohnDoe", + // bio_optional: Some("Software Developer"), + // } + +# } ```
-### Custom and Alternative Setters +Try out `cargo run --example former_trivial`. +
+[See code](./examples/former_trivial.rs). + +## Example : Custom and Alternative Setters With help of `Former`, it is possible to define multiple versions of a setter for a single field, providing the flexibility to include custom logic within the setter methods. This feature is particularly useful when you need to preprocess data or enforce specific constraints before assigning values to fields. Custom setters should have unique names to differentiate them from the default setters generated by `Former`, allowing for specialized behavior while maintaining clarity in your code. ```rust +# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +# fn main() {} + # #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +# fn main() # { use former::Former; @@ -327,12 +517,20 @@ assert_eq!( example.word, "Hello!".to_string() ); In the example above showcases a custom alternative setter, `word_exclaimed`, which appends an exclamation mark to the input string before storing it. This approach allows for additional processing or validation of the input data without compromising the simplicity of the builder pattern. -### Custom Setter Overriding +Try out `cargo run --example former_custom_setter`. +
+[See code](./examples/former_custom_setter.rs). + +## Example : Custom Setter Overriding But it's also possible to completely override setter and write its own from scratch. For that use attribe `[ setter( false ) ]` to disable setter. ```rust +# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +# fn main() {} + # #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +# fn main() # { use former::Former; @@ -341,7 +539,7 @@ use former::Former; #[ derive( Debug, Former ) ] pub struct StructWithCustomSetters { - #[ setter( false ) ] + #[ scalar( setter = false ) ] word : String, } @@ -362,17 +560,26 @@ let example = StructWithCustomSetters::former() .word( "Hello" ) .form(); assert_eq!( example.word, "Hello!".to_string() ); + # } ``` In the example above, the default setter for `word` is disabled, and a custom setter is defined to automatically append an exclamation mark to the string. This method allows for complete control over the data assignment process, enabling the inclusion of any necessary logic or validation steps. -## Custom Default +Try out `cargo run --example former_custom_setter_overriden`. +
+[See code](./examples/former_custom_setter_overriden.rs). + +## Example : Custom Defaults -The `Former` crate enhances struct initialization in Rust by allowing the specification of custom default values for fields through the `default` attribute. This feature not only provides a way to set initial values for struct fields without relying on the `Default` trait but also adds flexibility in handling cases where a field's type does not implement `Default`, or a non-standard default value is desired. +The `Former` crate enhances struct initialization by allowing the specification of custom default values for fields through the `default` attribute. This feature not only provides a way to set initial values for struct fields without relying on the `Default` trait but also adds flexibility in handling cases where a field's type does not implement `Default`, or a non-standard default value is desired. ```rust +# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +# fn main() {} + # #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +# fn main() # { use former::Former; @@ -381,11 +588,11 @@ use former::Former; #[ derive( Debug, PartialEq, Former ) ] pub struct ExampleStruct { - #[ default( 5 ) ] + #[ former( default = 5 ) ] number : i32, - #[ default( "Hello, Former!".to_string() ) ] + #[ former( default = "Hello, Former!".to_string() ) ] greeting : String, - #[ default( vec![ 10, 20, 30 ] ) ] + #[ former( default = vec![ 10, 20, 30 ] ) ] numbers : Vec< i32 >, } @@ -407,6 +614,7 @@ dbg!( &instance ); // > 30, // > ], // > } + # } ``` @@ -417,200 +625,654 @@ The above code snippet showcases the `Former` crate's ability to initialize stru This approach significantly simplifies struct construction, particularly for complex types or where defaults beyond the `Default` trait's capability are required. By utilizing the `default` attribute, developers can ensure their structs are initialized safely and predictably, enhancing code clarity and maintainability. -## Concept of subformer +Try out `cargo run --example former_custom_defaults`. +
+[See code](./examples/former_custom_defaults.rs). + +## Concept of Storage and Former + +Storage is temporary storage structure holds the intermediate state of an object during its construction. + +Purpose of Storage: + +- **Intermediate State Holding**: Storage serves as a temporary repository for all the partially set properties and data of the object being formed. This functionality is essential in situations where the object's completion depends on multiple, potentially complex stages of configuration. +- **Decoupling Configuration from Instantiation**: Storage separates the accumulation of configuration states from the actual creation of the final object. This separation fosters cleaner, more maintainable code, allowing developers to apply configurations in any order and manage interim states more efficiently, without compromising the integrity of the final object. + +Storage is not just a passive container; it is an active part of a larger ecosystem that includes the former itself, a context, and a callback (often referred to as `FormingEnd`): + +- **Former as an Active Manager**: The former is responsible for managing the storage, utilizing it to keep track of the object's evolving configuration. It orchestrates the formation process by handling intermediate states and preparing the object for its final form. +- **Contextual Flexibility**: The context associated with the former adds an additional layer of flexibility, allowing the former to adjust its behavior based on the broader circumstances of the object's formation. This is particularly useful when the forming process involves conditions or states external to the object itself. +- **FormingEnd Callback**: The `FormingEnd` callback is a dynamic component that defines the final steps of the forming process. It can modify the storage based on final adjustments, validate the object's readiness, or integrate the object into a larger structure, such as embedding it as a subformer within another structure. + +These elements work in concert to ensure that the forming process is not only about building an object step-by-step but also about integrating it seamlessly into larger, more complex structures or systems. + +## Concept of Definitions + +Definitions are utilized to encapsulate and manage generic parameters efficiently and avoid passing each parameter individually. + +Two key definition Traits: + +1. **`FormerDefinitionTypes`**: + - This trait outlines the essential components involved in the formation process, including the types of storage, the form being created, and the context used. It focuses on the types involved rather than the termination of the formation process. +2. **`FormerDefinition`**: + - Building upon `FormerDefinitionTypes`, this trait incorporates the `FormingEnd` callback, linking the formation types with a definitive ending. It specifies how the formation process should conclude, which may involve validations, transformations, or integrations into larger structures. + - The inclusion of the `End` type parameter specifies the end conditions of the formation process, effectively connecting the temporary state held in storage to its ultimate form. + +## Overview of Formation Traits + +The formation process utilizes several core traits, each serving a specific purpose in the lifecycle of entity creation. These traits ensure that entities are constructed methodically, adhering to a structured pattern that enhances maintainability and scalability. Below is a summary of these key traits: -Subformers are specialized builders used within the `Former` framework to construct nested or collection-based data structures like vectors, hash maps, and hash sets. They simplify the process of adding elements to these structures by providing a fluent interface that can be seamlessly integrated into the overall builder pattern of a parent struct. This approach allows for clean and intuitive initialization of complex data structures, enhancing code readability and maintainability. +- `EntityToDefinition`: Links entities to their respective formation definitions which dictate their construction process. +- `EntityToFormer`: Connects entities with formers that are responsible for their step-by-step construction. +- `EntityToStorage`: Specifies the storage structures that temporarily hold the state of an entity during its formation. +- `FormerDefinition`, `FormerDefinitionTypes`: Define the essential properties and ending conditions of the formation process, ensuring entities are formed according to predetermined rules and logic. +- `Storage`: Establishes the fundamental interface for storage types used in the formation process, ensuring each can initialize to a default state. +- `StoragePreform`: Describes the transformation of storage from a mutable, intermediate state into the final, immutable state of the entity, crucial for accurately concluding the formation process. +- `FormerMutator`: Allows for custom mutation logic on the storage and context immediately before the formation process completes, ensuring last-minute adjustments are possible. +- `FormingEnd`: Specifies the closure action at the end of the formation process, which can transform or validate the final state of the entity. +- `FormingEndClosure`: Provides a flexible mechanism for dynamically handling the end of the formation process using closures, useful for complex scenarios. +- `FormerBegin`: Initiates a subforming process, managing how entities begin their formation in terms of storage and context setup. -### Subformer example: Building a Vector +These traits collectively facilitate a robust and flexible builder pattern that supports complex object creation and configuration scenarios. -The following example illustrates how to use a `VectorSubformer` to construct a `Vec` field within a struct. The subformer enables adding elements to the vector with a fluent interface, streamlining the process of populating collection fields within structs. +## Example : Custom Definition + +Define a custom former definition and custom forming logic, and apply them to a container. + +The example showcases how to accumulate elements into a container and then transform them into a single result using a custom `FormingEnd` implementation. This pattern is useful for scenarios where the formation process involves aggregation or transformation of input elements into a different type or form. ```rust +# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +# fn main() {} + # #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] -# #[ cfg( not( feature = "no_std" ) ) ] +# fn main() # { -#[ derive( Debug, PartialEq, former::Former ) ] -pub struct StructWithVec -{ - #[ subformer( former::VectorSubformer ) ] - vec : Vec< &'static str >, -} + // Define a struct `Sum` that will act as a custom former definition. + struct Sum; -let instance = StructWithVec::former() -.vec() - .push( "apple" ) - .push( "banana" ) - .end() -.form(); + // Implement `FormerDefinitionTypes` for `Sum`. + // This trait defines the types used during the forming process. + impl former::FormerDefinitionTypes for Sum + { + type Storage = Vec; // Container for the integers. + type Formed = i32; // The final type after forming, which is a single integer. + type Context = (); // No additional context is used in this example. + } + + // Implement `FormerMutator` for `Sum`. + // This trait could include custom mutation logic applied during the forming process, but it's empty in this example. + impl former::FormerMutator for Sum + { + } + + // Implement `FormerDefinition` for `Sum`. + // This trait links the custom types to the former. + impl former::FormerDefinition for Sum + { + type Types = Sum; // Associate the `FormerDefinitionTypes` with `Sum`. + type End = Sum; // Use `Sum` itself as the end handler. + type Storage = Vec; // Specify the storage type. + type Formed = i32; // Specify the final formed type. + type Context = (); // Specify the context type, not used here. + } + + // Implement `FormingEnd` for `Sum`. + // This trait handles the final step of the forming process. + impl former::FormingEnd for Sum + { + fn call + ( + &self, + storage: < Sum as former::FormerDefinitionTypes >::Storage, + _context: Option< < Sum as former::FormerDefinitionTypes >::Context> + ) + -> < Sum as former::FormerDefinitionTypes >::Formed + { + // Sum all integers in the storage vector. + storage.iter().sum() + } + } + + // Use the custom `Former` to sum a list of integers. + let got = former::ContainerFormer::::new(Sum) + .add( 1 ) // Add an integer to the storage. + .add( 2 ) // Add another integer. + .add( 10 ) // Add another integer. + .form(); // Perform the form operation, which triggers the summing logic. + let exp = 13; // Expected result after summing 1, 2, and 10. + assert_eq!(got, exp); // Assert the result is as expected. + + dbg!(got); // Debug print the result to verify the output. + // > got = 13 -assert_eq!( instance, StructWithVec { vec: vec![ "apple", "banana" ] } ); # } ``` -### Subformer example: Building a Hashmap +## Concept of subformer + +Subformers are specialized builders used within the former to construct nested or collection-based data structures like vectors, hash maps, and hash sets. They simplify the process of adding elements to these structures by providing a fluent interface that can be seamlessly integrated into the overall builder pattern of a parent struct. This approach allows for clean and intuitive initialization of complex data structures, enhancing code readability and maintainability. + +## Types of Setters / Subformers + +It's crucial to understand the differences among subform setters, container setters, and scalar setters: + +- **Scalar Setter**: Directly sets scalar values or simple fields within the forming entity. Unlike subform or container setters that manage complex objects or collections, scalar setters handle basic data types or individual fields. These are typically straightforward setter methods that do not involve nested formers or additional structuring. + +- **Container Setter**: Returns a former of the container itself, offering an interface to manage the container as a whole rather than its individual elements. This type of setter is useful for applying configurations or validations to the entire collection, such as a `HashMap` of children. -This example demonstrates the use of a `HashMapSubformer` to build a hash map within a struct. The subformer provides a concise way to insert key-value pairs into the map, making it easier to manage and construct hash map fields. +- **Subform Setter**: Returns a former of an element within a container, providing an interface to individually form each element. For example, the `child` method acts as a subform setter, allowing for the addition and configuration of individual `Child` entities within the `Parent`'s `HashMap`. + +Each type of setter is designed to address different needs in the formation process, ensuring that users can build complex, nested structures or simply set individual field values as required. + +## Example : Container Setter for a Vector + +This example demonstrates how to employ the `Former` trait to configure a `Vec` using a container setter in a structured manner. ```rust -# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] -# #[ cfg( not( feature = "no_std" ) ) ] +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# fn main() {} + +# #[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +# fn main() # { -use test_tools::exposed::*; + #[ derive( Debug, PartialEq, former::Former ) ] + pub struct StructWithVec + { + #[ container ] + vec : Vec< &'static str >, + } -#[ derive( Debug, PartialEq, former::Former ) ] -pub struct StructWithMap -{ - #[ subformer( former::HashMapSubformer ) ] - map : std::collections::HashMap< &'static str, &'static str >, -} + let instance = StructWithVec::former() + .vec() + .add( "apple" ) + .add( "banana" ) + .end() + .form(); + + assert_eq!( instance, StructWithVec { vec: vec![ "apple", "banana" ] } ); + dbg!( instance ); -let struct1 = StructWithMap::former() -.map() - .insert( "a", "b" ) - .insert( "c", "d" ) - .end() -.form() -; -assert_eq!( struct1, StructWithMap { map : hmap!{ "a" => "b", "c" => "d" } } ); # } ``` -### Subformer example: Building a Hashset +Try out `cargo run --example former_container_vector`. +
+[See code](./examples/former_container_vector.rs). + +## Example : Container Setter for a Hashmap -In the following example, a `HashSetSubformer` is utilized to construct a hash set within a struct. This illustrates the convenience of adding elements to a set using the builder pattern facilitated by subformers. +This example demonstrates how to effectively employ the `Former` trait to configure a `HashMap` using a container setter. ```rust -# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] -# #[ cfg( not( feature = "no_std" ) ) ] +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# fn main() {} + +# #[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +# fn main() # { + use collection_tools::{ HashMap, hmap }; + + #[ derive( Debug, PartialEq, former::Former ) ] + pub struct StructWithMap + { + #[ container ] + map : HashMap< &'static str, &'static str >, + } + + let instance = StructWithMap::former() + .map() + .add( ( "a", "b" ) ) + .add( ( "c", "d" ) ) + .end() + .form() + ; + assert_eq!( instance, StructWithMap { map : hmap!{ "a" => "b", "c" => "d" } } ); + dbg!( instance ); + +# } +``` + +Try out `cargo run --example former_container_hashmap`. +
+[See code](./examples/former_container_hashmap.rs). + +## Example : Container Setter for a Hashset + +This example demonstrates the use of the `Former` trait to build a `collection_tools::HashSet` through subforming. -use test_tools::exposed::*; +```rust +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# fn main() {} -#[ derive( Debug, PartialEq, former::Former ) ] -pub struct StructWithSet +# #[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +# fn main() { - #[ subformer( former::HashSetSubformer ) ] - set : std::collections::HashSet< &'static str >, -} + use collection_tools::{ HashSet, hset }; -let instance = StructWithSet::former() -.set() - .insert("apple") - .insert("banana") - .end() -.form(); + #[ derive( Debug, PartialEq, former::Former ) ] + pub struct StructWithSet + { + #[ container ] + set : HashSet< &'static str >, + } + + let instance = StructWithSet::former() + .set() + .add( "apple" ) + .add( "banana" ) + .end() + .form(); + + assert_eq!(instance, StructWithSet { set : hset![ "apple", "banana" ] }); + dbg!( instance ); -assert_eq!(instance, StructWithSet { set : hset![ "apple", "banana" ] }); # } ``` -### Custom Subformer +Try out `cargo run --example former_container_hashset`. +
+[See code](./examples/former_container_hashset.rs). -It is possible to use former of one structure to construct field of another one and integrate it into former of the last one. +## Example : Custom Scalar Setter -The example below illustrates how to incorporate the builder pattern of one structure as a subformer in another, enabling nested struct initialization within a single fluent interface. +This example demonstrates the implementation of a scalar setter using the `Former` trait. Unlike the more complex subform and container setters shown in previous examples, this example focuses on a straightforward approach to directly set a scalar value within a parent entity. The `Parent` struct manages a `HashMap` of `Child` entities, and the scalar setter is used to set the entire `HashMap` directly. - -Example of how to use former of another structure as subformer of former of current one -function `command` integrate `CommandFormer` into `AggregatorFormer`. +The `child` function within `ParentFormer` is a custom subform setter that plays a crucial role. It uniquely employs the `ChildFormer` to add and configure children by their names within the parent's builder pattern. This method demonstrates a powerful technique for integrating subformers that manage specific elements of a container—each child entity in this case. ```rust -# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# fn main() {} + +# #[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +# fn main() # { + use collection_tools::HashMap; + use former::Former; -fn main() -{ - use std::collections::HashMap; + // Child struct with Former derived for builder pattern support + #[ derive( Debug, PartialEq, Former ) ] + // #[ debug ] + pub struct Child + { + name : String, + description : String, + } + + // Parent struct to hold children + #[ derive( Debug, PartialEq, Former ) ] + // #[ debug ] + pub struct Parent + { + // Use `hint = true` to gennerate sketch of setter. + #[ scalar( setter = false, hint = false ) ] + children : HashMap< String, Child >, + } + + impl< Definition > ParentFormer< Definition > + where + Definition : former::FormerDefinition< Storage = ParentFormerStorage >, + { + #[ inline ] + pub fn children< Src >( mut self, src : Src ) -> Self + where + Src : ::core::convert::Into< HashMap< String, Child > >, + { + debug_assert!( self.storage.children.is_none() ); + self.storage.children = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); + self + } + } + + let echo = Child { name : "echo".to_string(), description : "prints all subjects and properties".to_string() }; + let exit = Child { name : "exit".to_string(), description : "just exit".to_string() }; + let mut children = HashMap::new(); + children.insert( echo.name.clone(), echo ); + children.insert( exit.name.clone(), exit ); + let ca = Parent::former() + .children( children ) + .form(); + + dbg!( &ca ); + // > &ca = Parent { + // > child: { + // > "echo": Child { + // > name: "echo", + // > description: "prints all subjects and properties", + // > }, + // > "exit": Child { + // > name: "exit", + // > description: "just exit", + // > }, + // > }, + // > } + +# } +``` + +In this example, the `Parent` struct functions as a container for multiple `Child` structs, each identified by a unique child name. The `ParentFormer` implements a custom method `child`, which serves as a subformer for adding `Child` instances into the `Parent`. + +- **Child Definition**: Each `Child` consists of a `name` and a `description`, and we derive `Former` to enable easy setting of these properties using a builder pattern. +- **Parent Definition**: It holds a collection of `Child` objects in a `HashMap`. The `#[setter(false)]` attribute is used to disable the default setter, and a custom method `child` is defined to facilitate the addition of children with specific attributes. +- **Custom Subformer Integration**: The `child` method in the `ParentFormer` initializes a `ChildFormer` with a closure that integrates the `Child` into the `Parent`'s `child` map upon completion. + +Try out `cargo run --example former_custom_scalar_setter`. +
+[See code](./examples/former_custom_scalar_setter.rs). + +## Example : Custom Container Setter + +This example demonstrates the use of container setters to manage complex nested data structures with the `Former` trait, focusing on a parent-child relationship structured around a container `HashMap`. Unlike typical builder patterns that add individual elements using subform setters, this example uses a container setter to manage the entire collection of children. + +The `child` function within `ParentFormer` is a custom subform setter that plays a crucial role. It uniquely employs the `ChildFormer` to add and configure children by their names within the parent's builder pattern. This method demonstrates a powerful technique for integrating subformers that manage specific elements of a container—each child entity in this case. + +```rust +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# fn main() {} + +// Ensure the example only compiles when the appropriate features are enabled. +# #[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +# fn main() +# { + use collection_tools::HashMap; use former::Former; - // Command struct with Former derived for builder pattern support + // Child struct with Former derived for builder pattern support #[ derive( Debug, PartialEq, Former ) ] - pub struct Command + // #[ debug ] + pub struct Child { name : String, description : String, } - // Aggregator struct to hold commands + // Parent struct to hold children #[ derive( Debug, PartialEq, Former ) ] - pub struct Aggregator + // #[ debug ] + pub struct Parent { - #[ setter( false ) ] - command : HashMap< String, Command >, + // Use `hint = true` to gennerate sketch of setter. + #[ scalar( setter = false, hint = false ) ] + children : HashMap< String, Child >, } - // Use CommandFormer as custom subformer for AggregatorFormer to add commands by name. - impl< Context, End > AggregatorFormer< Context, End > + impl< Definition > ParentFormer< Definition > where - End : former::FormingEnd< Aggregator, Context >, + Definition : former::FormerDefinition< Storage = ParentFormerStorage >, { - pub fn command< IntoName >( self, name : IntoName ) -> CommandFormer< Self, impl former::FormingEnd< Command, Self > > + #[ inline ] + pub fn children< Src >( mut self, src : Src ) -> Self where - IntoName: core::convert::Into< String >, + Src : ::core::convert::Into< HashMap< String, Child > >, { - let on_end = | command : Command, super_former : core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - if let Some( ref mut commands ) = super_former.storage.command - { - commands.insert( command.name.clone(), command ); - } - else - { - let mut commands: HashMap< String, Command > = Default::default(); - commands.insert( command.name.clone(), command ); - super_former.storage.command = Some( commands ); - } - super_former - }; - let former = CommandFormer::begin( None, Some( self ), on_end ); - former.name( name ) + debug_assert!( self.storage.children.is_none() ); + self.storage.children = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); + self } } - let ca = Aggregator::former() - .command( "echo" ) + let echo = Child { name : "echo".to_string(), description : "prints all subjects and properties".to_string() }; + let exit = Child { name : "exit".to_string(), description : "just exit".to_string() }; + let mut children = HashMap::new(); + children.insert( echo.name.clone(), echo ); + children.insert( exit.name.clone(), exit ); + let ca = Parent::former() + .children( children ) + .form(); + + dbg!( &ca ); + // > &ca = Parent { + // > child: { + // > "echo": Child { + // > name: "echo", + // > description: "prints all subjects and properties", + // > }, + // > "exit": Child { + // > name: "exit", + // > description: "just exit", + // > }, + // > }, + // > } + +# } +``` + +Try out `cargo run --example former_custom_container_setter`. +
+[See code](./examples/former_custom_container_setter.rs). + +## Example : Custom Subform Setter + +This example illustrates the implementation of nested builder patterns using the `Former` trait, emphasizing a parent-child relationship. Here, the `Parent` struct utilizes `ChildFormer` as a custom subformer to dynamically manage its `child` field—a `HashMap`. Each child in the `HashMap` is uniquely identified and configured via the `ChildFormer`. + +The `child` function within `ParentFormer` is a custom subform setter that plays a crucial role. It uniquely employs the `ChildFormer` to add and configure children by their names within the parent's builder pattern. This method demonstrates a powerful technique for integrating subformers that manage specific elements of a container—each child entity in this case. + +```rust +# #[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +# fn main() {} + +# // Ensure the example only compiles when the appropriate features are enabled. +# #[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +# fn main() +# { + use collection_tools::HashMap; + use former::Former; + + // Child struct with Former derived for builder pattern support + #[ derive( Debug, PartialEq, Former ) ] + // #[ debug ] + pub struct Child + { + name : String, + description : String, + } + + // Parent struct to hold children + #[ derive( Debug, PartialEq, Former ) ] + // #[ debug ] + pub struct Parent + { + // Use `hint = true` to gennerate sketch of setter. + #[ subform( setter = false, hint = false ) ] + child : HashMap< String, Child >, + } + + /// Initializes and configures a subformer for adding named child entities. This method leverages an internal function + /// to create and return a configured subformer instance. It allows for the dynamic addition of children with specific names, + /// integrating them into the formation process of the parent entity. + /// + impl< Definition > ParentFormer< Definition > + where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, + { + + #[ inline( always ) ] + pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._child_add::< ChildFormer< _ >, _, >() + .name( name ) + } + + } + + // Required to define how `value` is converted into pair `( key, value )` + impl former::ValToEntry< HashMap< String, Child > > for Child + { + type Entry = ( String, Child ); + #[ inline( always ) ] + fn val_to_entry( self ) -> Self::Entry + { + ( self.name.clone(), self ) + } + } + + let ca = Parent::former() + .child( "echo" ) .description( "prints all subjects and properties" ) // sets additional properties using custom subformer .end() - .command( "exit" ) + .child( "exit" ) .description( "just exit" ) // Sets additional properties using using custom subformer .end() .form(); dbg!( &ca ); - // > &ca = Aggregator { - // > command: { - // > "echo": Command { + // > &ca = Parent { + // > child: { + // > "echo": Child { // > name: "echo", // > description: "prints all subjects and properties", // > }, - // > "exit": Command { + // > "exit": Child { // > name: "exit", // > description: "just exit", // > }, // > }, // > } -} # } ``` -In this example, the `Aggregator` struct functions as a container for multiple `Command` structs, each identified by a unique command name. The `AggregatorFormer` implements a custom method `command`, which serves as a subformer for adding `Command` instances into the `Aggregator`. +Try out `cargo run --example former_custom_subform_setter`. +
+[See code](./examples/former_custom_subform_setter.rs). + +## General Container Interface + +There are suite of traits designed to abstract and enhance the functionality of container data structures within the forming process. These traits are integral to managing the complexity of container operations, such as adding, modifying, and converting between different representations within containers like vectors, hash maps, etc. They are especially useful when used in conjunction with the `container` attribute in the `former` macro, which automates the implementation of these traits to create robust and flexible builder patterns for complex data structures. + +- [`Container`] - Defines basic functionalities for containers, managing entries and values, establishing the fundamental operations required for any custom container implementation in forming processes. +- [`EntryToVal`] - Facilitates the conversion of container entries to their value representations, crucial for operations that treat container elements more abstractly as values. +- [`ValToEntry`] - Provides the reverse functionality of `EntryToVal`, converting values back into entries, which is essential for operations that require adding or modifying entries in the container based on value data. +- [`ContainerAdd`] - Adds functionality for inserting entries into a container, considering container-specific rules such as duplication handling and order preservation, enhancing the usability of containers in forming scenarios. +- [`ContainerAssign`] - Extends the container functionality to replace all existing entries with new ones, enabling bulk updates or complete resets of container contents, which is particularly useful in dynamic data environments. + +## Custom Container Former + +Container interface is defined in the crate and implemented for containers like vectors, hash maps, etc, but if you want to use non-standard container you can implement container interface for the container. This example demonstrate how to do that. + +Try out `cargo run --example former_custom_container`. +
+[See code](./examples/former_custom_container.rs). + +## Concept of Mutator + +Provides a mechanism for mutating the context and storage just before the forming process is completed. + +The `FormerMutator` trait allows for the implementation of custom mutation logic on the internal state +of an entity (context and storage) just before the final forming operation is completed. This mutation +occurs immediately before the `FormingEnd` callback is invoked. + +Use cases of Mutator + +- Applying last-minute changes to the data being formed. +- Setting or modifying properties that depend on the final state of the storage or context. +- Storage-specific fields which are not present in formed structure. + +## Storage-Specific Fields + +Storage-specific fields are intermediate fields that exist only in the storage structure during +the forming process. These fields are not present in the final formed structure but are instrumental +in complex forming operations, such as conditional mutations, temporary state tracking, or accumulations. + +These fields are used to manage intermediate data or state that aids in the construction +of the final object but does not necessarily have a direct representation in the object's schema. For +instance, counters, flags, or temporary computation results that determine the final state of the object. + +The `FormerMutator` trait facilitates the implementation of custom mutation logic. It acts on the internal +state (context and storage) just before the final forming operation is completed, right before the `FormingEnd` +callback is invoked. This trait is crucial for making last-minute adjustments or computations based on the +accumulated state in the storage. + +## Mutator vs `FormingEnd` + +Unlike `FormingEnd`, which is responsible for integrating and finalizing the formation process of a field within +a parent former, `form_mutation` directly pertains to the entity itself. This method is designed to be independent +of whether the forming process is occurring within the context of a superformer or if the structure is a standalone +or nested field. This makes `form_mutation` suitable for entity-specific transformations that should not interfere +with the hierarchical forming logic managed by `FormingEnd`. + +## Example : Mutator and Storage Fields + +This example illustrates how to use the `FormerMutator` trait for implementing custom mutations +and demonstrates the concept of storage-specific fields in the forming process. + +In this example, the fields `a` and `b` are defined only within the storage and used +within the custom mutator to enrich or modify the field `c` of the formed entity. This approach +allows for a richer and more flexible formation logic that can adapt based on the intermediate state +held within the storage. + +```rust +# #[ cfg( not( all( feature = "enabled", feature = "derive_former" ) ) ) ] +# fn main() {} + +# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +# fn main() +# { + + use former::Former; + + #[ derive( Debug, PartialEq, Former ) ] + #[ storage_fields( a : i32, b : Option< String > ) ] + #[ mutator( custom = true ) ] + pub struct Struct1 + { + c : String, + } + + // = former mutator + + impl< Context, Formed > former::FormerMutator + for Struct1FormerDefinitionTypes< Context, Formed > + { + /// Mutates the context and storage of the entity just before the formation process completes. + #[ inline ] + fn form_mutation( storage : &mut Self::Storage, _context : &mut ::core::option::Option< Self::Context > ) + { + storage.a.get_or_insert_with( Default::default ); + storage.b.get_or_insert_with( Default::default ); + storage.c = Some( format!( "{:?} - {}", storage.a.unwrap(), storage.b.as_ref().unwrap() ) ); + } + } + + let got = Struct1::former().a( 13 ).b( "abc" ).c( "def" ).form(); + let exp = Struct1 + { + c : "13 - abc".to_string(), + }; + assert_eq!( got, exp ); + dbg!( got ); + // > got = Struct1 { + // > c: "13 - abc", + // > } + +# } +``` + +Try out `cargo run --example former_custom_mutator`. +
+[See code](./examples/former_custom_mutator.rs). + +## Index of Examples + + + + + + -- **Command Definition**: Each `Command` consists of a `name` and a `description`, and we derive `Former` to enable easy setting of these properties using a builder pattern. -- **Aggregator Definition**: It holds a collection of `Command` objects in a `HashMap`. The `#[setter(false)]` attribute is used to disable the default setter, and a custom method `command` is defined to facilitate the addition of commands with specific attributes. -- **Custom Subformer Integration**: The `command` method in the `AggregatorFormer` initializes a `CommandFormer` with a closure that integrates the `Command` into the `Aggregator`'s `command` map upon completion. +- [Custom Defaults](./examples/former_custom_defaults.rs) - Former allows the specification of custom default values for fields through the `former( default )` attribute. +- [Custom Definition](./examples/former_custom_definition.rs) - Define a custom former definition and custom forming logic, and apply them to a container. -This pattern of using a structure's former as a subformer within another facilitates the creation of deeply nested or complex data structures through a coherent and fluent interface, showcasing the powerful capabilities of the `Former` framework for Rust applications. + -### To add to your project +## To add to your project ```sh cargo add former ``` -### Try out from the repository +## Try out from the repository ```sh git clone https://github.com/Wandalen/wTools diff --git a/module/core/former/examples/former_container_hashmap.rs b/module/core/former/examples/former_container_hashmap.rs new file mode 100644 index 0000000000..8908adc7ab --- /dev/null +++ b/module/core/former/examples/former_container_hashmap.rs @@ -0,0 +1,29 @@ +//! +//! This example demonstrates how to effectively employ the `Former` trait to configure a `HashMap` using a container setter. +//! + +#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +fn main() {} +#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +fn main() +{ + use collection_tools::{ HashMap, hmap }; + + #[ derive( Debug, PartialEq, former::Former ) ] + pub struct StructWithMap + { + #[ container ] + map : HashMap< &'static str, &'static str >, + } + + let instance = StructWithMap::former() + .map() + .add( ( "a", "b" ) ) + .add( ( "c", "d" ) ) + .end() + .form() + ; + assert_eq!( instance, StructWithMap { map : hmap!{ "a" => "b", "c" => "d" } } ); + dbg!( instance ); + +} diff --git a/module/core/former/examples/former_container_hashset.rs b/module/core/former/examples/former_container_hashset.rs new file mode 100644 index 0000000000..2c76d24a0a --- /dev/null +++ b/module/core/former/examples/former_container_hashset.rs @@ -0,0 +1,29 @@ +//! +//! This example demonstrates the use of the `Former` trait to build a `collection_tools::HashSet` through subforming. +//! + +#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +fn main() {} +#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +fn main() +{ + use collection_tools::{ HashSet, hset }; + + #[ derive( Debug, PartialEq, former::Former ) ] + pub struct StructWithSet + { + #[ container ] + set : HashSet< &'static str >, + } + + let instance = StructWithSet::former() + .set() + .add( "apple" ) + .add( "banana" ) + .end() + .form(); + + assert_eq!(instance, StructWithSet { set : hset![ "apple", "banana" ] }); + dbg!( instance ); + +} diff --git a/module/core/former/examples/former_container_vector.rs b/module/core/former/examples/former_container_vector.rs new file mode 100644 index 0000000000..92f67dbd47 --- /dev/null +++ b/module/core/former/examples/former_container_vector.rs @@ -0,0 +1,28 @@ +//! +//! This example demonstrates how to employ the `Former` trait to configure a `Vec` using a container setter in a structured manner. +//! + +#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +fn main() {} +#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +fn main() +{ + + #[ derive( Debug, PartialEq, former::Former ) ] + pub struct StructWithVec + { + #[ container ] + vec : Vec< &'static str >, + } + + let instance = StructWithVec::former() + .vec() + .add( "apple" ) + .add( "banana" ) + .end() + .form(); + + assert_eq!( instance, StructWithVec { vec: vec![ "apple", "banana" ] } ); + dbg!( instance ); + +} diff --git a/module/core/former/examples/former_custom_container.rs b/module/core/former/examples/former_custom_container.rs new file mode 100644 index 0000000000..c3e9ff0f8b --- /dev/null +++ b/module/core/former/examples/former_custom_container.rs @@ -0,0 +1,278 @@ +//! Example former_custom_container.rs +//! +//! This example demonstrates how to define and use a custom container with former. +//! The custom container implemented here is a `LoggingSet`, which extends the basic `HashSet` behavior +//! by logging each addition. This example illustrates how to integrate such custom containers with the +//! Former trait system for use in structured data types. + +#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +fn main() {} +#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +fn main() +{ + use collection_tools::HashSet; + + // Custom container that logs additions. + #[ derive( Debug, PartialEq ) ] + pub struct LoggingSet< K > + where + K : core::cmp::Eq + core::hash::Hash, + { + set : HashSet< K >, // Internal HashSet to store the elements. + } + + // Implement default for the custom container. + impl< K > Default for LoggingSet< K > + where + K : core::cmp::Eq + core::hash::Hash, + { + #[ inline( always ) ] + fn default() -> Self + { + Self + { + set : Default::default() // Initialize the internal HashSet. + } + } + } + + // Allow the custom container to be converted into an iterator, to iterate over the elements. + impl< K > IntoIterator for LoggingSet< K > + where + K : std::cmp::Eq + std::hash::Hash, + { + type Item = K; + type IntoIter = std::collections::hash_set::IntoIter< K >; + + fn into_iter( self ) -> Self::IntoIter + { + self.set.into_iter() // Create an iterator from the internal HashSet. + } + } + + // Similar iterator functionality but for borrowing the elements. + impl<'a, K> IntoIterator for &'a LoggingSet< K > + where + K : std::cmp::Eq + std::hash::Hash, + { + type Item = &'a K; + type IntoIter = std::collections::hash_set::Iter< 'a, K >; + + fn into_iter( self ) -> Self::IntoIter + { + self.set.iter() // Borrow the elements via an iterator. + } + } + + // Implement the Container trait to integrate with the former system. + impl< K > former::Container for LoggingSet< K > + where + K : core::cmp::Eq + core::hash::Hash, + { + type Entry = K; + type Val = K; + + #[ inline( always ) ] + fn entry_to_val( e : Self::Entry ) -> Self::Val + { + e // Direct mapping of entries to values. + } + } + + // Implement ContainerAdd to handle adding elements to the custom container. + impl< K > former::ContainerAdd for LoggingSet< K > + where + K : core::cmp::Eq + core::hash::Hash, + { + #[ inline( always ) ] + fn add( &mut self, e : Self::Entry ) -> bool + { + self.set.insert( e ) // Log the addition and add the element to the internal HashSet. + } + } + + // Implement ContainerAssign to handle bulk assignment of elements. + impl< K > former::ContainerAssign for LoggingSet< K > + where + K : core::cmp::Eq + core::hash::Hash, + { + fn assign< Elements >( &mut self, elements : Elements ) -> usize + where + Elements : IntoIterator< Item = Self::Entry > + { + let initial_len = self.set.len(); + self.set.extend( elements ); // Extend the set with a collection of elements. + self.set.len() - initial_len // Return the number of elements added. + } + } + + // Implement ContainerValToEntry to convert values back to entries. + impl< K > former::ContainerValToEntry< K > for LoggingSet< K > + where + K : core::cmp::Eq + core::hash::Hash, + { + type Entry = K; + #[ inline( always ) ] + fn val_to_entry( val : K ) -> Self::Entry + { + val // Direct conversion of value to entry. + } + } + + // = storage + + // Define storage behavior for the custom container. + impl< K > former::Storage + for LoggingSet< K > + where + K : ::core::cmp::Eq + ::core::hash::Hash, + { + type Preformed = LoggingSet< K >; // Define the type after the forming process. + } + + // Implement the preforming behavior to finalize the storage. + impl< K > former::StoragePreform + for LoggingSet< K > + where + K : ::core::cmp::Eq + ::core::hash::Hash, + { + fn preform( self ) -> Self::Preformed + { + self // Return the container as is. + } + } + + // = definition types + + // Definitions related to the type settings for the LoggingSet, which detail how the container should behave with former. + + /// Holds generic parameter types for forming operations related to `LoggingSet`. + #[ derive( Debug, Default ) ] + pub struct LoggingSetDefinitionTypes< K, Context = (), Formed = LoggingSet< K > > + { + _phantom : core::marker::PhantomData< ( K, Context, Formed ) >, // PhantomData is used to handle generic parameters safely. + } + + /// Specifies the storage, formed type, and context for the `LoggingSet` when used in a forming process. + impl< K, Context, Formed > former::FormerDefinitionTypes + for LoggingSetDefinitionTypes< K, Context, Formed > + where + K : ::core::cmp::Eq + ::core::hash::Hash, + { + type Storage = LoggingSet< K >; // Specifies that `LoggingSet` is used as the storage. + type Formed = Formed; // The final formed type after the forming process. + type Context = Context; // The context required for forming, can be specified by the user. + } + + // = definition + + /// Provides a complete definition for `LoggingSet` including the end condition of the forming process. + #[ derive( Debug, Default ) ] + pub struct LoggingSetDefinition< K, Context = (), Formed = LoggingSet< K >, End = former::ReturnStorage > + { + _phantom : core::marker::PhantomData< ( K, Context, Formed, End ) >, + } + + /// Associates the `LoggingSet` with a specific forming process and defines its behavior. + impl< K, Context, Formed, End > former::FormerDefinition + for LoggingSetDefinition< K, Context, Formed, End > + where + K : ::core::cmp::Eq + ::core::hash::Hash, + End : former::FormingEnd< LoggingSetDefinitionTypes< K, Context, Formed > >, + { + type Storage = LoggingSet< K >; // The storage type during the formation process. + type Formed = Formed; // The type resulting from the formation process. + type Context = Context; // The context used during the formation process. + type Types = LoggingSetDefinitionTypes< K, Context, Formed >; // The associated type settings. + type End = End; // The ending condition for the forming process. + } + + // = mutator + + /// Optional: Implements mutating capabilities to modify the forming process of `LoggingSet` if needed. + impl< K, Context, Formed > former::FormerMutator + for LoggingSetDefinitionTypes< K, Context, Formed > + where + K : ::core::cmp::Eq + ::core::hash::Hash, + { + } + + // = Entity To + + /// Associates the `LoggingSet` with a specific `Former` for use in forming processes. + impl< K, Definition > former::EntityToFormer< Definition > for LoggingSet< K > + where + K : ::core::cmp::Eq + ::core::hash::Hash, + Definition : former::FormerDefinition + < + Storage = LoggingSet< K >, + Types = LoggingSetDefinitionTypes + < + K, + < Definition as former::FormerDefinition >::Context, + < Definition as former::FormerDefinition >::Formed, + >, + >, + Definition::End : former::FormingEnd< Definition::Types >, + { + type Former = LoggingSetAsSubformer< K, Definition::Context, Definition::Formed, Definition::End >; + } + + /// Specifies the storage for `LoggingSet`. + impl< K > former::EntityToStorage + for LoggingSet< K > + where + K : ::core::cmp::Eq + ::core::hash::Hash, + { + type Storage = LoggingSet< K >; + } + + /// Defines the relationship between `LoggingSet` and its formal definition within the forming system. + impl< K, Context, Formed, End > former::EntityToDefinition< Context, Formed, End > + for LoggingSet< K > + where + K : ::core::cmp::Eq + ::core::hash::Hash, + End : former::FormingEnd< LoggingSetDefinitionTypes< K, Context, Formed > >, + { + type Definition = LoggingSetDefinition< K, Context, Formed, End >; + type Types = LoggingSetDefinitionTypes< K, Context, Formed >; + } + + /// Provides type-specific settings for the formation process related to `LoggingSet`. + impl< K, Context, Formed > former::EntityToDefinitionTypes< Context, Formed > + for LoggingSet< K > + where + K : ::core::cmp::Eq + ::core::hash::Hash, + { + type Types = LoggingSetDefinitionTypes< K, Context, Formed >; + } + + // = subformer + + // Subformer type alias simplifies the usage of `ContainerFormer` with `LoggingSet`. + pub type LoggingSetAsSubformer< K, Context, Formed, End > = + former::ContainerFormer::< K, LoggingSetDefinition< K, Context, Formed, End > >; + + // == use custom container + + /// Parent required for the template. + #[ derive( Debug, Default, PartialEq, former::Former ) ] + pub struct Parent + { + #[ container ] + children : LoggingSet< i32 >, + } + + // Using the builder pattern provided by Former to manipulate Parent + let parent = Parent::former() + .children() + .add(10) + .add(20) + .add(10) + .end() + .form(); + + println!("Got: {:?}", parent); + // > Parent { children: LoggingSet { set: {10, 20} } } + +} diff --git a/module/core/former/examples/former_custom_container_setter.rs b/module/core/former/examples/former_custom_container_setter.rs new file mode 100644 index 0000000000..d5565a0908 --- /dev/null +++ b/module/core/former/examples/former_custom_container_setter.rs @@ -0,0 +1,91 @@ +// Example former_custom_container_setter.rs + +//! +//! This example demonstrates the use of container setters to manage complex nested data structures with the `Former` trait, focusing on a parent-child relationship structured around a container `HashMap`. Unlike typical builder patterns that add individual elements using subform setters, this example uses a container setter to manage the entire collection of children. +//! +//! The `child` function within `ParentFormer` is a custom subform setter that plays a crucial role. It uniquely employs the `ChildFormer` to add and configure children by their names within the parent's builder pattern. This method demonstrates a powerful technique for integrating subformers that manage specific elements of a container—each child entity in this case. +//! +//! #### Types of Setters +//! +//! It's crucial to understand the differences among subform setters, container setters, and scalar setters: +//! +//! - **Scalar Setter**: Directly sets scalar values or simple fields within the forming entity. Unlike subform or container setters that manage complex objects or collections, scalar setters handle basic data types or individual fields. These are typically straightforward setter methods that do not involve nested formers or additional structuring. +//! +//! - **Container Setter**: Returns a former of the container itself, offering an interface to manage the container as a whole rather than its individual elements. This type of setter is useful for applying configurations or validations to the entire collection, such as a `HashMap` of children. +//! +//! - **Subform Setter**: Returns a former of an element within a container, providing an interface to individually form each element. For example, the `child` method acts as a subform setter, allowing for the addition and configuration of individual `Child` entities within the `Parent`'s `HashMap`. +//! +//! Each type of setter is designed to address different needs in the formation process, ensuring that users can build complex, nested structures or simply set individual field values as required. +//! + +// Ensure the example only compiles when the appropriate features are enabled. +#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +fn main() {} +#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +fn main() +{ + use collection_tools::HashMap; + use former::Former; + + // Child struct with Former derived for builder pattern support + #[ derive( Debug, PartialEq, Former ) ] + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + pub struct Child + { + name : String, + description : String, + } + + // Parent struct to hold children + #[ derive( Debug, PartialEq, Former ) ] + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + pub struct Parent + { + // Use `hint = true` to gennerate sketch of setter. + #[ container( setter = false, hint = false ) ] + children : HashMap< String, Child >, + } + + /// The containr setter provides a container setter that returns a ContainerFormer tailored for managing a collection of child entities. It employs a generic container definition to facilitate operations on the entire collection, such as adding or updating elements. + impl< Definition, > ParentFormer< Definition, > + where + Definition : former::FormerDefinition< Storage = ParentFormerStorage >, + { + + #[ inline( always ) ] + pub fn children( self ) -> former::ContainerFormer:: + < + ( String, Child ), + former::HashMapDefinition< String, Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > + > + { + self._children_container_former() + } + + } + + let echo = Child { name : "echo".to_string(), description : "prints all subjects and properties".to_string() }; + let exit = Child { name : "exit".to_string(), description : "just exit".to_string() }; + let ca = Parent::former() + .children() + .add( ( echo.name.clone(), echo ) ) + .add( ( exit.name.clone(), exit ) ) + .end() + .form(); + + dbg!( &ca ); + // > &ca = Parent { + // > child: { + // > "echo": Child { + // > name: "echo", + // > description: "prints all subjects and properties", + // > }, + // > "exit": Child { + // > name: "exit", + // > description: "just exit", + // > }, + // > }, + // > } +} diff --git a/module/core/former/examples/former_custom_default.rs b/module/core/former/examples/former_custom_defaults.rs similarity index 80% rename from module/core/former/examples/former_custom_default.rs rename to module/core/former/examples/former_custom_defaults.rs index 2cc73f3fc0..e7f8e779d7 100644 --- a/module/core/former/examples/former_custom_default.rs +++ b/module/core/former/examples/former_custom_defaults.rs @@ -1,7 +1,10 @@ -//! The `Former` crate enhances struct initialization in Rust by allowing the specification of custom default values for fields through the `default` attribute. + +//! ## Example : Custom Defaults +//! +//! Former allows the specification of custom default values for fields through the `former( default )` attribute. //! //! This feature not only provides a way to set initial values for struct fields without relying on the `Default` trait but also adds flexibility in handling cases where a field's type does not implement `Default`, or a non-standard default value is desired. -//! The above code snippet showcases the `Former` crate's ability to initialize struct fields with custom default values: +//! The example showcases the `Former` crate's ability to initialize struct fields with custom default values: //! - The `number` field is initialized to `5`. //! - The `greeting` field defaults to a greeting message, "Hello, Former!". //! - The `numbers` field starts with a vector containing the integers `10`, `20`, and `30`. @@ -21,11 +24,11 @@ fn main() #[ derive( Debug, PartialEq, Former ) ] pub struct ExampleStruct { - #[ default( 5 ) ] + #[ former( default = 5 ) ] number : i32, - #[ default( "Hello, Former!".to_string() ) ] + #[ former( default = "Hello, Former!".to_string() ) ] greeting : String, - #[ default( vec![ 10, 20, 30 ] ) ] + #[ former( default = vec![ 10, 20, 30 ] ) ] numbers : Vec< i32 >, } diff --git a/module/core/former/examples/former_custom_definition.rs b/module/core/former/examples/former_custom_definition.rs new file mode 100644 index 0000000000..40957cf3ce --- /dev/null +++ b/module/core/former/examples/former_custom_definition.rs @@ -0,0 +1,72 @@ +//! ## Example : Custom Definition +//! +//! Define a custom former definition and custom forming logic, and apply them to a container. +//! +//! The example showcases how to accumulate elements into a container and then transform them into a single result +//! using a custom `FormingEnd` implementation. This pattern is useful for scenarios where the formation process +//! involves aggregation or transformation of input elements into a different type or form. + +#[ cfg( not( all( feature = "derive_former", feature = "enabled" ) ) ) ] +fn main() {} + +#[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +fn main() +{ + // Define a struct `Sum` that will act as a custom former definition. + struct Sum; + + // Implement `FormerDefinitionTypes` for `Sum`. + // This trait defines the types used during the forming process. + impl former::FormerDefinitionTypes for Sum + { + type Storage = Vec; // Container for the integers. + type Formed = i32; // The final type after forming, which is a single integer. + type Context = (); // No additional context is used in this example. + } + + // Implement `FormerMutator` for `Sum`. + // This trait could include custom mutation logic applied during the forming process, but it's empty in this example. + impl former::FormerMutator for Sum + { + } + + // Implement `FormerDefinition` for `Sum`. + // This trait links the custom types to the former. + impl former::FormerDefinition for Sum + { + type Types = Sum; // Associate the `FormerDefinitionTypes` with `Sum`. + type End = Sum; // Use `Sum` itself as the end handler. + type Storage = Vec; // Specify the storage type. + type Formed = i32; // Specify the final formed type. + type Context = (); // Specify the context type, not used here. + } + + // Implement `FormingEnd` for `Sum`. + // This trait handles the final step of the forming process. + impl former::FormingEnd for Sum + { + fn call + ( + &self, + storage: < Sum as former::FormerDefinitionTypes >::Storage, + _context: Option< < Sum as former::FormerDefinitionTypes >::Context> + ) + -> < Sum as former::FormerDefinitionTypes >::Formed + { + // Sum all integers in the storage vector. + storage.iter().sum() + } + } + + // Use the custom `Former` to sum a list of integers. + let got = former::ContainerFormer::::new(Sum) + .add( 1 ) // Add an integer to the storage. + .add( 2 ) // Add another integer. + .add( 10 ) // Add another integer. + .form(); // Perform the form operation, which triggers the summing logic. + let exp = 13; // Expected result after summing 1, 2, and 10. + assert_eq!(got, exp); // Assert the result is as expected. + + dbg!(got); // Debug print the result to verify the output. + // > got = 13 +} diff --git a/module/core/former/examples/former_custom_mutator.rs b/module/core/former/examples/former_custom_mutator.rs new file mode 100644 index 0000000000..5d83fe77c9 --- /dev/null +++ b/module/core/former/examples/former_custom_mutator.rs @@ -0,0 +1,76 @@ +// former_custom_mutator.rs + +//! This example illustrates how to use the `FormerMutator` trait for implementing custom mutations +//! and demonstrates the concept of storage-specific fields in the forming process. +//! +//! #### Storage-Specific Fields +//! +//! Storage-specific fields are intermediate fields that exist only in the storage structure during +//! the forming process. These fields are not present in the final formed structure but are instrumental +//! in complex forming operations, such as conditional mutations, temporary state tracking, or accumulations. +//! +//! These fields are used to manage intermediate data or state that aids in the construction +//! of the final object but does not necessarily have a direct representation in the object's schema. For +//! instance, counters, flags, or temporary computation results that determine the final state of the object. +//! +//! The `FormerMutator` trait facilitates the implementation of custom mutation logic. It acts on the internal +//! state (context and storage) just before the final forming operation is completed, right before the `FormingEnd` +//! callback is invoked. This trait is crucial for making last-minute adjustments or computations based on the +//! accumulated state in the storage. +//! +//! In this example, the fields `a` and `b` are defined only within the storage and used +//! within the custom mutator to enrich or modify the field `c` of the formed entity. This approach +//! allows for a richer and more flexible formation logic that can adapt based on the intermediate state +//! held within the storage. +//! +//! #### Differences from `FormingEnd` +//! +//! Unlike `FormingEnd`, which is primarily responsible for integrating and finalizing the formation process of a field +//! within a parent former, `form_mutation` directly pertains to the entity itself. This method is designed to be independent +//! of whether the forming process is occurring within the context of a superformer or if the structure is a standalone +//! or nested field. This makes `form_mutation` suitable for entity-specific transformations that should not interfere +//! with the hierarchical forming logic managed by `FormingEnd`. +//! + +#[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] +fn main() {} +#[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] +fn main() +{ + use former::Former; + + #[ derive( Debug, PartialEq, Former ) ] + #[ storage_fields( a : i32, b : Option< String > ) ] + #[ mutator( custom = true ) ] + pub struct Struct1 + { + c : String, + } + + // = former mutator + + impl< Context, Formed > former::FormerMutator + for Struct1FormerDefinitionTypes< Context, Formed > + { + //! Mutates the context and storage of the entity just before the formation process completes. + #[ inline ] + fn form_mutation( storage : &mut Self::Storage, _context : &mut ::core::option::Option< Self::Context > ) + { + storage.a.get_or_insert_with( Default::default ); + storage.b.get_or_insert_with( Default::default ); + storage.c = Some( format!( "{:?} - {}", storage.a.unwrap(), storage.b.as_ref().unwrap() ) ); + } + } + + let got = Struct1::former().a( 13 ).b( "abc" ).c( "def" ).form(); + let exp = Struct1 + { + c : "13 - abc".to_string(), + }; + assert_eq!( got, exp ); + dbg!( got ); + // > got = Struct1 { + // > c: "13 - abc", + // > } + +} diff --git a/module/core/former/examples/former_custom_scalar_setter.rs b/module/core/former/examples/former_custom_scalar_setter.rs new file mode 100644 index 0000000000..e5acb1847b --- /dev/null +++ b/module/core/former/examples/former_custom_scalar_setter.rs @@ -0,0 +1,90 @@ +// Example former_custom_scalar_setter.rs + +//! ## Example : Custom Scalar Setter +//! +//! Use of a scalar setter within a `Former` trait implementation to directly assign a `HashMap` of `Child` entities to a `Parent` structure using a custom setter function. +//! +//! Unlike the more complex subform and container setters shown in previous examples, this example focuses on a straightforward approach to directly set a scalar value within a parent entity. The `Parent` struct manages a `HashMap` of `Child` entities, and the scalar setter is used to set the entire `HashMap` directly. The `child` function within `ParentFormer` is a custom subform setter that plays a crucial role. It uniquely employs the `ChildFormer` to add and configure children by their names within the parent's builder pattern. This method demonstrates a powerful technique for integrating subformers that manage specific elements of a container—each child entity in this case. +//! +//! #### Types of Setters +//! +//! It's crucial to understand the differences among subform setters, container setters, and scalar setters: +//! +//! - **Scalar Setter**: Directly sets scalar values or simple fields within the forming entity. Unlike subform or container setters that manage complex objects or collections, scalar setters handle basic data types or individual fields. These are typically straightforward setter methods that do not involve nested formers or additional structuring. +//! +//! - **Container Setter**: Returns a former of the container itself, offering an interface to manage the container as a whole rather than its individual elements. This type of setter is useful for applying configurations or validations to the entire collection, such as a `HashMap` of children. +//! +//! - **Subform Setter**: Returns a former of an element within a container, providing an interface to individually form each element. For example, the `child` method acts as a subform setter, allowing for the addition and configuration of individual `Child` entities within the `Parent`'s `HashMap`. +//! +//! Each type of setter is designed to address different needs in the formation process, ensuring that users can build complex, nested structures or simply set individual field values as required. +//! + +#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +fn main() {} + +// Ensure the example only compiles when the appropriate features are enabled. +#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +fn main() +{ + use collection_tools::HashMap; + use former::Former; + + // Child struct with Former derived for builder pattern support + #[ derive( Debug, PartialEq, Former ) ] + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + pub struct Child + { + name : String, + description : String, + } + + // Parent struct to hold children + #[ derive( Debug, PartialEq, Former ) ] + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + pub struct Parent + { + // Use `hint = true` to gennerate sketch of setter. + #[ scalar( setter = false, hint = false ) ] + children : HashMap< String, Child >, + } + + impl< Definition > ParentFormer< Definition > + where + Definition : former::FormerDefinition< Storage = ParentFormerStorage >, + { + #[ inline ] + pub fn children< Src >( mut self, src : Src ) -> Self + where + Src : ::core::convert::Into< HashMap< String, Child > >, + { + debug_assert!( self.storage.children.is_none() ); + self.storage.children = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); + self + } + } + + let echo = Child { name : "echo".to_string(), description : "prints all subjects and properties".to_string() }; + let exit = Child { name : "exit".to_string(), description : "just exit".to_string() }; + let mut children = HashMap::new(); + children.insert( echo.name.clone(), echo ); + children.insert( exit.name.clone(), exit ); + let ca = Parent::former() + .children( children ) + .form(); + + dbg!( &ca ); + // > &ca = Parent { + // > child: { + // > "echo": Child { + // > name: "echo", + // > description: "prints all subjects and properties", + // > }, + // > "exit": Child { + // > name: "exit", + // > description: "just exit", + // > }, + // > }, + // > } +} diff --git a/module/core/former/examples/former_custom_setter_overriden.rs b/module/core/former/examples/former_custom_setter_overriden.rs index c817ab6872..4723ab16e2 100644 --- a/module/core/former/examples/former_custom_setter_overriden.rs +++ b/module/core/former/examples/former_custom_setter_overriden.rs @@ -15,7 +15,7 @@ fn main() #[ derive( Debug, Former ) ] pub struct StructWithCustomSetters { - #[ setter( false ) ] + #[ scalar( setter = false ) ] word : String, } diff --git a/module/core/former/examples/former_custom_subform_setter.rs b/module/core/former/examples/former_custom_subform_setter.rs new file mode 100644 index 0000000000..bff35ac6ea --- /dev/null +++ b/module/core/former/examples/former_custom_subform_setter.rs @@ -0,0 +1,103 @@ +// Example former_custom_subformer.rs + +//! +//! This example illustrates the implementation of nested builder patterns using the `Former` trait, emphasizing a parent-child relationship. Here, the `Parent` struct utilizes `ChildFormer` as a custom subformer to dynamically manage its `child` field—a `HashMap`. Each child in the `HashMap` is uniquely identified and configured via the `ChildFormer`. +//! +//! The `child` function within `ParentFormer` is a custom subform setter that plays a crucial role. It uniquely employs the `ChildFormer` to add and configure children by their names within the parent's builder pattern. This method demonstrates a powerful technique for integrating subformers that manage specific elements of a container—each child entity in this case. +//! +//! #### Types of Setters +//! +//! It's crucial to understand the differences among subform setters, container setters, and scalar setters: +//! +//! - **Scalar Setter**: Directly sets scalar values or simple fields within the forming entity. Unlike subform or container setters that manage complex objects or collections, scalar setters handle basic data types or individual fields. These are typically straightforward setter methods that do not involve nested formers or additional structuring. +//! +//! - **Container Setter**: Returns a former of the container itself, offering an interface to manage the container as a whole rather than its individual elements. This type of setter is useful for applying configurations or validations to the entire collection, such as a `HashMap` of children. +//! +//! - **Subform Setter**: Returns a former of an element within a container, providing an interface to individually form each element. For example, the `child` method acts as a subform setter, allowing for the addition and configuration of individual `Child` entities within the `Parent`'s `HashMap`. +//! +//! Each type of setter is designed to address different needs in the formation process, ensuring that users can build complex, nested structures or simply set individual field values as required. +//! + +#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +fn main() {} + +// Ensure the example only compiles when the appropriate features are enabled. +#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +fn main() +{ + use collection_tools::HashMap; + use former::Former; + + // Child struct with Former derived for builder pattern support + #[ derive( Debug, PartialEq, Former ) ] + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + pub struct Child + { + name : String, + description : String, + } + + // Parent struct to hold children + #[ derive( Debug, PartialEq, Former ) ] + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + pub struct Parent + { + // Use `hint = true` to gennerate sketch of setter. + #[ subform( setter = false, hint = false ) ] + child : HashMap< String, Child >, + } + + /// Initializes and configures a subformer for adding named child entities. This method leverages an internal function + /// to create and return a configured subformer instance. It allows for the dynamic addition of children with specific names, + /// integrating them into the formation process of the parent entity. + /// + impl< Definition > ParentFormer< Definition > + where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, + { + + #[ inline( always ) ] + pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._child_add::< ChildFormer< _ >, _, >() + .name( name ) + } + + } + + // Required to define how `value` is converted into pair `( key, value )` + impl former::ValToEntry< HashMap< String, Child > > for Child + { + type Entry = ( String, Child ); + #[ inline( always ) ] + fn val_to_entry( self ) -> Self::Entry + { + ( self.name.clone(), self ) + } + } + + let ca = Parent::former() + .child( "echo" ) + .description( "prints all subjects and properties" ) // sets additional properties using custom subformer + .end() + .child( "exit" ) + .description( "just exit" ) // Sets additional properties using using custom subformer + .end() + .form(); + + dbg!( &ca ); + // > &ca = Parent { + // > child: { + // > "echo": Child { + // > name: "echo", + // > description: "prints all subjects and properties", + // > }, + // > "exit": Child { + // > name: "exit", + // > description: "just exit", + // > }, + // > }, + // > } +} diff --git a/module/core/former/examples/former_custom_subform_setter2.rs b/module/core/former/examples/former_custom_subform_setter2.rs new file mode 100644 index 0000000000..7c781d6128 --- /dev/null +++ b/module/core/former/examples/former_custom_subform_setter2.rs @@ -0,0 +1,160 @@ +// Example former_custom_subformer2.rs + +//! +//! This example extends the demonstration of nested builder patterns using the `Former` trait, highlighting a parent-child relationship similar to the `former_custom_subformer.rs`. However, this variant, `former_custom_subformer2.rs`, showcases a more flexible but complex approach to managing the `child` field in the `Parent` struct—a `HashMap` of `Child` entities. Instead of relying on a predefined subformer setter (`_child_add`), this example constructs the subformer logic directly using closures. This method provides greater control over how children are added and managed within the `Parent`. +//! +//! #### Custom Subform Setter +//! +//! The `child` function within `ParentFormer` is a custom subform setter that plays a crucial role. It uniquely employs the `ChildFormer` to add and configure children by their names within the parent's builder pattern. This method demonstrates a powerful technique for integrating subformers that manage specific elements of a container—each child entity in this case. +//! +//! #### Types of Setters +//! +//! It's crucial to understand the differences among subform setters, container setters, and scalar setters: +//! +//! - **Scalar Setter**: Directly sets scalar values or simple fields within the forming entity. Unlike subform or container setters that manage complex objects or collections, scalar setters handle basic data types or individual fields. These are typically straightforward setter methods that do not involve nested formers or additional structuring. +//! +//! - **Container Setter**: Returns a former of the container itself, offering an interface to manage the container as a whole rather than its individual elements. This type of setter is useful for applying configurations or validations to the entire collection, such as a `HashMap` of children. +//! +//! - **Subform Setter**: Returns a former of an element within a container, providing an interface to individually form each element. For example, the `child` method acts as a subform setter, allowing for the addition and configuration of individual `Child` entities within the `Parent`'s `HashMap`. +//! +//! Each type of setter is designed to address different needs in the formation process, ensuring that users can build complex, nested structures or simply set individual field values as required. +//! + +// Ensure the example only compiles when the appropriate features are enabled. +#[ cfg( not( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ) ] +fn main() {} +#[ cfg( all( feature = "enabled", feature = "derive_former", not( feature = "no_std" ) ) ) ] +fn main() +{ + use collection_tools::HashMap; + use former::Former; + + // Child struct with Former derived for builder pattern support + #[ derive( Clone, Debug, PartialEq, Former ) ] + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + pub struct Child + { + name : String, + description : String, + } + + // Parent struct to hold children + #[ derive( Debug, PartialEq, Former ) ] + // Use `#[ debug ]` to expand and debug generate code. + // #[ debug ] + pub struct Parent + { + // Use `hint = true` to gennerate sketch of setter. + #[ subform( setter = false, hint = false ) ] + child : HashMap< String, Child >, + } + + // Use ChildFormer as custom subformer for ParentFormer to add children by name. + impl< Definition > ParentFormer< Definition > + where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, + { + + /// Adds a named child entity to the `Parent`'s `child` field using a custom subformer setup. + /// This method simplifies the process of dynamically adding child entities with specified names, + /// providing a basic yet powerful example of custom subformer implementation. + /// + #[ inline( always ) ] + pub fn child1( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + let on_end = | substorage : ChildFormerStorage, super_former : core::option::Option< Self > | -> Self + { + let mut super_former = super_former.unwrap(); + let preformed = former::StoragePreform::preform( substorage ); + + if super_former.storage.child.is_none() + { + super_former.storage.child = Some( Default::default() ); + } + + // add instance to the container + super_former.storage.child.as_mut().unwrap() + .entry( preformed.name.clone() ) + .or_insert( preformed.clone() ); + + super_former + }; + let subformer = ChildAsSubformer::< Self, _ >::begin( None, Some( self ), former::FormingEndClosure::new( on_end ) ); + subformer.name( name ) + } + + /// Dynamically adds named child entities to the `Parent` structure using a custom subformer. + /// Unlike traditional methods that might use predefined setters like `_child_add`, this function + /// explicitly constructs a subformer setup through a closure to provide greater flexibility and control. + /// + #[ inline( always ) ] + pub fn child2( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + let on_end = | substorage : ChildFormerStorage, super_former : core::option::Option< Self > | -> Self + { + let mut super_former = super_former.unwrap(); + let preformed = former::StoragePreform::preform( substorage ); + + if super_former.storage.child.is_none() + { + super_former.storage.child = Some( Default::default() ); + } + + // add instance to the container + super_former.storage.child.as_mut().unwrap() + .entry( preformed.name.clone() ) + .or_insert( preformed.clone() ); + + // custom logic to add two instances to the container + super_former.storage.child.as_mut().unwrap() + .entry( format!( "{}_2", preformed.name ) ) + .or_insert( preformed.clone() ); + + super_former + }; + let subformer = ChildAsSubformer::< Self, _ >::begin( None, Some( self ), former::FormingEndClosure::new( on_end ) ); + subformer.name( name ) + } + + } + + // Required to define how `value` is converted into pair `( key, value )` + impl former::ValToEntry< HashMap< String, Child > > for Child + { + type Entry = ( String, Child ); + #[ inline( always ) ] + fn val_to_entry( self ) -> Self::Entry + { + ( self.name.clone(), self ) + } + } + + let ca = Parent::former() + .child1( "echo" ) + .description( "prints all subjects and properties" ) // sets additional properties using custom subformer + .end() + .child2( "exit" ) + .description( "just exit" ) // Sets additional properties using using custom subformer + .end() + .form(); + + dbg!( &ca ); + // > &ca = Parent { + // > child: { + // > "echo": Child { + // > name: "echo", + // > description: "prints all subjects and properties", + // > }, + // > "exit": Child { + // > name: "exit", + // > description: "just exit", + // > }, + // > "exit_2": Child { + // > name: "exit", + // > description: "just exit", + // > }, + // > }, + // > } + +} diff --git a/module/core/former/examples/former_custom_subformer.rs b/module/core/former/examples/former_custom_subformer.rs deleted file mode 100644 index 2cf8c2cc7a..0000000000 --- a/module/core/former/examples/former_custom_subformer.rs +++ /dev/null @@ -1,82 +0,0 @@ -//! example of how to use former of another structure as subformer of former of current one -//! function `command` integrate `CommandFormer` into `AggregatorFormer`. - -#[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] -fn main() {} - -#[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] -fn main() -{ - use std::collections::HashMap; - use former::Former; - - // Command struct with Former derived for builder pattern support - #[ derive( Debug, PartialEq, Former ) ] - pub struct Command - { - name : String, - description : String, - } - - // Aggregator struct to hold commands - #[ derive( Debug, PartialEq, Former ) ] - pub struct Aggregator - { - #[ setter( false ) ] - command : HashMap< String, Command >, - } - - // Use CommandFormer as custom subformer for AggregatorFormer to add commands by name. - impl< Context, End > AggregatorFormer< Context, End > - where - End : former::FormingEnd< Aggregator, Context >, - { - #[ inline( always ) ] - pub fn command< IntoName >( self, name : IntoName ) -> CommandFormer< Self, impl former::FormingEnd< Command, Self > > - where - IntoName : core::convert::Into< String >, - { - let on_end = | command : Command, super_former : core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - if let Some( ref mut commands ) = super_former.storage.command - { - commands.insert( command.name.clone(), command ); - } - else - { - let mut commands: HashMap< String, Command > = Default::default(); - commands.insert( command.name.clone(), command ); - super_former.storage.command = Some( commands ); - } - super_former - }; - let former = CommandFormer::begin( None, Some( self ), on_end ); - former.name( name ) - } - // xxx : review - } - - let ca = Aggregator::former() - .command( "echo" ) - .description( "prints all subjects and properties" ) // sets additional properties using custom subformer - .end() - .command( "exit" ) - .description( "just exit" ) // Sets additional properties using using custom subformer - .end() - .form(); - - dbg!( &ca ); - // > &ca = Aggregator { - // > command: { - // > "echo": Command { - // > name: "echo", - // > description: "prints all subjects and properties", - // > }, - // > "exit": Command { - // > name: "exit", - // > description: "just exit", - // > }, - // > }, - // > } -} diff --git a/module/core/former/examples/former_debug.rs b/module/core/former/examples/former_debug.rs index 0a849f684a..8d610eae3c 100644 --- a/module/core/former/examples/former_debug.rs +++ b/module/core/former/examples/former_debug.rs @@ -13,8 +13,8 @@ fn main() #[ derive( Debug, PartialEq, Former ) ] + // Use `#[ debug ]` to expand and debug generate code. // #[ debug ] - // Uncomment to see what derive expand into pub struct UserProfile { age : i32, diff --git a/module/core/former/examples/former_many_fields.rs b/module/core/former/examples/former_many_fields.rs index 6a193b1975..2e04953c77 100644 --- a/module/core/former/examples/former_many_fields.rs +++ b/module/core/former/examples/former_many_fields.rs @@ -7,7 +7,7 @@ //! - `int_1`: A required integer field. //! - `string_1`: A required string field. //! - `vec_1`: A vector of unsigned integers, showcasing collection handling. -//! - `hashmap_strings_1`: A hash map storing key-value pairs, both strings, illustrating how `Former` can manage more complex data structures. +//! - `hashmap_1`: A hash map storing key-value pairs, both strings, illustrating how `Former` can manage more complex data structures. //! - `int_optional_1`: An optional integer field, demonstrating `Former`'s capability to handle optional fields seamlessly. //! - `string_optional_1`: An optional string field, further exemplifying optional field handling. //! @@ -15,7 +15,7 @@ //! //! The builder pattern methods significantly streamline the process of struct initialization, especially for structs with complex or optional fields. By leveraging `Former`, developers can write more readable and maintainable initialization code, avoiding the verbosity and complexity often associated with manual struct instantiation. //! -//! The `dbg!` macro is utilized to print the constructed `Structure1` instance, confirming that all fields are correctly assigned, including the handling of optional fields and collections. This example underscores the power and convenience of using `Former` for struct initialization in Rust projects. +//! The `dbg!` macro is utilized to print the constructed `Structure1` instance, confirming that all fields are correctly assigned, including the handling of optional fields and collections. #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] fn main() {} @@ -31,11 +31,11 @@ fn main() int_1 : i32, string_1 : String, vec_1 : Vec< u32 >, - hashmap_strings_1 : std::collections::HashMap< String, String >, + hashmap_1 : collection_tools::HashMap< String, String >, int_optional_1 : core::option::Option< i32 >, string_optional_1 : Option< String >, } - let hashmap = std::collections::HashMap::from + let hashmap = collection_tools::HashMap::from ([ ( "k1".to_string(), "v1".to_string() ), ( "k2".to_string(), "v2".to_string() ), @@ -45,7 +45,7 @@ fn main() .int_1( 13 ) .string_1( "Abcd".to_string() ) .vec_1( vec![ 1, 3 ] ) - .hashmap_strings_1( hashmap ) + .hashmap_1( hashmap ) .string_optional_1( "dir1" ) .form(); dbg!( &struct1 ); @@ -57,7 +57,7 @@ fn main() // < 1, // < 3, // < ], -// < hashmap_strings_1: { +// < hashmap_1: { // < "k1": "v1", // < "k2": "v2", // < }, diff --git a/module/core/former/examples/former_subformer_hashmap.rs b/module/core/former/examples/former_subformer_hashmap.rs deleted file mode 100644 index 0cfb6dff30..0000000000 --- a/module/core/former/examples/former_subformer_hashmap.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! # Example Usage -//! -//! Demonstrates how to use `HashMapSubformer` with the `HashMapLike` trait to build a `std::collections::HashMap`: -//! - -#[ cfg( not( all( feature = "derive_former", not( feature = "no_std" ) ) ) ) ] -fn main() {} - -#[ cfg( all( feature = "derive_former", not( feature = "no_std" ) ) ) ] -fn main() -{ - use test_tools::exposed::*; - - #[ derive( Debug, PartialEq, former::Former ) ] - pub struct StructWithMap - { - #[ subformer( former::HashMapSubformer ) ] - map : std::collections::HashMap< &'static str, &'static str >, - } - - let struct1 = StructWithMap::former() - .map() - .insert( "a", "b" ) - .insert( "c", "d" ) - .end() - .form() - ; - assert_eq!( struct1, StructWithMap { map : hmap!{ "a" => "b", "c" => "d" } } ); -} diff --git a/module/core/former/examples/former_subformer_hashset.rs b/module/core/former/examples/former_subformer_hashset.rs deleted file mode 100644 index 7ce1d3a365..0000000000 --- a/module/core/former/examples/former_subformer_hashset.rs +++ /dev/null @@ -1,30 +0,0 @@ -//! # Example Usage -//! -//! Demonstrates how to use `HashMapSubformer` with the `HashMapLike` trait to build a `std::collections::HashMap`: -//! - -#[ cfg( not( all( feature = "derive_former", not( feature = "no_std" ) ) ) ) ] -fn main() {} - -#[ cfg( all( feature = "derive_former", not( feature = "no_std" ) ) ) ] -fn main() -{ - use test_tools::exposed::*; - - #[ derive( Debug, PartialEq, former::Former ) ] - pub struct StructWithSet - { - #[ subformer( former::HashSetSubformer ) ] - set : std::collections::HashSet< &'static str >, - } - - let instance = StructWithSet::former() - .set() - .insert("apple") - .insert("banana") - .end() - .form(); - - assert_eq!(instance, StructWithSet { set : hset![ "apple", "banana" ] }); - -} diff --git a/module/core/former/examples/former_subformer_vector.rs b/module/core/former/examples/former_subformer_vector.rs deleted file mode 100644 index 9d7b22bdc0..0000000000 --- a/module/core/former/examples/former_subformer_vector.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! # Example Usage -//! -//! Demonstrates how to use `HashMapSubformer` with the `HashMapLike` trait to build a `std::collections::HashMap`: -//! - -#[ cfg( not( all( feature = "derive_former", not( feature = "no_std" ) ) ) ) ] -fn main() {} - -#[ cfg( all( feature = "derive_former", not( feature = "no_std" ) ) ) ] -fn main() -{ - - #[ derive( Debug, PartialEq, former::Former ) ] - pub struct StructWithVec - { - #[ subformer( former::VectorSubformer ) ] - vec : Vec< &'static str >, - } - - let instance = StructWithVec::former() - .vec() - .push( "apple" ) - .push( "banana" ) - .end() - .form(); - - assert_eq!( instance, StructWithVec { vec: vec![ "apple", "banana" ] } ); - -} diff --git a/module/core/former/examples/former_trivial.rs b/module/core/former/examples/former_trivial.rs index 78331e5577..b330278f68 100644 --- a/module/core/former/examples/former_trivial.rs +++ b/module/core/former/examples/former_trivial.rs @@ -24,7 +24,10 @@ fn main() { use former::Former; + // Use attribute debug to print expanded code. #[ derive( Debug, PartialEq, Former ) ] + // Uncomment to see what derive expand into + // #[ debug ] pub struct UserProfile { age : i32, diff --git a/module/core/former/examples/former_trivial_expaned.rs b/module/core/former/examples/former_trivial_expaned.rs index 5b8d8cd8c2..484f19262b 100644 --- a/module/core/former/examples/former_trivial_expaned.rs +++ b/module/core/former/examples/former_trivial_expaned.rs @@ -1,3 +1,4 @@ +#![ allow( dead_code ) ] //! # Builder Pattern Implementation with Former //! //! This module demonstrates the use of the `Former` trait to apply the builder pattern for Rust structs. @@ -16,17 +17,13 @@ //! This approach abstracts away the need for manually implementing a builder for each struct, making code more readable and maintainable. //! -// xxx : regenerate - -#![ allow( dead_code ) ] - #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ] -fn main(){} - +fn main() {} #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] fn main() { + // Use attribute debug to print expanded code. #[ derive( Debug, PartialEq ) ] pub struct UserProfile { @@ -36,111 +33,204 @@ fn main() } impl UserProfile + where { #[ inline( always ) ] - pub fn former() -> UserProfileFormer< UserProfile, former::ReturnFormed > + pub fn former() -> UserProfileFormer< + UserProfileFormerDefinition< (), UserProfile, former::ReturnPreformed > + > { - UserProfileFormer::< UserProfile, former::ReturnFormed >::new() + UserProfileFormer::< UserProfileFormerDefinition< (), UserProfile, former::ReturnPreformed > >:: + new_coercing(former::ReturnPreformed) } } - #[ derive( Debug, Default ) ] - pub struct UserProfileFormerStorage + // = entity to + + impl< Definition > former::EntityToFormer< Definition > for UserProfile + where + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >, + { + type Former = UserProfileFormer< Definition >; + } + + impl former::EntityToStorage for UserProfile + where + { + type Storage = UserProfileFormerStorage; + } + + impl< Context, Formed, End > former::EntityToDefinition< Context, Formed, End > for UserProfile< > + where + End : former::FormingEnd< UserProfileFormerDefinitionTypes< Context, Formed > >, + { + type Definition = UserProfileFormerDefinition< Context, Formed, End >; + type Types = UserProfileFormerDefinitionTypes< Context, Formed >; + } + + // = definition + + #[derive(Debug)] + pub struct UserProfileFormerDefinitionTypes< Context = (), Formed = UserProfile, > + where + { + _phantom : core::marker::PhantomData< (*const Context, *const Formed) >, + } + + impl< Context, Formed, > ::core::default::Default for UserProfileFormerDefinitionTypes< Context, Formed, > + where + { + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } + } + + impl< Context, Formed, > former::FormerDefinitionTypes for UserProfileFormerDefinitionTypes< Context, Formed, > + where + { + type Storage = UserProfileFormerStorage; + type Formed = Formed; + type Context = Context; + } + + #[derive(Debug)] + pub struct UserProfileFormerDefinition< Context = (), Formed = UserProfile, End = former::ReturnPreformed, > + where + { + _phantom : core::marker::PhantomData< (*const Context, *const Formed, *const End) >, + } + + impl< Context, Formed, End, > ::core::default::Default for UserProfileFormerDefinition< Context, Formed, End, > + where + { + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } + } + + impl< Context, Formed, End, > former::FormerDefinition for UserProfileFormerDefinition< Context, Formed, End, > + where + End : former::FormingEnd< UserProfileFormerDefinitionTypes< Context, Formed, > >, { - age : Option< i32 >, - username : Option< String >, - bio_optional : Option< String >, + type Types = UserProfileFormerDefinitionTypes< Context, Formed, >; + type End = End; + type Storage = UserProfileFormerStorage; + type Formed = Formed; + type Context = Context; } - pub struct UserProfileFormer - < - Context = UserProfile, - End = former::ReturnFormed, - > + impl< Context, Formed, > former::FormerMutator for UserProfileFormerDefinitionTypes< Context, Formed, > + where + {} + + // = storage + + pub struct UserProfileFormerStorage where - End : former::FormingEnd< UserProfile, Context >, { - storage : UserProfileFormerStorage, - context : Option< Context >, - on_end : Option< End >, + pub age : ::core::option::Option< i32 >, + pub username : ::core::option::Option< String >, + pub bio_optional : Option< String >, } - impl< Context, End > UserProfileFormer< Context, End > + impl ::core::default::Default for UserProfileFormerStorage where - End : former::FormingEnd< UserProfile, Context >, { #[ inline( always ) ] - pub fn form( mut self ) -> UserProfile + fn default() -> Self + { + Self + { + age : ::core::option::Option::None, + username : ::core::option::Option::None, + bio_optional : ::core::option::Option::None, + } + } + } + + impl former::Storage for UserProfileFormerStorage + where + { + type Preformed = UserProfile; + } + + impl former::StoragePreform for UserProfileFormerStorage + where + { + // type Preformed = UserProfile; + fn preform(mut self) -> Self::Preformed { - let age = if self.storage.age.is_some() + let age = if self.age.is_some() { - self.storage.age.take().unwrap() + self.age.take().unwrap() } else { - let val : i32 = { - trait NotDefault< T > - { - fn maybe_default( self : &Self ) -> T { panic!( "Field 'age' isn't initialized" ) } - } - trait WithDefault< T > + trait MaybeDefault< T > { - fn maybe_default( self : &Self ) -> T; + fn maybe_default(self : &Self) -> T + { + panic!("Field 'age' isn't initialized") + } } - impl< T > NotDefault< T > for &::core::marker::PhantomData< T > {} - impl< T > WithDefault< T > for ::core::marker::PhantomData< T > - where - T : ::core::default::Default, + impl< T > MaybeDefault< T > for &::core::marker::PhantomData< T > + {} + impl< T > MaybeDefault< T > for ::core::marker::PhantomData< T > + where T : ::core::default::Default, { - fn maybe_default( self : &Self ) -> T + fn maybe_default(self : &Self) -> T { T::default() } } - ( &::core::marker::PhantomData::< i32 > ).maybe_default() - }; - val + (&::core::marker::PhantomData::< i32 >).maybe_default() + } }; - let username = if self.storage.username.is_some() + let username = if self.username.is_some() { - self.storage.username.take().unwrap() + self.username.take().unwrap() } else { - let val : String = { - trait NotDefault< T > + trait MaybeDefault< T > { - fn maybe_default( self : &Self ) -> T { panic!( "Field 'username' isn't initialized" ) } - } - trait WithDefault< T > - { - fn maybe_default( self : &Self ) -> T; + fn maybe_default(self : &Self) -> T + { + panic!("Field 'username' isn't initialized") + } } - impl< T > NotDefault< T > for &::core::marker::PhantomData< T > {} - impl< T > WithDefault< T > for ::core::marker::PhantomData< T > - where - T : ::core::default::Default, + impl< T > MaybeDefault< T > for &::core::marker::PhantomData< T > + {} + impl< T > MaybeDefault< T > for ::core::marker::PhantomData< T > + where T : ::core::default::Default, { - fn maybe_default( self : &Self ) -> T + fn maybe_default(self : &Self) -> T { T::default() } } - ( &::core::marker::PhantomData::< String > ).maybe_default() - }; - val + (&::core::marker::PhantomData::< String >).maybe_default() + } }; - let bio_optional = if self.storage.bio_optional.is_some() + let bio_optional = if self.bio_optional.is_some() { - Option::Some( self.storage.bio_optional.take().unwrap() ) + ::core::option::Option::Some(self.bio_optional.take().unwrap()) } else { - Option::None + ::core::option::Option::None }; - let result = UserProfile + let result = UserProfile::<> { age, username, @@ -148,83 +238,167 @@ fn main() }; return result; } + } + + pub struct UserProfileFormer< Definition = UserProfileFormerDefinition< (), UserProfile, former::ReturnPreformed >, > + where + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >, + { + pub storage : Definition::Storage, + pub context : core::option::Option< Definition::Context >, + pub on_end : core::option::Option< Definition::End >, + } + impl< Definition, > UserProfileFormer< Definition, > + where + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >, Definition::Types : former::FormerDefinitionTypes< Storage = UserProfileFormerStorage >, + { #[ inline( always ) ] - pub fn perform( self ) -> UserProfile + pub fn new(on_end : Definition::End) -> Self { - let result = self.form(); - return result; + Self::begin_coercing(None, None, on_end) } #[ inline( always ) ] - pub fn new() -> UserProfileFormer< UserProfile, former::ReturnFormed > + pub fn new_coercing< IntoEnd >(end : IntoEnd) -> Self + where IntoEnd : Into< Definition::End >, { - UserProfileFormer::< UserProfile, former::ReturnFormed >::begin( None, former::ReturnFormed ) + Self::begin_coercing(None, None, end,) } #[ inline( always ) ] - pub fn begin - ( - context : Option< Context >, - on_end : End, - ) -> Self + pub fn begin(mut storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : ::End,) -> Self { + if storage.is_none() + { + storage = Some(::core::default::Default::default()); + } Self { - storage : core::default::Default::default(), + storage : storage.unwrap(), context : context, - on_end : Option::Some( on_end ), + on_end : ::core::option::Option::Some(on_end), + } + } + + #[ inline( always ) ] + pub fn begin_coercing< IntoEnd >(mut storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : IntoEnd,) -> Self + where IntoEnd : ::core::convert::Into< ::End >, + { + if storage.is_none() + { + storage = Some(::core::default::Default::default()); } + Self + { + storage : storage.unwrap(), + context : context, + on_end : ::core::option::Option::Some(::core::convert::Into::into(on_end)), + } + } + + #[ inline( always ) ] + pub fn form(self) -> ::Formed + { + self.end() } #[ inline( always ) ] - pub fn end( mut self ) -> Context + pub fn end(mut self) -> ::Formed { let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) + let mut context = self.context.take(); + ::form_mutation(&mut self.storage, &mut context); + former::FormingEnd::::call(&on_end, self.storage, context) } - #[ inline ] - pub fn age< Src >( mut self, src : Src ) -> Self - where - Src : Into< i32 >, + #[ inline( always ) ] + pub fn age< Src >(mut self, src : Src) -> Self + where Src : ::core::convert::Into< i32 >, { - debug_assert!( self.storage.age.is_none() ); - self.storage.age = Option::Some( src.into() ); + debug_assert!(self.storage.age.is_none()); + self.storage.age = ::core::option::Option::Some(::core::convert::Into::into(src)); self } - #[ inline ] - pub fn username< Src >( mut self, src : Src ) -> Self - where - Src : Into< String >, + #[ inline( always ) ] + pub fn username< Src >(mut self, src : Src) -> Self + where Src : ::core::convert::Into< String >, { - debug_assert!( self.storage.username.is_none() ); - self.storage.username = Option::Some( src.into() ); + debug_assert!(self.storage.username.is_none()); + self.storage.username = ::core::option::Option::Some(::core::convert::Into::into(src)); self } - #[ inline ] - pub fn bio_optional< Src >( mut self, src : Src ) -> Self - where - Src : Into< String >, + #[ inline( always ) ] + pub fn bio_optional< Src >(mut self, src : Src) -> Self + where Src : ::core::convert::Into< String >, { - debug_assert!( self.storage.bio_optional.is_none() ); - self.storage.bio_optional = Option::Some( src.into() ); + debug_assert!(self.storage.bio_optional.is_none()); + self.storage.bio_optional = ::core::option::Option::Some(::core::convert::Into::into(src)); self } } + impl< Definition, > UserProfileFormer< Definition, > + where + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage, Formed = UserProfile >, + { + pub fn preform(self) -> ::Formed + { + former::StoragePreform::preform(self.storage) + } + } + + impl< Definition, > UserProfileFormer< Definition, > + where + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage, Formed = UserProfile, >, + { + #[ inline( always ) ] + pub fn perform(self) -> Definition::Formed + { + let result = self.form(); + return result; + } + } + + impl< Definition > former::FormerBegin< Definition > for UserProfileFormer< Definition, > + where + Definition : former::FormerDefinition< Storage = UserProfileFormerStorage >, + { + #[ inline( always ) ] + fn former_begin(storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : Definition::End,) -> Self + { + debug_assert!(storage.is_none()); + Self::begin(None, context, on_end) + } + } + + // = as subformer + + pub type UserProfileAsSubformer< Superformer, End > = + UserProfileFormer< UserProfileFormerDefinition< Superformer, Superformer, End, >, >; + + pub trait UserProfileAsSubformerEnd< SuperFormer > + where + Self : former::FormingEnd< UserProfileFormerDefinitionTypes< SuperFormer, SuperFormer >, >, {} + + impl< SuperFormer, T > UserProfileAsSubformerEnd< SuperFormer > for T + where + Self : former::FormingEnd< UserProfileFormerDefinitionTypes< SuperFormer, SuperFormer >, >, + {} + + // = end + let profile = UserProfile::former() .age( 30 ) .username( "JohnDoe".to_string() ) - .bio_optional( "Software Developer".to_string() ) + .bio_optional( "Software Developer".to_string() ) // Optionally provide a bio .form(); - dbg!( &profile ); + // Expected output: + // // &profile = UserProfile { // age: 30, // username: "JohnDoe", diff --git a/module/core/former/src/axiomatic.rs b/module/core/former/src/axiomatic.rs index 161a0f3ea8..e69de29bb2 100644 --- a/module/core/former/src/axiomatic.rs +++ b/module/core/former/src/axiomatic.rs @@ -1,192 +0,0 @@ -//! .... - -/// Defines a handler for the end of a subforming process, enabling the return of the original context. -/// -/// This trait is designed to be flexible, allowing for various end-of-forming behaviors in builder patterns. -/// Implementors can define how to transform or pass through the context during the forming process's completion. -/// -/// # Parameters -/// - `Formed`: The type of the container being processed. -/// - `Context`: The type of the context that might be altered or returned upon completion. -pub trait FormingEnd< Formed, Context > -{ - /// Called at the end of the subforming process to return the modified or original context. - /// - /// # Parameters - /// - `container`: The container being processed. - /// - `context`: Optional context to be transformed or returned. - /// - /// # Returns - /// Returns the transformed or original context based on the implementation. - #[ allow( dead_code ) ] - fn call( &self, storage : Formed, context : core::option::Option< Context > ) -> Context; -} - -impl< Storage, Context, F > FormingEnd< Storage, Context > for F -where - F : Fn( Storage, core::option::Option< Context > ) -> Context, -{ - #[ inline( always ) ] - fn call( &self, storage : Storage, context : core::option::Option< Context > ) -> Context - { - self( storage, context ) - } -} - -/// A wrapper around a closure to be used as a `FormingEnd`. -/// -/// This struct allows for dynamic dispatch of a closure that matches the -/// `FormingEnd` trait's `call` method signature. It is useful for cases where -/// a closure needs to be stored or passed around as an object implementing -/// `FormingEnd`. -/// -/// # Type Parameters -/// -/// * `Storage` - The type of the container being processed. This type is passed to the closure -/// when it's called. -/// * `Context` - The type of the context that may be altered or returned by the closure. -/// This allows for flexible manipulation of context based on the container. -#[ cfg( not( feature = "no_std" ) ) ] -pub struct FormingEndWrapper< Storage, Context > -{ - closure : Box< dyn Fn( Storage, Option< Context > ) -> Context >, - _marker : std::marker::PhantomData< Storage >, -} - -#[ cfg( not( feature = "no_std" ) ) ] -impl< Storage, Context > FormingEndWrapper< Storage, Context > -{ - /// Constructs a new `FormingEndWrapper` with the provided closure. - /// - /// # Parameters - /// - /// * `closure` - A closure that matches the expected signature for transforming a container - /// and context into a new context. This closure is stored and called by the - /// `call` method of the `FormingEnd` trait implementation. - /// - /// # Returns - /// - /// Returns an instance of `FormingEndWrapper` encapsulating the provided closure. - pub fn new( closure : impl Fn( Storage, Option< Context > ) -> Context + 'static ) -> Self - { - Self - { - closure : Box::new( closure ), - _marker : std::marker::PhantomData - } - } -} - -#[ cfg( not( feature = "no_std" ) ) ] -use std::fmt; -#[ cfg( not( feature = "no_std" ) ) ] -impl< Storage, Context > fmt::Debug for FormingEndWrapper< Storage, Context > -{ - fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result - { - f.debug_struct( "FormingEndWrapper" ) - .field( "closure", &format_args!{ "- closure -" } ) - .field( "_marker", &self._marker ) - .finish() - } -} - -#[ cfg( not( feature = "no_std" ) ) ] -impl< Storage, Context > FormingEnd< Storage, Context > -for FormingEndWrapper< Storage, Context > -{ - fn call( &self, storage : Storage, context : Option< Context > ) -> Context - { - ( self.closure )( storage, context ) - } -} - -// /// A `FormingEnd` implementation that returns the original context without any modifications. -// /// -// /// This struct is used when no end-of-forming processing is needed, and the original context is to be returned as-is. -// #[ derive( Debug, Default ) ] -// pub struct NoEnd; -// -// impl< Formed, Context > FormingEnd< Formed, Context > -// for NoEnd -// { -// #[ inline( always ) ] -// fn call( &self, _formed : Formed, context : core::option::Option< Context > ) -> Context -// { -// context.unwrap() -// } -// } - -/// A `FormingEnd` implementation that returns the formed container itself instead of the context. -/// -/// This struct is useful when the forming process should result in the formed container being returned directly, -/// bypassing any additional context processing. It simplifies scenarios where the formed container is the final result. -#[ derive( Debug, Default ) ] -pub struct ReturnFormed; - -impl< Storage > FormingEnd< Storage, Storage > -for ReturnFormed -{ - #[ inline( always ) ] - fn call( &self, storage : Storage, _context : core::option::Option< Storage > ) -> Storage - { - storage - } -} - -// - -/// A trait defining the initialization process for a subformer with contextual linkage. -/// -/// This trait is designed for types that need to initiate a subforming process, -/// passing themselves as the context and specifying a closure or handler (`on_end`) to be -/// called upon completion. It facilitates the construction of builder pattern chains -/// that maintain stateful context through each step of the process. -/// -/// # Type Parameters -/// -/// * `Formed` - Represents the type that is being constructed or transformed by the subformer. -/// * `Context` - Denotes the contextual information or the environment in which `Formed` is being formed. -/// This could be a reference to a parent builder, configuration settings, or any other -/// relevant state. -/// -/// # Associated Types -/// -/// * `End` - Specifies the trait bound for the closure or handler that gets called at the completion -/// of the subforming process. This type must implement the `FormingEnd` -/// trait, which defines how the final transformation or construction of `Formed` is handled, -/// potentially using the provided `Context`. -/// - -pub trait FormerBegin< Storage, Formed, Context > -{ - - /// * `End` - Specifies the trait bound for the closure or handler that gets called at the completion - /// of the subforming process. This type must implement the `FormingEnd` - /// trait, which defines how the final transformation or construction of `Formed` is handled, - /// potentially using the provided `Context`. - type End : FormingEnd< Formed, Context >; - - /// Initializes the subforming process by setting the context and specifying an `on_end` completion handler. - /// - /// This function is the entry point for initiating a subforming sequence, allowing the caller - /// to establish initial contextual information and define how the process concludes. - /// - /// # Parameters - /// - /// * `context` - An optional parameter providing initial context for the subforming process. This - /// might include configuration data, references to parent structures, or any state - /// relevant to the formation of `Formed`. - /// - /// * `on_end` - A closure or handler of type `Self::End` that is invoked at the completion of - /// the subforming process. This handler is responsible for applying any final transformations - /// to `Formed` and potentially utilizing `Context` to influence the outcome. - /// - fn _begin - ( - storage : core::option::Option< Storage >, - context : core::option::Option< Context >, - on_end : Self::End, - ) -> Self; - -} diff --git a/module/core/former/src/axiomatic2.rs b/module/core/former/src/axiomatic2.rs deleted file mode 100644 index 16f43060ef..0000000000 --- a/module/core/former/src/axiomatic2.rs +++ /dev/null @@ -1,207 +0,0 @@ -//! .... - -/// xxx2 -pub trait StoragePerform -{ - type Formed; - fn preform( self ) -> Self::Formed; -} - -/// xxx2 -pub trait FormerDescriptor -{ - type Storage : StoragePerform< Formed = Self::Formed >; - type Formed; - // type Former; -} - -pub trait FormerDefinition -{ - type Storage : StoragePerform< Formed = Self::Formed >; - type Formed; - type Context; - type FormerDescriptor : FormerDescriptor< Storage = Self::Storage, Formed = Self::Formed >; - type End : FormingEnd2< Self::FormerDescriptor, Self::Context >; -} - -/// Defines a handler for the end of a subforming process, enabling the return of the original context. -/// -/// This trait is designed to be flexible, allowing for various end-of-forming behaviors in builder patterns. -/// Implementors can define how to transform or pass through the context during the forming process's completion. -/// -/// # Parameters -/// - `Storage`: The type of the container being processed. -/// - `Context`: The type of the context that might be altered or returned upon completion. -pub trait FormingEnd2< Former : FormerDescriptor, Context > -{ - /// Called at the end of the subforming process to return the modified or original context. - /// - /// # Parameters - /// - `container`: The container being processed. - /// - `context`: Optional context to be transformed or returned. - /// - /// # Returns - /// Returns the transformed or original context based on the implementation. - // #[ allow( dead_code ) ] - fn call( &self, storage : Former::Storage, context : core::option::Option< Context > ) -> Former::Formed; -} - -impl< Former : FormerDescriptor, Context, F > FormingEnd2< Former, Context > for F -where - F : Fn( Former::Storage, core::option::Option< Context > ) -> Former::Formed, -{ - #[ inline( always ) ] - fn call( &self, storage : Former::Storage, context : core::option::Option< Context > ) -> Former::Formed - { - self( storage, context ) - } -} - -/// A `FormingEnd2` implementation that returns the formed container itself instead of the context. -/// -/// This struct is useful when the forming process should result in the formed container being returned directly, -/// bypassing any additional context processing. It simplifies scenarios where the formed container is the final result. -#[ derive( Debug, Default ) ] -pub struct ReturnStorage2; - -impl< Former : FormerDescriptor > FormingEnd2< Former, () > -for ReturnStorage2 -// where - // Storage : StoragePreform<>, -{ - #[ inline( always ) ] - fn call( &self, storage : Former::Storage, _context : core::option::Option< () > ) -> Former::Formed - { - storage.preform() - } -} - -/// A wrapper around a closure to be used as a `FormingEnd2`. -/// -/// This struct allows for dynamic dispatch of a closure that matches the -/// `FormingEnd2` trait's `call` method signature. It is useful for cases where -/// a closure needs to be stored or passed around as an object implementing -/// `FormingEnd2`. -/// -/// # Type Parameters -/// -/// * `Storage` - The type of the container being processed. This type is passed to the closure -/// when it's called. -/// * `Context` - The type of the context that may be altered or returned by the closure. -/// This allows for flexible manipulation of context based on the container. -#[ cfg( not( feature = "no_std" ) ) ] -pub struct FormingEndWrapper2< Former : FormerDescriptor, Context > -{ - closure : Box< dyn Fn( Former::Storage, Option< Context > ) -> Former::Formed >, - _marker : std::marker::PhantomData< Former::Storage >, -} - -#[ cfg( not( feature = "no_std" ) ) ] -impl< Former : FormerDescriptor, Context > FormingEndWrapper2< Former, Context > -{ - /// Constructs a new `FormingEndWrapper2` with the provided closure. - /// - /// # Parameters - /// - /// * `closure` - A closure that matches the expected signature for transforming a container - /// and context into a new context. This closure is stored and called by the - /// `call` method of the `FormingEnd2` trait implementation. - /// - /// # Returns - /// - /// Returns an instance of `FormingEndWrapper2` encapsulating the provided closure. - pub fn new( closure : impl Fn( Former::Storage, Option< Context > ) -> Former::Formed + 'static ) -> Self - { - Self - { - closure : Box::new( closure ), - _marker : std::marker::PhantomData - } - } -} - -#[ cfg( not( feature = "no_std" ) ) ] -use std::fmt; -#[ cfg( not( feature = "no_std" ) ) ] -impl< Former : FormerDescriptor, Context > fmt::Debug for FormingEndWrapper2< Former, Context > -{ - fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result - { - f.debug_struct( "FormingEndWrapper2" ) - .field( "closure", &format_args!{ "- closure -" } ) - .field( "_marker", &self._marker ) - .finish() - } -} - -#[ cfg( not( feature = "no_std" ) ) ] -impl< Former : FormerDescriptor, Context > FormingEnd2< Former, Context > -for FormingEndWrapper2< Former, Context > -{ - fn call( &self, storage : Former::Storage, context : Option< Context > ) -> Former::Formed - { - ( self.closure )( storage, context ) - } -} - -// - -/// A trait for initiating a structured subforming process with contextual and intermediary storage linkage. -/// -/// This trait facilitates the creation of a subformer that carries through a builder pattern chain, -/// utilizing intermediary storage for accumulating state or data before finally transforming it into -/// a `Formed` structure. It is designed for scenarios where a multi-step construction or transformation -/// process benefits from maintaining both transient state (`Storage`) and contextual information (`Context`), -/// before concluding with the generation of a final product (`Formed`). -/// -/// # Type Parameters -/// -/// * `Storage` - Represents a mutable intermediary storage structure used throughout the subforming process -/// to accumulate data, state, or partial computations. This storage is internal to the -/// subformer and is eventually converted into the final `Formed` structure by the subformer, -/// not directly by implementations of this trait. -/// -/// * `Formed` - Denotes the final type that results from the subforming process. This is the intended outcome -/// of the builder chain, constructed or transformed from the `Storage` with consideration of -/// the provided `Context`. -/// -/// * `Context` - Specifies the contextual backdrop against which the subforming process unfolds. This could -/// encompass references to parent builders, configuration data, or any state influencing how -/// `Storage` transitions into `Formed`. -/// -/// # Functions -/// -/// * `_begin` - This function launches the subforming process, marking the start of a construction or transformation -/// sequence defined by the implementing type. It establishes the foundational `Storage` and `Context`, -/// alongside specifying an `on_end` completion handler that dictates the final conversion into `Formed`. -/// -/// The `FormerBegin2` trait, by decoupling `Storage` from `Formed` and introducing a contextual layer, enables -/// sophisticated and flexible construction patterns conducive to complex data transformations or object creation -/// sequences within builder patterns. - -// xxx2 : change sequence -pub trait FormerBegin2< Former : FormerDescriptor, Context > -{ - - /// * `End` - A trait bound marking the closure or handler invoked upon completing the subforming process. Implementers - /// of this trait (`End`) are tasked with applying the final transformations that transition `Storage` - /// into `Formed`, optionally utilizing `Context` to guide this transformation. It is crucial that this - /// associated type satisfies the `FormingEnd2` trait, defining the precise mechanics of - /// how the subformer concludes its operation. - type End : FormingEnd2< Former, Context >; - - /// Launches the subforming process with an initial storage and context, setting up an `on_end` completion handler. - /// - /// # Parameters - /// - /// * `storage` - An optional initial state for the intermediary storage structure. - /// * `context` - An optional initial setting providing contextual information for the subforming process. - /// * `on_end` - A completion handler responsible for transforming the accumulated `Storage` into the final `Formed` structure. - fn _begin - ( - storage : core::option::Option< Former::Storage >, - context : core::option::Option< Context >, - on_end : Self::End, - ) -> Self; - -} diff --git a/module/core/former/src/container.rs b/module/core/former/src/container.rs index cfdbfdb4d1..2e0897da73 100644 --- a/module/core/former/src/container.rs +++ b/module/core/former/src/container.rs @@ -1,190 +1,564 @@ -//! Interface for containers. - -/// A trait defining the capability to add elements to a container. -/// -/// This trait should be implemented by container types that require a generic interface -/// for adding new elements. It abstracts over the specific details of how elements are -/// added to the container, providing a consistent API regardless of the underlying -/// container's structure. -/// -/// # Type Parameters -/// -/// - There are no explicit type parameters for the trait itself, but implementers will -/// specify their own types as needed. -/// -/// # Associated Types -/// -/// * `Element`: The type of elements that can be added to the container. This type is -/// defined by the implementer of the trait, allowing for flexibility in the kinds of -/// elements different containers can accept. -/// -pub trait ContainerAdd +//! +//! This module defines traits and structures that facilitate the management and manipulation +//! of container data structures within a builder pattern context. It provides a comprehensive +//! interface for adding, managing, and converting elements within various types of containers, +//! such as vectors, hash maps, and custom container implementations. +//! + +/// Internal namespace. +pub( crate ) mod private { - /// The type of elements to be added to the container. - type Element; - /// Adds an element to the container. - /// - /// Implementations of this function should add the provided element to the container, - /// respecting the container's specific semantics for element addition (e.g., handling - /// duplicates or maintaining order). The function returns a boolean indicating whether - /// the addition was successful. - /// - /// # Parameters - /// - /// * `e`: The element to be added to the container. The type of the element is specified - /// by the associated `Element` type. - /// - /// # Returns - /// - /// Returns `true` if the element was successfully added to the container, or `false` if - /// the addition failed. Failure conditions are defined by the implementer but may include - /// situations like the container being at capacity or the element already existing in a - /// set. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use former::ContainerAdd; + use crate::*; + + /// Facilitates the conversion of container entries to their corresponding value representations. /// - /// struct MyContainer - /// { - /// elements : Vec< i32 >, - /// } + /// This trait is utilized to transform an entry of a container into a value, abstracting the operation of containers + /// like vectors or hash maps. It ensures that even in complex container structures, entries can be seamlessly managed + /// and manipulated as values. + pub trait EntryToVal< Container > + { + /// The type of values stored in the container. This might be distinct from `Entry` in complex containers. + /// For example, in a `HashMap`, while `Entry` might be a ( key, value ) tuple, `Val` might only be the value part. + type Val; + + /// Converts an entry into a value representation specific to the type of container. This conversion is crucial + /// for handling operations on entries, especially when they need to be treated or accessed as individual values, + /// such as retrieving the value part from a key-value pair in a hash map. + fn entry_to_val( self ) -> Self::Val; + } + + impl< C, E > EntryToVal< C > for E + where + C : Container< Entry = E >, + { + type Val = C::Val; + + fn entry_to_val( self ) -> Self::Val + { + C::entry_to_val( self ) + } + } + + /// Provides a mechanism for transforming a value back into a container-specific entry format. /// - /// impl ContainerAdd for MyContainer - /// { - /// type Element = i32; + /// This trait is particularly valuable in scenarios where the operations on a container require + /// not just the manipulation of values but also the re-integration of these values as entries. + /// It is especially crucial in complex data structures, such as `HashMap`s, where entries + /// often involve a key-value pair, and simple values need to be restructured to fit this model + /// for operations like insertion or update. + + pub trait ContainerValToEntry< Val > + { + /// The specific type of entry that corresponds to the value within the container. + /// For example, in a `HashMap`, this might be a tuple of a key and a value. + type Entry; + + /// Converts a value into a container-specific entry, facilitating operations that modify + /// the container. This method is key for ensuring that values can be correctly integrated + /// back into the container, particularly when the entry type is more complex than the value. + /// + /// # Parameters + /// * `val` - The value to be converted into an entry. + /// + /// # Returns + /// Returns the entry constructed from the provided value, ready for insertion or other modifications. + /// + /// # Example + /// ``` + /// use former::ContainerValToEntry; + /// + /// struct PairMap; + /// + /// impl ContainerValToEntry< ( i32, i32 ) > for PairMap + /// { + /// type Entry = ( String, i32 ); + /// + /// fn val_to_entry( val : ( i32, i32 ) ) -> Self::Entry + /// { + /// (val.0.to_string(), val.1) + /// } + /// } + /// ``` + fn val_to_entry( val : Val ) -> Self::Entry; + } + + /// Facilitates the conversion of values back into entries for specific container types. /// - /// fn add( &mut self, e : Self::Element ) -> bool - /// { - /// if self.elements.contains( &e ) - /// { - /// false - /// } - /// else - /// { - /// self.elements.push( e ); - /// true - /// } - /// } - /// } + /// This trait wraps the functionality of `ContainerValToEntry`, providing a more ergonomic + /// interface for converting values directly within the type they pertain to. It is useful + /// in maintaining the integrity of container operations, especially when dealing with + /// sophisticated structures that separate the concept of values and entries, such as `HashMap`s + /// and other associative containers. + pub trait ValToEntry< Container > + { + /// Represents the type of entry that corresponds to the value within the container. + type Entry; + + /// Transforms the instance (value) into an entry compatible with the specified container. + /// This conversion is essential for operations like insertion or modification within the container, + /// where the value needs to be formatted as an entry. + /// + /// # Returns + /// Returns the entry constructed from the instance of the value, ready for integration into the container. + /// + /// # Example + /// ``` + /// use former::ValToEntry; + /// + /// struct PairMap; + /// + /// impl ValToEntry< PairMap > for (i32, i32) + /// { + /// type Entry = ( String, i32 ); + /// + /// fn val_to_entry( self ) -> Self::Entry + /// { + /// (self.0.to_string(), self.1) + /// } + /// } + /// ``` + fn val_to_entry( self ) -> Self::Entry; + } + + impl< C, Val > ValToEntry< C > for Val + where + C : ContainerValToEntry< Val >, + { + type Entry = C::Entry; + + /// Invokes the `val_to_entry` function of the `ContainerValToEntry` trait to convert the value to an entry. + fn val_to_entry( self ) -> C::Entry + { + C::val_to_entry( self ) + } + } + + /// Represents a container by defining the types of entries and values it handles. /// - /// let mut container = MyContainer { elements : vec![] }; - /// assert!( container.add( 10 ) ); // Returns true, element added - /// assert!( !container.add( 10 ) ); // Returns false, element already exists - /// ``` + /// This trait abstracts the nature of containers in data structures, facilitating the handling of contained + /// entries and values, especially in scenarios where the structure of the container allows for complex relationships, + /// such as `HashMap`s. It not only identifies what constitutes an entry and a value in the context of the container + /// but also provides utility for converting between these two, which is critical in operations involving entry manipulation + /// and value retrieval. + + pub trait Container + { + /// The type of entries that can be added to the container. This type can differ from `Val` in containers like `HashMap`, + /// where an entry might represent a key-value pair, and `Val` could represent just the value or the key. + type Entry; + + /// The type of values stored in the container. This might be distinct from `Entry` in complex containers. + /// For example, in a `HashMap`, while `Entry` might be a ( key, value ) tuple, `Val` might only be the value part. + type Val; + + /// Converts an entry to its corresponding value within the container. This function is essential for abstracting + /// the container's internal representation from the values it manipulates. + fn entry_to_val( e : Self::Entry ) -> Self::Val; + } + + /// Provides functionality to add individual entries to a container. /// - /// This example demonstrates a simple container that does not allow duplicate elements. - /// The `add` method checks for the existence of the element before adding it, returning - /// `false` if the element is already present. + /// This trait extends the basic `Container` trait by introducing a method to add entries to a container. + /// It is designed to handle the container's specific requirements and rules for adding entries, such as + /// managing duplicates, maintaining order, or handling capacity constraints. + pub trait ContainerAdd : Container + { + /// Adds an entry to the container and returns a boolean indicating the success of the operation. + /// + /// Implementations should ensure that the entry is added according to the rules of the container, + /// which might involve checking for duplicates, ordering, or capacity limits. + /// + /// # Parameters + /// + /// * `e`: The entry to be added to the container, where the type `Entry` is defined by the `Container` trait. + /// + /// # Returns + /// + /// Returns `true` if the entry was successfully added, or `false` if not added due to reasons such as + /// the entry already existing in the container or the container reaching its capacity. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + /// + /// use former::{ Container, ContainerAdd }; + /// + /// struct MyContainer + /// { + /// entries : Vec< i32 >, + /// } + /// + /// impl Container for MyContainer + /// { + /// type Entry = i32; + /// type Val = i32; + /// + /// #[ inline( always ) ] + /// fn entry_to_val( e : Self::Entry ) -> Self::Val + /// { + /// e + /// } + /// + /// } + /// + /// impl ContainerAdd for MyContainer + /// { + /// fn add( &mut self, e : Self::Entry ) -> bool + /// { + /// if self.entries.contains( &e ) + /// { + /// false + /// } + /// else + /// { + /// self.entries.push( e ); + /// true + /// } + /// } + /// } + /// + /// let mut container = MyContainer { entries : vec![] }; + /// assert!( container.add( 10 ) ); // Returns true, entry added + /// assert!( !container.add( 10 ) ); // Returns false, entry already exists + /// ``` + fn add( &mut self, e : Self::Entry ) -> bool; + } + + /// Defines the capability to replace all entries in a container with a new set of entries. /// - fn add( &mut self, e : Self::Element ) -> bool; + /// This trait extends the `Container` trait by providing a method to replace the existing entries in + /// the container with a new set. This can be useful for resetting the container's contents or bulk-updating + /// them based on external criteria or operations. + pub trait ContainerAssign : Container + where + Self : IntoIterator< Item = Self::Entry >, + { + /// Replaces all entries in the container with the provided entries and returns the count of new entries added. + /// + /// This method clears the existing entries and populates the container with new ones provided by an iterator. + /// It is ideal for scenarios where the container needs to be refreshed or updated with a new batch of entries. + /// + /// # Parameters + /// + /// * `entries` : An iterator over the entries to be added to the container. The entries must conform to + /// the `Entry` type defined by the `Container` trait. + /// + /// # Returns + /// + /// Returns the number of entries successfully added to the container. This count may differ from the total + /// number of entries in the iterator if the container imposes restrictions such as capacity limits or duplicate + /// handling. + /// + /// # Examples + /// + /// ```rust + /// use former::{ Container, ContainerAssign }; + /// + /// struct MyContainer + /// { + /// entries : Vec< i32 >, + /// } + /// + /// impl Container for MyContainer + /// { + /// type Entry = i32; + /// type Val = i32; + /// + /// #[ inline( always ) ] + /// fn entry_to_val( e : Self::Entry ) -> Self::Val + /// { + /// e + /// } + /// + /// } + /// + /// impl IntoIterator for MyContainer + /// { + /// type Item = i32; + /// type IntoIter = std::vec::IntoIter< i32 >; + /// // type IntoIter = collection_tools::vec::IntoIter< i32 >; + /// // qqq : zzz : make sure collection_tools has itearators + /// + /// fn into_iter( self ) -> Self::IntoIter + /// { + /// self.entries.into_iter() // Create an iterator from the internal HashSet. + /// } + /// } + /// + /// impl ContainerAssign for MyContainer + /// { + /// fn assign< Entries >( &mut self, entries : Entries ) -> usize + /// where + /// Entries : IntoIterator< Item = Self::Entry >, + /// { + /// self.entries.clear(); + /// self.entries.extend( entries ); + /// self.entries.len() + /// } + /// } + /// + /// let mut container = MyContainer { entries : vec![ 1, 2, 3 ] }; + /// let new_elements = vec![ 4, 5, 6 ]; + /// assert_eq!( container.assign( new_elements ), 3 ); // Container now contains [ 4, 5, 6 ] + /// ``` + fn assign< Entries >( &mut self, entries : Entries ) -> usize + where + Entries : IntoIterator< Item = Self::Entry >; + } -} + // = -impl< T > ContainerAdd for collection_tools::Vec< T > -{ - type Element = T; + /// A builder structure for constructing containers with a fluent and flexible interface. + #[ derive( Default ) ] + pub struct ContainerFormer< E, Definition > + where + Definition : FormerDefinition, + Definition::Storage : ContainerAdd< Entry = E >, + { + storage : Definition::Storage, + context : core::option::Option< Definition::Context >, + on_end : core::option::Option< Definition::End >, + } - #[ inline( always ) ] - fn add( &mut self, e : Self::Element ) -> bool + use core::fmt; + impl< E, Definition > fmt::Debug for ContainerFormer< E, Definition > + where + Definition : FormerDefinition, + Definition::Storage : ContainerAdd< Entry = E >, { - self.push( e ); - true + fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + { + f + .debug_struct( "ContainerFormer" ) + .field( "storage", &"Storage Present" ) + .field( "context", &self.context.as_ref().map( |_| "Context Present" ) ) + .field( "on_end", &self.on_end.as_ref().map( |_| "End Present" ) ) + .finish() + } } -} + impl< E, Definition > ContainerFormer< E, Definition > + where + Definition : FormerDefinition, + Definition::Storage : ContainerAdd< Entry = E >, + { + /// Begins the construction process of a container with optional initial storage and context, + /// setting up an `on_end` completion handler to finalize the container's construction. + #[ inline( always ) ] + pub fn begin + ( + mut storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : Definition::End, + ) + -> Self + { + if storage.is_none() + { + storage = Some( core::default::Default::default() ); + } + Self + { + storage : storage.unwrap(), + context, + on_end : Some( on_end ), + } + } -impl< E > ContainerAdd for collection_tools::HashSet< E > -where - E : core::cmp::Eq + core::hash::Hash, -{ - type Element = E; + /// Provides a variation of the `begin` method allowing for coercion of the end handler, + /// facilitating ease of integration with different end conditions. + #[ inline( always ) ] + pub fn begin_coercing< IntoEnd > + ( + mut storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : IntoEnd, + ) + -> Self + where + IntoEnd : Into< Definition::End >, + { + if storage.is_none() + { + storage = Some( core::default::Default::default() ); + } + Self + { + storage : storage.unwrap(), + context, + on_end : Some( on_end.into() ), + } + } + + /// Finalizes the building process, returning the formed or a context incorporating it. + #[ inline( always ) ] + pub fn end( mut self ) -> Definition::Formed + { + let on_end = self.on_end.take().unwrap(); + let context = self.context.take(); + on_end.call( self.storage, context ) + } - #[ inline( always ) ] - fn add( &mut self, e : Self::Element ) -> bool + /// Alias for the `end` method to align with typical builder pattern terminologies. + #[ inline( always ) ] + pub fn form( self ) -> Definition::Formed + { + self.end() + } + + /// Replaces the current storage with a provided storage, allowing for resetting or + /// redirection of the building process. + #[ inline( always ) ] + pub fn replace( mut self, storage : Definition::Storage ) -> Self + { + self.storage = storage; + self + } + } + + impl< E, Storage, Formed, Definition > ContainerFormer< E, Definition > + where + Definition : FormerDefinition< Context = (), Storage = Storage, Formed = Formed >, + Definition::Storage : ContainerAdd< Entry = E >, { - self.insert( e ) + /// Constructs a new `ContainerFormer` instance, starting with an empty storage. + /// This method serves as the entry point for the builder pattern, facilitating the + /// creation of a new container. + #[ inline( always ) ] + pub fn new( end : Definition::End ) -> Self + { + Self::begin + ( + None, + None, + end, + ) + } + + /// Variant of the `new` method allowing for end condition coercion, providing flexibility + /// in specifying different types of end conditions dynamically. + #[ inline( always ) ] + pub fn new_coercing< IntoEnd >( end : IntoEnd ) -> Self + where + IntoEnd : Into< Definition::End >, + { + Self::begin + ( + None, + None, + end.into(), + ) + } } -} + impl< E, Definition > ContainerFormer< E, Definition > + where + Definition : FormerDefinition, + Definition::Storage : ContainerAdd< Entry = E >, + { -impl< K, V > ContainerAdd for collection_tools::HashMap< K, V > -where - K : core::cmp::Eq + core::hash::Hash, -{ - type Element = ( K, V ); + /// Appends an entry to the end of the storage, expanding the internal collection. + #[ inline( always ) ] + pub fn add< IntoElement >( mut self, entry : IntoElement ) -> Self + where IntoElement : core::convert::Into< E >, + { + ContainerAdd::add( &mut self.storage, entry.into() ); + self + } + + } - #[ inline( always ) ] - fn add( &mut self, ( k, v ) : Self::Element ) -> bool + // + + impl< E, Definition > FormerBegin< Definition > + for ContainerFormer< E, Definition > + where + Definition : FormerDefinition, + Definition::Storage : ContainerAdd< Entry = E >, { - self.insert( k, v ).map_or_else( || true, | _ | false ) + + #[ inline( always ) ] + fn former_begin + ( + storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : Definition::End, + ) + -> Self + { + Self::begin( storage, context, on_end ) + } + } } -// qqq : implement for other containers +/// Former of a vector. +mod vector; +/// Former of a hash map. +mod hash_map; +/// Former of a hash set. +mod hash_set; -/// A trait defining the capability to replface all elements. -pub trait ContainerAssign -{ - /// The type of elements to be added to the container. - type Element; +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use protected::*; - /// Agging elements to the container. - fn assign< Elements >( &mut self, elements : Elements ) -> usize - where - Elements : IntoIterator< Item = Self::Element >; +/// Protected namespace of the module. +pub mod protected +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::orphan::*; +} +/// Parented namespace of the module. +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; } -impl< T > ContainerAssign for collection_tools::Vec< T > +/// Exposed namespace of the module. +pub mod exposed { - type Element = T; - #[ inline( always ) ] - fn assign< Elements >( &mut self, elements : Elements ) -> usize - where - Elements : IntoIterator< Item = Self::Element > + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::prelude::*; + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: { - let initial_len = self.len(); - self.extend( elements ); - self.len() - initial_len - } -} + EntryToVal, + ContainerValToEntry, + ValToEntry, -impl< T > ContainerAssign for collection_tools::HashSet< T > -where - T : core::cmp::Eq + core::hash::Hash, -{ - type Element = T; + Container, + ContainerAdd, + ContainerAssign, + ContainerFormer, - fn assign< Elements >( &mut self, elements : Elements ) -> usize - where - Elements : IntoIterator< Item = Self::Element > + }; + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super:: { - let initial_len = self.len(); - self.extend( elements ); - self.len() - initial_len - } + vector::*, + hash_map::*, + hash_set::*, + }; + } -impl< K, V > ContainerAssign for collection_tools::HashMap< K, V > -where - K : core::cmp::Eq + core::hash::Hash, +/// Prelude to use essentials: `use my_module::prelude::*`. +pub mod prelude { - type Element = ( K, V ); - - fn assign< Elements >( &mut self, elements : Elements ) -> usize - where - Elements : IntoIterator< Item = Self::Element > - { - let initial_len = self.len(); - self.extend( elements ); - self.len() - initial_len - } } diff --git a/module/core/former/src/container/hash_map.rs b/module/core/former/src/container/hash_map.rs new file mode 100644 index 0000000000..6054850903 --- /dev/null +++ b/module/core/former/src/container/hash_map.rs @@ -0,0 +1,252 @@ +//! This module provides a comprehensive approach to applying the builder pattern to `HashMap` containers. +//! +//! By leveraging traits such as `Container`, `ContainerAdd`, `ContainerAssign`, and `ContainerValToEntry`, +//! this module abstracts the operations on hashmap-like data structures, making them more flexible and easier to integrate as +//! as subformer, enabling fluid and intuitive manipulation of hashmaps via builder patterns. +//! + +use crate::*; +use collection_tools::HashMap; + +impl< K, V > Container for collection_tools::HashMap< K, V > +where + K : core::cmp::Eq + core::hash::Hash, +{ + type Entry = ( K, V ); + type Val = V; + + #[ inline( always ) ] + fn entry_to_val( e : Self::Entry ) -> Self::Val + { + e.1 + } + +} + +impl< K, V > ContainerAdd for collection_tools::HashMap< K, V > +where + K : core::cmp::Eq + core::hash::Hash, +{ + + #[ inline( always ) ] + fn add( &mut self, ( k, v ) : Self::Entry ) -> bool + { + self.insert( k, v ).map_or_else( || true, | _ | false ) + } + +} + +impl< K, V > ContainerAssign for collection_tools::HashMap< K, V > +where + K : core::cmp::Eq + core::hash::Hash, +{ + + fn assign< Elements >( &mut self, elements : Elements ) -> usize + where + Elements : IntoIterator< Item = Self::Entry > + { + let initial_len = self.len(); + self.extend( elements ); + self.len() - initial_len + } +} + +// = storage + +impl< K, E > Storage +for HashMap< K, E > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Preformed = HashMap< K, E >; +} + +impl< K, E > StoragePreform +for HashMap< K, E > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + fn preform( self ) -> Self::Preformed + { + self + } +} + +// = definition + +/// Represents the formation definition for a hash map-like container within the former framework. +/// +/// This structure defines the essential elements required to form a hash map-like container, detailing +/// the key and value types, the contextual environment during formation, the final formed type, and the +/// behavior at the end of the formation process. It facilitates customization and extension of hash map +/// formation within any system that implements complex data management operations. +/// +/// # Type Parameters +/// - `K`: The key type of the hash map. +/// - `E`: The value type of the hash map. +/// - `Context`: The optional context provided during the formation process. +/// - `Formed`: The type of the entity produced, typically a `HashMap`. +/// - `End`: A trait defining the end behavior of the formation process, managing how the hash map is finalized. +/// + +#[ derive( Debug, Default ) ] +pub struct HashMapDefinition< K, E, Context = (), Formed = HashMap< K, E >, End = ReturnStorage > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + End : FormingEnd< HashMapDefinitionTypes< K, E, Context, Formed > >, +{ + _phantom : core::marker::PhantomData< ( K, E, Context, Formed, End ) >, +} + +impl< K, E, Context, Formed, End > FormerDefinition +for HashMapDefinition< K, E, Context, Formed, End > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + End : FormingEnd< HashMapDefinitionTypes< K, E, Context, Formed > >, +{ + + type Storage = HashMap< K, E >; + type Formed = Formed; + type Context = Context; + + type Types = HashMapDefinitionTypes< K, E, Context, Formed >; + type End = End; + +} + +// = definition types + +/// Holds the generic parameters for the `HashMapDefinition`. +/// +/// This companion struct to `HashMapDefinition` defines the storage type and the context, along with the +/// type that is ultimately formed through the process. It is crucial for maintaining the integrity and +/// consistency of type relations throughout the former lifecycle. +/// +/// # Type Parameters +/// - `K`: The key type of the hash map. +/// - `E`: The value type of the hash map. +/// - `Context`: The operational context in which the hash map is formed. +/// - `Formed`: The type produced, typically mirroring the structure of a `HashMap`. + +#[ derive( Debug, Default ) ] +pub struct HashMapDefinitionTypes< K, E, Context = (), Formed = HashMap< K, E > > +{ + _phantom : core::marker::PhantomData< ( K, E, Context, Formed ) >, +} + +impl< K, E, Context, Formed > FormerDefinitionTypes +for HashMapDefinitionTypes< K, E, Context, Formed > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Storage = HashMap< K, E >; + type Formed = Formed; + type Context = Context; +} + +// = mutator + +impl< K, E, Context, Formed > FormerMutator +for HashMapDefinitionTypes< K, E, Context, Formed > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ +} + +// = Entity To + +impl< K, E, Definition > EntityToFormer< Definition > for HashMap< K, E > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + Definition : FormerDefinition + < + Storage = HashMap< K, E >, + Types = HashMapDefinitionTypes + < + K, + E, + < Definition as definition::FormerDefinition >::Context, + < Definition as definition::FormerDefinition >::Formed, + >, + >, + Definition::End : forming::FormingEnd< Definition::Types >, +{ + type Former = HashMapFormer< K, E, Definition::Context, Definition::Formed, Definition::End >; +} + +impl< K, E > crate::EntityToStorage +for HashMap< K, E > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Storage = HashMap< K, E >; +} + +impl< K, E, Context, Formed, End > crate::EntityToDefinition< Context, Formed, End > +for HashMap< K, E > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + End : crate::FormingEnd< HashMapDefinitionTypes< K, E, Context, Formed > >, +{ + type Definition = HashMapDefinition< K, E, Context, Formed, End >; + type Types = HashMapDefinitionTypes< K, E, Context, Formed >; +} + +impl< K, E, Context, Formed > crate::EntityToDefinitionTypes< Context, Formed > +for HashMap< K, E > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Types = HashMapDefinitionTypes< K, E, Context, Formed >; +} + +// = subformer + +/// Provides a streamlined builder interface for constructing hash map-like containers. +/// +/// `HashMapFormer` is a type alias that configures the `ContainerFormer` specifically for hash maps, +/// facilitating a more intuitive and flexible way to build and manipulate hash maps within custom data structures. +/// This type alias simplifies the usage of hash maps in builder patterns by encapsulating complex generic parameters +/// and leveraging the `HashMapDefinition` to handle the construction logic. It supports fluent chaining of key-value +/// insertions and can be customized with various end actions to finalize the hash map upon completion. +/// +/// The alias helps reduce boilerplate code and enhances readability, making the construction of hash maps in +/// a builder pattern both efficient and expressive. + +pub type HashMapFormer< K, E, Context, Formed, End > = +ContainerFormer::< ( K, E ), HashMapDefinition< K, E, Context, Formed, End > >; + +// = extension + +/// Provides an extension method for hash maps to facilitate the use of the builder pattern. +/// +/// This trait extends the `HashMap` type, enabling it to use the `HashMapFormer` interface directly. +/// It allows for fluent, expressive construction and manipulation of hash maps, integrating seamlessly +/// with the builder pattern provided by the `former` framework. It's a convenience trait that simplifies +/// creating configured hash map builders with default settings. +/// + +pub trait HashMapExt< K, E > : sealed::Sealed +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + /// Initializes a builder pattern for `HashMap` using a default `HashMapFormer`. + fn former() -> HashMapFormer< K, E, (), HashMap< K, E >, ReturnStorage >; +} + +impl< K, E > HashMapExt< K, E > for HashMap< K, E > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + fn former() -> HashMapFormer< K, E, (), HashMap< K, E >, ReturnStorage > + { + HashMapFormer::< K, E, (), HashMap< K, E >, ReturnStorage >::new( ReturnStorage::default() ) + } +} + +mod sealed +{ + use super::HashMap; + pub trait Sealed {} + impl< K, E > Sealed for HashMap< K, E > {} +} diff --git a/module/core/former/src/container/hash_set.rs b/module/core/former/src/container/hash_set.rs new file mode 100644 index 0000000000..6e96684ee1 --- /dev/null +++ b/module/core/former/src/container/hash_set.rs @@ -0,0 +1,278 @@ +//! This module provides a builder pattern implementation (`HashSetFormer`) for `HashSet`-like containers. It is designed to extend the builder pattern, allowing for fluent and dynamic construction of sets within custom data structures. + +use crate::*; +use collection_tools::HashSet; + +impl< K > Container for collection_tools::HashSet< K > +where + K : core::cmp::Eq + core::hash::Hash, +{ + type Entry = K; + type Val = K; + + #[ inline( always ) ] + fn entry_to_val( e : Self::Entry ) -> Self::Val + { + e + } + +} + +impl< K > ContainerAdd for collection_tools::HashSet< K > +where + K : core::cmp::Eq + core::hash::Hash, +{ + // type Entry = K; + // type Val = K; + + #[ inline( always ) ] + fn add( &mut self, e : Self::Entry ) -> bool + { + self.insert( e ) + } + +} + +impl< K > ContainerAssign for collection_tools::HashSet< K > +where + K : core::cmp::Eq + core::hash::Hash, +{ + // type Entry = K; + + fn assign< Elements >( &mut self, elements : Elements ) -> usize + where + Elements : IntoIterator< Item = Self::Entry > + { + let initial_len = self.len(); + self.extend( elements ); + self.len() - initial_len + } +} + +impl< K > ContainerValToEntry< K > for HashSet< K > +where + K : core::cmp::Eq + core::hash::Hash, +{ + type Entry = K; + #[ inline( always ) ] + fn val_to_entry( val : K ) -> Self::Entry + { + val + } +} + +// /// A trait for containers behaving like a `HashSet`, allowing insertion operations. +// /// +// /// Implementing this trait enables the associated formed to be used with `HashSetFormer`, +// /// facilitating a builder pattern that is both intuitive and concise. +// /// +// /// # Example Implementation +// /// +// /// Implementing `HashSetLike` for `std::collections::HashSet`: +// /// +// +// pub trait HashSetLike< K > +// where +// K : core::cmp::Eq + core::hash::Hash, +// { +// /// Inserts a key-value pair into the map. +// fn insert( &mut self, element : K ) -> Option< K >; +// } +// +// // impl< K > HashSetLike< K > for HashSet< K > +// // where +// // K : core::cmp::Eq + core::hash::Hash, +// // { +// // fn insert( &mut self, element : K ) -> Option< K > +// // { +// // HashSet::replace( self, element ) +// // } +// // } + +// = storage + +impl< K > Storage +for HashSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + // type Formed = HashSet< K >; + type Preformed = HashSet< K >; +} + +impl< K > StoragePreform +for HashSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + // type Preformed = HashSet< K >; + fn preform( self ) -> Self::Preformed + { + self + } +} + +// = definition + +/// Represents the formation definition for a hash set-like container within the former framework. +/// +/// This structure defines the essential elements required to form a hash set-like container, detailing +/// the type of elements, the contextual environment during formation, the final formed type, and the +/// behavior at the end of the formation process. It is designed to support the construction and configuration +/// of hash set containers with dynamic characteristics and behaviors. +/// +/// # Type Parameters +/// - `K`: The type of elements in the hash set. +/// - `Context`: The optional context provided during the formation process. +/// - `Formed`: The type of the entity produced, typically a `HashSet`. +/// - `End`: A trait defining the end behavior of the formation process, managing how the hash set is finalized. +/// + +#[ derive( Debug, Default ) ] +pub struct HashSetDefinition< K, Context = (), Formed = HashSet< K >, End = ReturnStorage > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + End : FormingEnd< HashSetDefinitionTypes< K, Context, Formed > >, +{ + _phantom : core::marker::PhantomData< ( K, Context, Formed, End ) >, +} + +impl< K, Context, Formed, End > FormerDefinition +for HashSetDefinition< K, Context, Formed, End > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + End : FormingEnd< HashSetDefinitionTypes< K, Context, Formed > >, +{ + type Storage = HashSet< K >; + type Formed = Formed; + type Context = Context; + + type Types = HashSetDefinitionTypes< K, Context, Formed >; + type End = End; +} + +// = definition types + +/// Holds the generic parameters for the `HashSetDefinition`. +/// +/// This struct encapsulates the type relationships and characteristics essential for the formation process +/// of a `HashSet`, including the storage type, the context, and the type ultimately formed. It ensures that +/// these elements are congruent and coherent throughout the lifecycle of the hash set formation. +/// + +#[ derive( Debug, Default ) ] +pub struct HashSetDefinitionTypes< K, Context = (), Formed = HashSet< K > > +{ + _phantom : core::marker::PhantomData< ( K, Context, Formed ) >, +} + +impl< K, Context, Formed > FormerDefinitionTypes +for HashSetDefinitionTypes< K, Context, Formed > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Storage = HashSet< K >; + type Formed = Formed; + type Context = Context; +} + +// = mutator + +impl< K, Context, Formed > FormerMutator +for HashSetDefinitionTypes< K, Context, Formed > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ +} + +// = entity to + +impl< K, Definition > EntityToFormer< Definition > for HashSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + Definition : FormerDefinition + < + Storage = HashSet< K >, + Types = HashSetDefinitionTypes + < + K, + < Definition as definition::FormerDefinition >::Context, + < Definition as definition::FormerDefinition >::Formed, + >, + >, + Definition::End : forming::FormingEnd< Definition::Types >, +{ + type Former = HashSetFormer< K, Definition::Context, Definition::Formed, Definition::End >; +} + +impl< K > crate::EntityToStorage +for HashSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Storage = HashSet< K >; +} + +impl< K, Context, Formed, End > crate::EntityToDefinition< Context, Formed, End > +for HashSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + End : crate::FormingEnd< HashSetDefinitionTypes< K, Context, Formed > >, +{ + type Definition = HashSetDefinition< K, Context, Formed, End >; + type Types = HashSetDefinitionTypes< K, Context, Formed >; +} + +impl< K, Context, Formed > crate::EntityToDefinitionTypes< Context, Formed > +for HashSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Types = HashSetDefinitionTypes< K, Context, Formed >; +} + +// = subformer + +/// Provides a concise alias for `ContainerFormer` configured specifically for `HashSet`-like containers. +/// +/// `HashSetFormer` simplifies the creation of `HashSet` containers within builder patterns by leveraging +/// the `ContainerFormer` with predefined settings. This approach minimizes boilerplate code and enhances +/// readability, making it ideal for fluent and expressive construction of set containers within custom data structures. +/// + +pub type HashSetFormer< K, Context, Formed, End > = +ContainerFormer::< K, HashSetDefinition< K, Context, Formed, End > >; + +// = extension + +/// Provides an extension method for `HashSet` to facilitate the use of the builder pattern. +/// +/// This trait extends `HashSet`, enabling direct use of the `HashSetFormer` interface for fluent and expressive +/// set construction. It simplifies the process of building `HashSet` instances by providing a straightforward +/// way to start the builder pattern with default context and termination behavior. +/// + +pub trait HashSetExt< K > : sealed::Sealed +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + /// Initializes a builder pattern for `HashSet` using a default `HashSetFormer`. + fn former() -> HashSetFormer< K, (), HashSet< K >, ReturnStorage >; +} + +impl< K > HashSetExt< K > for HashSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + fn former() -> HashSetFormer< K, (), HashSet< K >, ReturnStorage > + { + HashSetFormer::< K, (), HashSet< K >, ReturnStorage >::new( ReturnStorage::default() ) + } +} + +mod sealed +{ + use super::HashSet; + pub trait Sealed {} + impl< K > Sealed for HashSet< K > {} +} diff --git a/module/core/former/src/container/vector.rs b/module/core/former/src/container/vector.rs new file mode 100644 index 0000000000..33c344d1ab --- /dev/null +++ b/module/core/former/src/container/vector.rs @@ -0,0 +1,234 @@ +//! This module provides a comprehensive approach to applying the builder pattern to `Vec` containers. +//! +//! By leveraging traits such as `Container`, `ContainerAdd`, `ContainerAssign`, and `ContainerValToEntry`, +//! this module abstracts the operations on vector-like data structures, making them more flexible and easier to integrate as +//! as subformer, enabling fluid and intuitive manipulation of vectors via builder patterns. +//! + +use crate::*; +#[ allow( unused ) ] +use collection_tools::Vec; + +impl< E > Container for collection_tools::Vec< E > +{ + type Entry = E; + type Val = E; + + #[ inline( always ) ] + fn entry_to_val( e : Self::Entry ) -> Self::Val + { + e + } + +} + +impl< E > ContainerAdd for collection_tools::Vec< E > +{ + + #[ inline( always ) ] + fn add( &mut self, e : Self::Entry ) -> bool + { + self.push( e ); + true + } + +} + +impl< E > ContainerAssign for collection_tools::Vec< E > +{ + #[ inline( always ) ] + fn assign< Elements >( &mut self, elements : Elements ) -> usize + where + Elements : IntoIterator< Item = Self::Entry > + { + let initial_len = self.len(); + self.extend( elements ); + self.len() - initial_len + } + +} + +impl< E > ContainerValToEntry< E > for collection_tools::Vec< E > +where +{ + type Entry = E; + #[ inline( always ) ] + fn val_to_entry( val : E ) -> Self::Entry + { + val + } +} + +// = storage + +impl< E > Storage +for Vec< E > +{ + type Preformed = Vec< E >; +} + +impl< E > StoragePreform +for Vec< E > +{ + fn preform( self ) -> Self::Preformed + { + self + } +} + +// = definition + +/// Represents the formation definition for a vector-like container within the former framework. +/// +/// This structure defines the necessary parameters and relationships needed to form a vector-like container, +/// including its storage, context, the result of the formation process, and the behavior at the end of the formation. +/// +/// # Type Parameters +/// - `E`: The element type of the vector. +/// - `Context`: The context needed for the formation, can be provided externally. +/// - `Formed`: The type formed at the end of the formation process, typically a `Vec`. +/// - `End`: A trait determining the behavior at the end of the formation process. +/// + +#[ derive( Debug, Default ) ] +pub struct VectorDefinition< E, Context, Formed, End > +where + End : FormingEnd< VectorDefinitionTypes< E, Context, Formed > >, +{ + _phantom : core::marker::PhantomData< ( E, Context, Formed, End ) >, +} + +impl< E, Context, Formed, End > FormerDefinition +for VectorDefinition< E, Context, Formed, End > +where + End : FormingEnd< VectorDefinitionTypes< E, Context, Formed > >, +{ + type Storage = Vec< E >; + type Context = Context; + type Formed = Formed; + + type Types = VectorDefinitionTypes< E, Context, Formed >; + type End = End; +} + +// = definition type + +/// Holds the generic parameters for the `VectorDefinition`. +/// +/// This struct acts as a companion to `VectorDefinition`, providing a concrete definition of types used +/// in the formation process. It is crucial for linking the type parameters with the operational mechanics +/// of the formation and ensuring type safety and correctness throughout the formation lifecycle. +/// +/// # Type Parameters +/// +/// - `E`: The element type of the vector. +/// - `Context`: The context in which the vector is formed. +/// - `Formed`: The type produced as a result of the formation process. + +#[ derive( Debug, Default ) ] +pub struct VectorDefinitionTypes< E, Context = (), Formed = Vec< E > > +{ + _phantom : core::marker::PhantomData< ( E, Context, Formed ) >, +} + +impl< E, Context, Formed > FormerDefinitionTypes +for VectorDefinitionTypes< E, Context, Formed > +{ + type Storage = Vec< E >; + type Context = Context; + type Formed = Formed; +} + +// = mutator + +impl< E, Context, Formed > FormerMutator +for VectorDefinitionTypes< E, Context, Formed > +{ +} + +// = Entity To + +impl< E, Definition > EntityToFormer< Definition > +for Vec< E > +where + Definition : FormerDefinition + < + Storage = Vec< E >, + Types = VectorDefinitionTypes + < + E, + < Definition as definition::FormerDefinition >::Context, + < Definition as definition::FormerDefinition >::Formed, + >, + >, + Definition::End : forming::FormingEnd< Definition::Types >, +{ + type Former = VectorFormer< E, Definition::Context, Definition::Formed, Definition::End >; +} + +impl< E > crate::EntityToStorage +for Vec< E > +{ + type Storage = Vec< E >; +} + +impl< E, Context, Formed, End > crate::EntityToDefinition< Context, Formed, End > +for Vec< E > +where + End : crate::FormingEnd< VectorDefinitionTypes< E, Context, Formed > >, +{ + type Definition = VectorDefinition< E, Context, Formed, End >; + type Types = VectorDefinitionTypes< E, Context, Formed >; +} + +impl< E, Context, Formed > crate::EntityToDefinitionTypes< Context, Formed > +for Vec< E > +{ + type Types = VectorDefinitionTypes< E, Context, Formed >; +} + +// = subformer + +/// Provides a streamlined builder interface for constructing vector-like containers. +/// +/// `VectorFormer` is a type alias that configures the `ContainerFormer` for use specifically with vectors. +/// It integrates the `VectorDefinition` to facilitate the fluent and dynamic construction of vectors, leveraging +/// predefined settings to reduce boilerplate code. This approach enhances readability and simplifies the use of +/// vectors in custom data structures where builder patterns are desired. +/// +/// The alias encapsulates complex generic parameters, making the construction process more accessible and maintainable. +/// It is particularly useful in scenarios where vectors are repeatedly used or configured in similar ways across different +/// parts of an application. +/// + +pub type VectorFormer< E, Context, Formed, End > = +ContainerFormer::< E, VectorDefinition< E, Context, Formed, End > >; + +// = extension + +/// Provides an extension method for vectors to facilitate the use of the builder pattern. +/// +/// This trait extends the `Vec` type, enabling it to use the `VectorFormer` interface directly. +/// This allows for fluent, expressive construction and manipulation of vectors, integrating seamlessly +/// with the builder pattern provided by the `former` framework. It's a convenience trait that simplifies +/// creating configured vector builders with default settings. +/// +pub trait VecExt< E > : sealed::Sealed +{ + /// Initializes a builder pattern for `Vec` using a default `VectorFormer`. + fn former() -> VectorFormer< E, (), Vec< E >, ReturnStorage >; +} + +impl< E > VecExt< E > for Vec< E > +{ + fn former() -> VectorFormer< E, (), Vec< E >, ReturnStorage > + { + VectorFormer::< E, (), Vec< E >, ReturnStorage >::new( ReturnStorage::default() ) + } +} + +mod sealed +{ + pub trait Sealed {} + impl< E > Sealed for super::Vec< E > {} +} diff --git a/module/core/former/src/definition.rs b/module/core/former/src/definition.rs new file mode 100644 index 0000000000..38563df0e8 --- /dev/null +++ b/module/core/former/src/definition.rs @@ -0,0 +1,100 @@ +//! Module `definition` +//! +//! Provides traits for defining the relationships between entities and their formation mechanisms. +//! These traits are central to the implementation of a flexible and extensible formation system, +//! enabling entities to be constructed using various configurations and complex logic. +//! +//! Key aspects of the module include: +//! - **Entity to Definition Mapping**: Linking entities to their specific formation definitions, +//! which detail how they are to be constructed. +//! - **Entity to Former Mapping**: Associating entities with formers that handle their construction +//! process. +//! - **Entity to Storage Mapping**: Defining the storage structures that maintain the state of an +//! entity during its formation. +//! - **Definition Traits**: Specifying the properties and ending conditions of the formation +//! process to ensure entities are formed according to specified rules and logic. +//! + +/// Maps a type of entity to its corresponding former definition. +/// This trait provides a linkage between the entity and its definition, +/// allowing the formation logic to understand what definition to apply +/// during the formation process. +pub trait EntityToDefinition< Context, Formed, End > +{ + /// The specific [`FormerDefinition`] associated with this entity. + type Definition : FormerDefinition; + /// The specific [`FormerDefinitionTypes`] associated with this entity. + type Types : FormerDefinitionTypes; +} + +/// Provides a mapping between a type of entity and its associated formation type definitions. +pub trait EntityToDefinitionTypes< Context, Formed > +{ + /// Specifies the `FormerDefinitionTypes` that define the storage, formed entity, and context types used during formation. + /// This association is essential for ensuring that the formation process is carried out with the correct type-specific logic. + type Types : FormerDefinitionTypes; +} + +/// Maps a type of entity to its corresponding former. +/// This trait binds an entity type to a specific former, facilitating the use +/// of custom formers in complex formation scenarios. +pub trait EntityToFormer< Definition > +where + Definition : FormerDefinition, +{ + /// The type of the former used for building the entity. + type Former; + + /// A placeholder function to reference the definition without operational logic to calm compiler. + fn __f(_: &Definition) {} +} + +/// Maps a type of entity to its storage type. +/// This trait defines what storage structure is used to hold the interim state +/// of an entity during its formation. +pub trait EntityToStorage +{ + /// The storage type used for forming the entity. + type Storage; +} + +/// Defines the fundamental components involved in the formation of an entity. +/// This trait specifies the types of storage, the formed entity, and the context +/// used during the formation process. +pub trait FormerDefinitionTypes : Sized +{ + /// The type of storage used to maintain the state during formation. + type Storage : Default; + + /// The type of the entity once fully formed. + type Formed; + + /// The contextual information used during formation, if any. + type Context; +} + +/// Expands on `FormerDefinitionTypes` by incorporating an ending mechanism for the formation process. +/// This trait connects the formation types with a specific endpoint, defining +/// how the formation process concludes, including any necessary transformations +/// or validations. +pub trait FormerDefinition : Sized +{ + /// Encapsulates the types related to the formation process including any mutators. + type Types : crate::FormerDefinitionTypes< Storage = Self::Storage, Formed = Self::Formed, Context = Self::Context > + + crate::FormerMutator; + + /// Defines the ending condition or operation of the formation process. + type End: crate::FormingEnd< Self::Types >; + + /// The storage type used during the formation. + type Storage : Default; + + /// The type of the entity being formed. It is + /// generally the structure for which the `Former` trait is derived, representing the fully formed + /// state of the entity. However, it can differ if a custom `FormingEnd` or a different `Formed` type + /// is defined to handle specific forming logic or requirements. + type Formed; + + /// The context used during the formation process. + type Context; +} diff --git a/module/core/former/src/forming.rs b/module/core/former/src/forming.rs new file mode 100644 index 0000000000..892f4ad526 --- /dev/null +++ b/module/core/former/src/forming.rs @@ -0,0 +1,286 @@ +//! Module `forming` +//! +//! This module defines a collection of traits that are crucial for implementing a structured and extensible builder pattern. +//! The traits provided manage the various stages of the forming process, handling the initiation, mutation, and completion +//! of constructing complex data structures. These traits facilitate the creation of flexible and maintainable formation +//! logic that can accommodate complex construction scenarios, including nested and conditional formations. + +/// Provides a mechanism for mutating the context and storage just before the forming process is completed. +/// +/// The `FormerMutator` trait allows for the implementation of custom mutation logic on the internal state +/// of an entity (context and storage) just before the final forming operation is completed. This mutation +/// occurs immediately before the `FormingEnd` callback is invoked. +/// +/// #### Differences from `FormingEnd` +/// +/// Unlike `FormingEnd`, which is responsible for integrating and finalizing the formation process of a field within +/// a parent former, `form_mutation` directly pertains to the entity itself. This method is designed to be independent +/// of whether the forming process is occurring within the context of a superformer or if the structure is a standalone +/// or nested field. This makes `form_mutation` suitable for entity-specific transformations that should not interfere +/// with the hierarchical forming logic managed by `FormingEnd`. +/// +/// #### Use Cases +/// +/// - Applying last-minute changes to the data being formed. +/// - Setting or modifying properties that depend on the final state of the storage or context. +/// - Storage-specific fields which are not present in formed structure. +/// +/// Look example `former_custom_mutator.rs` + +pub trait FormerMutator +where + Self : crate::FormerDefinitionTypes, +{ + /// Mutates the context and storage of the entity just before the formation process completes. + /// + /// This function is invoked immediately prior to the `FormingEnd` callback during the forming process. + /// It provides a hook for implementing custom logic that modifies the internal state (storage and context) + /// of the entity. `form_mutation` is particularly useful for adjustments or updates that need to reflect + /// in the entity just before it is finalized and returned. + /// + #[ inline ] + fn form_mutation( _storage : &mut Self::Storage, _context : &mut ::core::option::Option< Self::Context > ) + { + } +} + +// impl< Definition > crate::FormerMutator +// for Definition +// where +// Definition : crate::FormerDefinitionTypes, +// { +// } + +/// Defines a handler for the end of a subforming process, enabling the return of the original context. +/// +/// This trait is designed to be flexible, allowing for various end-of-forming behaviors in builder patterns. +/// Implementors can define how to transform or pass through the context during the forming process's completion. +/// +/// # Parameters +/// - `Storage`: The type of the container being processed. +/// - `Context`: The type of the context that might be altered or returned upon completion. + +pub trait FormingEnd< Definition : crate::FormerDefinitionTypes > +{ + /// Called at the end of the subforming process to return the modified or original context. + /// + /// # Parameters + /// - `container`: The container being processed. + /// - `context`: Optional context to be transformed or returned. + /// + /// # Returns + /// Returns the transformed or original context based on the implementation. + fn call( &self, storage : Definition::Storage, context : core::option::Option< Definition::Context > ) -> Definition::Formed; +} + +impl< Definition, F > FormingEnd< Definition > for F +where + F : Fn( Definition::Storage, core::option::Option< Definition::Context > ) -> Definition::Formed, + Definition : crate::FormerDefinitionTypes, +{ + #[ inline( always ) ] + fn call( &self, storage : Definition::Storage, context : core::option::Option< Definition::Context > ) -> Definition::Formed + { + self( storage, context ) + } +} + +/// A `FormingEnd` implementation that directly returns the formed container as the final product of the forming process. +/// +/// This struct is particularly useful when the end result of the forming process is simply the formed container itself, +/// without needing to integrate or process additional contextual information. It's ideal for scenarios where the final +/// entity is directly derived from the storage state without further transformations or context-dependent adjustments. +#[ derive( Debug, Default ) ] +pub struct ReturnPreformed; + +impl< Definition > FormingEnd< Definition > +for ReturnPreformed +where + Definition::Storage : crate::StoragePreform< Preformed = Definition::Formed >, + Definition : crate::FormerDefinitionTypes, +{ + /// Transforms the storage into its final formed state and returns it, bypassing context processing. + #[ inline( always ) ] + fn call( &self, storage : Definition::Storage, _context : core::option::Option< Definition::Context > ) -> Definition::Formed + { + crate::StoragePreform::preform( storage ) + } +} + +/// A `FormingEnd` implementation that returns the storage itself as the formed entity, disregarding any contextual data. +/// +/// This struct is suited for straightforward forming processes where the storage already represents the final state of the +/// entity, and no additional processing or transformation of the storage is required. It simplifies use cases where the +/// storage does not undergo a transformation into a different type at the end of the forming process. + +#[ derive( Debug, Default ) ] +pub struct ReturnStorage; + +impl< Definition, T > FormingEnd< Definition > +for ReturnStorage +where + Definition : crate::FormerDefinitionTypes< Context = (), Storage = T, Formed = T >, +{ + /// Returns the storage as the final product of the forming process, ignoring any additional context. + #[ inline( always ) ] + fn call( &self, storage : Definition::Storage, _context : core::option::Option< () > ) -> Definition::Formed + { + storage + } +} + +/// A placeholder `FormingEnd` used when no end operation is required or applicable. +/// +/// This implementation is useful in generic or templated scenarios where a `FormingEnd` is required by the interface, +/// but no meaningful end operation is applicable. It serves a role similar to `core::marker::PhantomData` by filling +/// generic parameter slots without contributing operational logic. +#[ derive( Debug, Default ) ] +pub struct NoEnd; + +impl< Definition > FormingEnd< Definition > +for NoEnd +where + Definition : crate::FormerDefinitionTypes, +{ + /// Intentionally causes a panic if called, as its use indicates a configuration error. + #[ inline( always ) ] + fn call( &self, _storage : Definition::Storage, _context : core::option::Option< Definition::Context > ) -> Definition::Formed + { + unreachable!(); + } +} + +#[ allow( unused_extern_crates ) ] +#[ cfg( all( feature = "no_std", feature = "use_alloc" ) ) ] +extern crate alloc; +#[ cfg( all( feature = "no_std", feature = "use_alloc" ) ) ] +use alloc::boxed::Box; + +/// A wrapper around a closure to be used as a `FormingEnd`. +/// +/// This struct allows for dynamic dispatch of a closure that matches the +/// `FormingEnd` trait's `call` method signature. It is useful for cases where +/// a closure needs to be stored or passed around as an object implementing +/// `FormingEnd`. +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +pub struct FormingEndClosure< Definition : crate::FormerDefinitionTypes > +{ + closure : Box< dyn Fn( Definition::Storage, Option< Definition::Context > ) -> Definition::Formed >, + _marker : core::marker::PhantomData< Definition::Storage >, +} + +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +impl< T, Definition > From< T > for FormingEndClosure< Definition > +where + T : Fn( Definition::Storage, Option< Definition::Context > ) -> Definition::Formed + 'static, + Definition : crate::FormerDefinitionTypes, +{ + #[ inline( always ) ] + fn from( closure : T ) -> Self + { + Self + { + closure : Box::new( closure ), + _marker : core::marker::PhantomData + } + } +} + +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +impl< Definition : crate::FormerDefinitionTypes > FormingEndClosure< Definition > +{ + /// Constructs a new `FormingEndClosure` with the provided closure. + /// + /// # Parameters + /// + /// * `closure` - A closure that matches the expected signature for transforming a container + /// and context into a new context. This closure is stored and called by the + /// `call` method of the `FormingEnd` trait implementation. + /// + /// # Returns + /// + /// Returns an instance of `FormingEndClosure` encapsulating the provided closure. + pub fn new( closure : impl Fn( Definition::Storage, Option< Definition::Context > ) -> Definition::Formed + 'static ) -> Self + { + Self + { + closure : Box::new( closure ), + _marker : core::marker::PhantomData + } + } +} + +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +use core::fmt; +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +impl< Definition : crate::FormerDefinitionTypes > fmt::Debug for FormingEndClosure< Definition > +{ + fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + { + f.debug_struct( "FormingEndClosure" ) + .field( "closure", &format_args!{ "- closure -" } ) + .field( "_marker", &self._marker ) + .finish() + } +} + +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +impl< Definition : crate::FormerDefinitionTypes > FormingEnd< Definition > +for FormingEndClosure< Definition > +{ + fn call( &self, storage : Definition::Storage, context : Option< Definition::Context > ) -> Definition::Formed + { + ( self.closure )( storage, context ) + } +} + +/// A trait for initiating a structured subforming process with contextual and intermediary storage linkage. +/// +/// This trait is crucial for the `derive(Former)` macro implementation, where it facilitates the creation +/// of a subformer that integrates seamlessly within a builder pattern chain. It handles intermediary storage +/// to accumulate state or data before finally transforming it into the final `Formed` structure. +/// +/// `FormerBegin` is particularly important in scenarios where complex, hierarchical structures are formed, +/// allowing a former to be reused within another former. This reusability and the ability to maintain both transient +/// state (`Storage`) and contextual information (`Context`) are essential for multi-step construction or transformation +/// processes that culminate in the generation of a final product (`Formed`). +/// +/// During code generation via the `derive(Former)` macro, `FormerBegin` provides the necessary scaffolding to +/// initiate the subforming process. This setup is critical for ensuring that all elements involved in the formation +/// are aligned from the onset, particularly when one former is nested within another, facilitating the creation +/// of complex hierarchical data structures. +/// + +pub trait FormerBegin< Definition : > +where + Definition : crate::FormerDefinition, +{ + + /// Launches the subforming process with an initial storage and context, setting up an `on_end` completion handler. + /// + /// This method initializes the formation process by providing the foundational elements necessary for + /// building the entity. It allows for the configuration of initial states and contextual parameters, which + /// are critical for accurately reflecting the intended final state of the entity. + /// + /// # Parameters + /// + /// * `storage` - An optional initial state for the intermediary storage structure. This parameter allows + /// for the pre-configuration of storage, which can be crucial for entities requiring specific initial states. + /// * `context` - An optional initial setting providing contextual information for the subforming process. + /// This context can influence how the formation process progresses, especially in complex forming scenarios. + /// * `on_end` - A completion handler responsible for transforming the accumulated `Storage` into the final `Formed` structure. + /// This parameter is vital for ensuring that the transition from `Storage` to `Formed` is handled correctly, + /// incorporating any last-minute adjustments or validations necessary for the entity's integrity. + /// + /// # Returns + /// + /// Returns an instance of Former. + /// + fn former_begin + ( + storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : Definition::End, + ) -> Self; + +} diff --git a/module/core/former/src/hash_map.rs b/module/core/former/src/hash_map.rs deleted file mode 100644 index 14c0831370..0000000000 --- a/module/core/former/src/hash_map.rs +++ /dev/null @@ -1,250 +0,0 @@ -use super::*; - -use collection_tools::HashMap; - -/// A trait for types that behave like hash maps, supporting insertion and custom forming behaviors. -/// -/// This trait allows for generic operations on hash map-like data structures, enabling the insertion -/// of key-value pairs and the creation of formers for more complex construction patterns. -/// -/// # Type Parameters -/// - `K`: The type of keys stored in the hash map. Must implement `Eq` and `Hash`. -/// - `E`: The type of elements (values) stored in the hash map. -pub trait HashMapLike< K, E > -where - K : core::cmp::Eq + core::hash::Hash, - Self : Sized + Default, -{ - - /// Inserts a key-value pair into the map. - fn insert( &mut self, k : K, e : E ) -> Option< E >; - - /// Return former. - #[ inline( always ) ] - fn former( self ) - -> HashMapSubformer< K, E, Self, Self, impl FormingEnd< Self, Self > > - { - HashMapSubformer::begin( Some( self ), None, ReturnFormed ) - } - - // /// Return former with a custom context. - // #[ inline( always ) ] - // fn former_begin< Context, End >( self, context : Context, end : End ) - // -> HashMapSubformer< K, E, Self, Context, End > - // where End : FormingEnd< Self, Context > - // { - // HashMapSubformer::begin( Some( self ), Some( context ), end ) - // } - -} - -impl< K, E > HashMapLike< K, E > for HashMap< K, E > -where - K : core::cmp::Eq + core::hash::Hash, - Self : Sized + Default, -{ - - #[ inline( always ) ] - fn insert( &mut self, k : K, e : E ) -> Option< E > - { - HashMap::insert( self, k, e ) - } - -} - -/// A builder for constructing hash map-like structures with a fluent interface. -/// -/// `HashMapSubformer` leverages the `HashMapLike` trait to enable a flexible and customizable -/// way to build hash map-like structures. It supports the chaining of insert operations and -/// allows for the definition of custom end actions to finalize the building process. -/// -/// # Type Parameters -/// - `K`: Key type, must implement `Eq` and `Hash`. -/// - `E`: Element (value) type. -/// - `Formed`: The hash map-like formed being built. -/// - `Context`: Type of the optional context used during the building process. -/// - `End`: End-of-forming action to be executed upon completion. -/// -/// # Examples -/// ``` -/// # #[ cfg( all( feature = "enabled", not( feature = "no_std" ) ) ) ] -/// # { -/// # use test_tools::exposed::*; -/// -/// #[ derive( Debug, PartialEq, former::Former ) ] -/// pub struct StructWithMap -/// { -/// #[ subformer( former::HashMapSubformer ) ] -/// map : std::collections::HashMap< &'static str, &'static str >, -/// } -/// -/// let struct1 = StructWithMap::former() -/// .map() -/// .insert( "a", "b" ) -/// .insert( "c", "d" ) -/// .end() -/// .form() -/// ; -/// assert_eq!( struct1, StructWithMap { map : hmap!{ "a" => "b", "c" => "d" } } ); -/// -/// # } -/// ``` - -#[ derive( Debug, Default ) ] -pub struct HashMapSubformer< K, E, Formed, Context, End > -where - K : core::cmp::Eq + core::hash::Hash, - Formed : HashMapLike< K, E > + core::default::Default, - End : FormingEnd< Formed, Context >, -{ - formed : core::option::Option< Formed >, - context : core::option::Option< Context >, - on_end : core::option::Option< End >, - _e_phantom : core::marker::PhantomData< E >, - _k_phantom : core::marker::PhantomData< K >, -} - -impl< K, E, Formed, Context, End > -HashMapSubformer< K, E, Formed, Context, End > -where - K : core::cmp::Eq + core::hash::Hash, - Formed : HashMapLike< K, E > + core::default::Default, - End : FormingEnd< Formed, Context >, -{ - - /// Form current former into target structure. - #[ inline( always ) ] - pub fn form( mut self ) -> Formed - { - let formed = if self.formed.is_some() - { - self.formed.take().unwrap() - } - else - { - let val = Default::default(); - val - }; - formed - } - - /// Make a new HashMapSubformer. It should be called by a context generated for your structure. - /// The context is returned after completion of forming by function `on_end``. - #[ inline( always ) ] - pub fn begin - ( - formed : core::option::Option< Formed >, - context : core::option::Option< Context >, - on_end : End, - ) -> Self - { - Self - { - formed, - context, - on_end : Some( on_end ), - _e_phantom : core::marker::PhantomData, - _k_phantom : core::marker::PhantomData, - } - } - - /// Return context of your struct moving formed there. Should be called after configuring the formed. - #[ inline( always ) ] - pub fn end( mut self ) -> Context - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) - } - - /// Set the whole formed instead of setting each element individually. - #[ inline( always ) ] - pub fn replace( mut self, formed : Formed ) -> Self - { - self.formed = Some( formed ); - self - } - -} - -// impl< E, Formed > VectorSubformer< E, Formed, Formed, crate::ReturnFormed > -// where -// Formed : VectorLike< E > + core::default::Default, - -impl< K, E, Formed > -HashMapSubformer< K, E, Formed, Formed, crate::ReturnFormed > -where - K : core::cmp::Eq + core::hash::Hash, - Formed : HashMapLike< K, E > + core::default::Default, -{ - - /// Create a new instance without context or on end processing. It just returns continaer on end of forming. - #[ inline( always ) ] - pub fn new() -> Self - { - HashMapSubformer::begin - ( - None, - None, - crate::ReturnFormed, - ) - } - -} - -impl< K, E, Formed, Context, End > -HashMapSubformer< K, E, Formed, Context, End > -where - K : core::cmp::Eq + core::hash::Hash, - Formed : HashMapLike< K, E > + core::default::Default, - End : FormingEnd< Formed, Context >, -{ - - /// Inserts a key-value pair into the formed. If the formed doesn't exist, it is created. - /// - /// # Parameters - /// - `k`: The key for the value to be inserted. Will be converted into the formed's key type. - /// - `e`: The value to be inserted. Will be converted into the formed's value type. - /// - /// # Returns - /// Returns `self` for chaining further insertions or operations. - /// - #[ inline( always ) ] - pub fn insert< K2, E2 >( mut self, k : K2, e : E2 ) -> Self - where - K2 : core::convert::Into< K >, - E2 : core::convert::Into< E >, - { - if self.formed.is_none() - { - self.formed = core::option::Option::Some( Default::default() ); - } - if let core::option::Option::Some( ref mut formed ) = self.formed - { - formed.insert( k.into(), e.into() ); - } - self - } - - /// Alias for insert. - /// - /// # Parameters - /// - `k`: The key for the value to be inserted. Will be converted into the formed's key type. - /// - `e`: The value to be inserted. Will be converted into the formed's value type. - /// - /// # Returns - /// Returns `self` for chaining further insertions or operations. - /// - #[ inline( always ) ] - pub fn push< K2, E2 >( self, k : K2, e : E2 ) -> Self - where - K2 : core::convert::Into< K >, - E2 : core::convert::Into< E >, - { - self.insert( k, e ) - } - -} - -// diff --git a/module/core/former/src/hash_set.rs b/module/core/former/src/hash_set.rs deleted file mode 100644 index 20a521d3f6..0000000000 --- a/module/core/former/src/hash_set.rs +++ /dev/null @@ -1,244 +0,0 @@ -//! # HashSetLike Trait and HashSetSubformer Struct -//! -//! This part of the crate provides a flexible interface (`HashSetLike`) and a builder pattern implementation (`HashSetSubformer`) for `HashSet`-like containers. It's designed to extend the builder pattern, allowing for fluent and dynamic construction of sets within custom data structures. - -use super::*; -use collection_tools::HashSet; - -/// A trait for containers behaving like a `HashSet`, allowing insertion operations. -/// -/// Implementing this trait enables the associated formed to be used with `HashSetSubformer`, -/// facilitating a builder pattern that is both intuitive and concise. -/// -/// # Example Implementation -/// -/// Implementing `HashSetLike` for `std::collections::HashSet`: -/// - -pub trait HashSetLike< E > -where - E : core::cmp::Eq + core::hash::Hash, -{ - /// Inserts a key-value pair into the map. - fn insert( &mut self, element : E ) -> Option< E >; -} - -impl< E > HashSetLike< E > for HashSet< E > -where - E : core::cmp::Eq + core::hash::Hash, -{ - fn insert( &mut self, element : E ) -> Option< E > - { - HashSet::replace( self, element ) - } -} - -/// Facilitates building `HashSetLike` containers with a fluent API. -/// -/// `HashSetSubformer` leverages the `HashSetLike` trait to enable a concise and expressive way -/// of populating `HashSet`-like containers. It exemplifies the crate's builder pattern variation for sets. -/// -/// # Example Usage -/// -/// Using `HashSetSubformer` to populate a `HashSet` within a struct: -/// -/// ```rust -/// # #[ cfg( all( feature = "enabled", not( feature = "no_std" ) ) ) ] -/// # { -/// # use test_tools::exposed::*; -/// -/// #[ derive( Debug, PartialEq, former::Former ) ] -/// pub struct StructWithSet -/// { -/// #[ subformer( former::HashSetSubformer ) ] -/// set : std::collections::HashSet< &'static str >, -/// } -/// -/// let instance = StructWithSet::former() -/// .set() -/// .insert( "apple" ) -/// .insert( "banana" ) -/// .end() -/// .form(); -/// -/// assert_eq!(instance, StructWithSet { set : hset![ "apple", "banana" ] }); -/// # } -/// ``` - -#[ derive( Debug, Default ) ] -pub struct HashSetSubformer< E, Formed, Context, ContainerEnd > -where - E : core::cmp::Eq + core::hash::Hash, - Formed : HashSetLike< E > + core::default::Default, - ContainerEnd : FormingEnd< Formed, Context >, -{ - formed : core::option::Option< Formed >, - context : core::option::Option< Context >, - on_end : core::option::Option< ContainerEnd >, - _e_phantom : core::marker::PhantomData< E >, -} - -impl< E, Formed, Context, ContainerEnd > -HashSetSubformer< E, Formed, Context, ContainerEnd > -where - E : core::cmp::Eq + core::hash::Hash, - Formed : HashSetLike< E > + core::default::Default, - ContainerEnd : FormingEnd< Formed, Context >, -{ - - /// Form current former into target structure. - #[ inline( always ) ] - pub fn form( mut self ) -> Formed - { - let formed = if self.formed.is_some() - { - self.formed.take().unwrap() - } - else - { - let val = Default::default(); - val - }; - formed - } - - /// Begins the building process with an optional context and formed. - /// - /// This method is typically called internally by the builder but can be used directly - /// to initialize the builder with specific contexts or containers. - /// - /// # Parameters - /// - `context`: An optional context for the building process. - /// - `formed`: An optional initial formed to populate. - /// - `on_end`: A handler to be called at the end of the building process. - /// - #[ inline( always ) ] - pub fn begin - ( - formed : core::option::Option< Formed >, - context : core::option::Option< Context >, - on_end : ContainerEnd, - ) -> Self - { - Self - { - formed, - context : context, - on_end : Some( on_end ), - _e_phantom : core::marker::PhantomData, - } - } - - /// Finalizes the building process and returns the constructed formed or a context. - /// - /// This method concludes the building process by applying the `on_end` handler to transform - /// the formed or incorporate it into a given context. It's typically called at the end - /// of the builder chain to retrieve the final product of the building process. - /// - /// # Returns - /// Depending on the `on_end` handler's implementation, this method can return either the - /// constructed formed or a context that incorporates the formed. - /// - #[ inline( always ) ] - pub fn end( mut self ) -> Context - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) - } - - /// Replaces the current formed with a new one. - /// - /// This method allows for replacing the entire set being built with a different one. - /// It can be useful in scenarios where a pre-populated set needs to be modified or - /// replaced entirely during the building process. - /// - /// # Parameters - /// - `formed`: The new formed to use for subsequent builder operations. - /// - /// # Returns - /// The builder instance with the formed replaced, enabling further chained operations. - /// - #[ inline( always ) ] - pub fn replace( mut self, formed : Formed ) -> Self - { - self.formed = Some( formed ); - self - } - -} - -// impl< E, Formed > VectorSubformer< E, Formed, Formed, crate::ReturnFormed > -// where -// Formed : VectorLike< E > + core::default::Default, -// { - -impl< E, Formed > -HashSetSubformer< E, Formed, Formed, crate::ReturnFormed > -where - E : core::cmp::Eq + core::hash::Hash, - Formed : HashSetLike< E > + core::default::Default, - // ContainerEnd : FormingEnd< Formed, Context >, -{ - - /// Initializes a new instance of the builder with default settings. - /// - /// This method provides a starting point for building a `HashSetLike` formed using - /// a fluent interface. It sets up an empty formed ready to be populated. - /// - /// # Returns - /// A new instance of `HashSetSubformer` with no elements. - /// - #[ inline( always ) ] - pub fn new() -> Self - { - HashSetSubformer::begin - ( - None, - None, - crate::ReturnFormed, - ) - } - -} - -impl< E, Formed, Context, ContainerEnd > -HashSetSubformer< E, Formed, Context, ContainerEnd > -where - E : core::cmp::Eq + core::hash::Hash, - Formed : HashSetLike< E > + core::default::Default, - ContainerEnd : FormingEnd< Formed, Context >, -{ - - /// Inserts an element into the set, possibly replacing an existing element. - /// - /// This method ensures that the set contains the given element, and if the element - /// was already present, it might replace it depending on the formed's behavior. - /// - /// # Parameters - /// - `element`: The element to insert into the set. - /// - /// # Returns - /// - `Some(element)` if the element was replaced. - /// - `None` if the element was newly inserted without replacing any existing element. - /// - #[ inline( always ) ] - pub fn insert< E2 >( mut self, element : E2 ) -> Self - where - E2 : core::convert::Into< E >, - { - if self.formed.is_none() - { - self.formed = core::option::Option::Some( Default::default() ); - } - if let core::option::Option::Some( ref mut formed ) = self.formed - { - formed.insert( element.into() ); - } - self - } - -} - -// \ No newline at end of file diff --git a/module/core/former/src/lib.rs b/module/core/former/src/lib.rs index c3fdf25c49..9653b8152e 100644 --- a/module/core/former/src/lib.rs +++ b/module/core/former/src/lib.rs @@ -4,43 +4,34 @@ #![ doc( html_root_url = "https://docs.rs/former/latest/former/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -// xxx : describe "Context-aware forming process" - /// Axiomatic things. #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_former" ) ] mod axiomatic; - -/// Interface for containers. +/// Forming process. #[ cfg( feature = "enabled" ) ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] #[ cfg( feature = "derive_former" ) ] -mod container; -/// Former of a vector. +mod definition; +/// Forming process. #[ cfg( feature = "enabled" ) ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] #[ cfg( feature = "derive_former" ) ] -mod vector; -/// Former of a hash map. +mod forming; +/// Storage. #[ cfg( feature = "enabled" ) ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] #[ cfg( feature = "derive_former" ) ] -mod hash_map; -/// Former of a hash set. +mod storage; + +/// Interface for containers. #[ cfg( feature = "enabled" ) ] #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] #[ cfg( feature = "derive_former" ) ] -mod hash_set; +mod container; /// Component-based forming. #[ cfg( feature = "enabled" ) ] #[ cfg( any( feature = "derive_component_from", feature = "derive_component_assign" ) ) ] mod component; -// mod axiomatic2; -// mod vector2; -// mod vector3; - /// Namespace with dependencies. #[ cfg( feature = "enabled" ) ] pub mod dependency @@ -91,7 +82,13 @@ pub mod exposed #[ allow( unused_imports ) ] #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_former" ) ] - pub use super::axiomatic::*; + pub use super:: + { + axiomatic::*, + definition::*, + forming::*, + storage::*, + }; #[ doc( inline ) ] #[ allow( unused_imports ) ] @@ -99,24 +96,6 @@ pub mod exposed #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] #[ cfg( feature = "derive_former" ) ] pub use super::container::*; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - #[ cfg( feature = "enabled" ) ] - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - #[ cfg( feature = "derive_former" ) ] - pub use super::vector::*; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - #[ cfg( feature = "enabled" ) ] - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - #[ cfg( feature = "derive_former" ) ] - pub use super::hash_map::*; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - #[ cfg( feature = "enabled" ) ] - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - #[ cfg( feature = "derive_former" ) ] - pub use super::hash_set::*; } @@ -130,5 +109,3 @@ pub mod prelude #[ cfg( any( feature = "derive_component_from", feature = "derive_component_assign" ) ) ] pub use super::component::*; } - -// xxx : explain role of container in former diff --git a/module/core/former/src/storage.rs b/module/core/former/src/storage.rs new file mode 100644 index 0000000000..02e20e78d5 --- /dev/null +++ b/module/core/former/src/storage.rs @@ -0,0 +1,49 @@ +//! Module `storage` +//! +//! Provides traits that define the storage mechanics used during the formation of entities in a builder pattern. +//! This module is critical for managing the state of entities as they are constructed, ensuring that all +//! interim data is handled appropriately before finalizing the entity's construction. +//! +//! Key components of the module include: +//! - **Storage Interface**: Defines the essential interface for any storage type used in the formation +//! process. It ensures that each storage type can be initialized to a default state. +//! - **Storage Preformation**: Outlines the method for transitioning storage from a mutable, intermediate +//! state to a finalized, immutable state of the entity. This is pivotal for concluding the formation process +//! with integrity and accuracy. +//! + +/// Defines the storage interface for entities being constructed using a forming pattern. +/// +/// This trait is required for any storage type that temporarily holds data during the construction +/// of an entity. It mandates the implementation of `Default`, ensuring that storage can be initialized +/// to a default state at the start of the forming process. +pub trait Storage : ::core::default::Default +{ + /// The type of the entity as it should appear once preformed. It could, but does not have to be the same type as `Formed`. + type Preformed; + // /// The type of the fully formed entity that results from the forming process. + // type Formed; +} + +/// Provides a mechanism to finalize the forming process by converting storage into its final formed state. +/// +/// This trait is crucial for transitioning the mutable, intermediate storage state into the final, +/// immutable state of an entity. The transformation is typically performed once all configurations +/// and modifications are applied to the storage during the forming process. The type `Preformed` and `Formed` is +/// generally the structure for which the `Former` trait is derived, representing the fully formed +/// state of the entity. However, it can differ if a custom `FormingEnd` or a different `Formed` type +/// is defined to handle specific forming logic or requirements. +/// But even if `Formed` is custom `Preformed` is always that structure. +pub trait StoragePreform : Storage +{ + // /// The type of the entity as it should appear once fully formed. + // type Preformed; + + /// Transforms the storage into the final formed state of the entity. + /// + /// This function is called at the conclusion of the forming process to finalize the entity's state, + /// effectively turning the mutable storage state into the immutable, fully formed entity. This transition + /// reflects the culmination of the forming process where the temporary, modifiable attributes of the + /// storage are solidified into the permanent attributes of the formed entity. + fn preform( self ) -> Self::Preformed; +} diff --git a/module/core/former/src/vector.rs b/module/core/former/src/vector.rs deleted file mode 100644 index 46426d8733..0000000000 --- a/module/core/former/src/vector.rs +++ /dev/null @@ -1,207 +0,0 @@ -use super::*; - -#[ allow( unused ) ] -use collection_tools::Vec; - -/// Trait for containers that behave like a vector, providing an interface for element addition. -/// -/// This trait enables the use of custom or standard vector-like containers within the builder pattern, -/// allowing for a unified and flexible approach to constructing collections. -/// -pub trait VectorLike< E > -{ - /// Appends an element to the back of a formed. - fn push( &mut self, element : E ); -} - -impl< E > VectorLike< E > for Vec< E > -{ - fn push( &mut self, element : E ) - { - Vec::push( self, element ); - } -} - -/// A builder for constructing `VectorLike` containers, facilitating a fluent and flexible interface. -/// -/// `VectorSubformer` leverages the `VectorLike` trait to enable the construction and manipulation -/// of vector-like containers in a builder pattern style, promoting readability and ease of use. -/// -/// # Example -/// ```rust -/// #[ derive( Debug, PartialEq, former::Former ) ] -/// pub struct StructWithVec -/// { -/// #[ subformer( former::VectorSubformer ) ] -/// vec : Vec< &'static str >, -/// } -/// -/// let instance = StructWithVec::former() -/// .vec() -/// .push( "apple" ) -/// .push( "banana" ) -/// .end() -/// .form(); -/// -/// assert_eq!( instance, StructWithVec { vec: vec![ "apple", "banana" ] } ); -///``` -/// -#[ derive( Debug, Default ) ] -pub struct VectorSubformer< E, Formed, Context, ContainerEnd > -where - Formed : VectorLike< E > + core::default::Default, - ContainerEnd : FormingEnd< Formed, Context >, -{ - formed : core::option::Option< Formed >, - context : core::option::Option< Context >, - on_end : core::option::Option< ContainerEnd >, - _phantom : core::marker::PhantomData< E >, -} - -impl< E, Formed, Context, ContainerEnd > VectorSubformer< E, Formed, Context, ContainerEnd > -where - Formed : VectorLike< E > + core::default::Default, - ContainerEnd : FormingEnd< Formed, Context >, -{ - - /// Form current former into target structure. - #[ inline( always ) ] - pub fn form( mut self ) -> Formed - { - let formed = if self.formed.is_some() - { - self.formed.take().unwrap() - } - else - { - let val = Default::default(); - val - }; - formed - } - - // /// Initializes a new `VectorSubformer` instance, starting with an empty formed. - // /// This function serves as the entry point for the builder pattern. - // /// - // /// # Returns - // /// A new instance of `VectorSubformer` with an empty internal formed. - // /// - // #[ inline( always ) ] - // pub fn new() -> VectorSubformer< E, Formed, Formed, impl FormingEnd< Formed, Formed > > - // { - // VectorSubformer::begin - // ( - // None, - // None, - // crate::ReturnFormed, - // ) - // } - - /// Begins the building process, optionally initializing with a context and formed. - #[ inline( always ) ] - pub fn begin - ( - formed : core::option::Option< Formed >, - context : core::option::Option< Context >, - on_end : ContainerEnd - ) -> Self - { - Self - { - context, - formed, - on_end : Some( on_end ), - _phantom : core::marker::PhantomData, - } - } - - /// Finalizes the building process, returning the formed or a context incorporating it. - #[ inline( always ) ] - pub fn end( mut self ) -> Context - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) - } - - /// Replaces the current formed with a provided one, allowing for a reset or redirection of the building process. - #[ inline( always ) ] - pub fn replace( mut self, vector : Formed ) -> Self - { - self.formed = Some( vector ); - self - } - -} - -impl< E, Formed > VectorSubformer< E, Formed, Formed, crate::ReturnFormed > -where - Formed : VectorLike< E > + core::default::Default, -{ - - /// Initializes a new `VectorSubformer` instance, starting with an empty formed. - /// This function serves as the entry point for the builder pattern. - /// - /// # Returns - /// A new instance of `VectorSubformer` with an empty internal formed. - /// - #[ inline( always ) ] - pub fn new() -> Self - { - Self::begin - ( - None, - None, - crate::ReturnFormed, - ) - } - -} - -impl< E, Formed, Context, ContainerEnd > VectorSubformer< E, Formed, Context, ContainerEnd > -where - Formed : VectorLike< E > + core::default::Default, - ContainerEnd : FormingEnd< Formed, Context >, -{ - - /// Appends an element to the end of the formed, expanding the internal collection. - #[ inline( always ) ] - pub fn push< E2 >( mut self, element : E2 ) -> Self - where E2 : core::convert::Into< E >, - { - if self.formed.is_none() - { - self.formed = core::option::Option::Some( Default::default() ); - } - if let core::option::Option::Some( ref mut formed ) = self.formed - { - formed.push( element.into() ); - } - self - } - -} - -// - -impl< E, Formed, Context, End > FormerBegin< Formed, Formed, Context > -for VectorSubformer< E, Formed, Context, End > -where - End : FormingEnd< Formed, Context >, - Formed : VectorLike< E > + Default, -{ - type End = End; - - #[ inline( always ) ] - fn _begin - ( - formed : core::option::Option< Formed >, - context : core::option::Option< Context >, - on_end : End, - ) -> Self - { - Self::begin( formed, context, on_end ) - } - -} diff --git a/module/core/former/src/vector2.rs b/module/core/former/src/vector2.rs deleted file mode 100644 index 9c7e17beb5..0000000000 --- a/module/core/former/src/vector2.rs +++ /dev/null @@ -1,195 +0,0 @@ -use super::*; -use axiomatic2::*; - -#[ allow( unused ) ] -use collection_tools::Vec; - -/// Trait for containers that behave like a vector, providing an interface for element addition. -/// -/// This trait enables the use of custom or standard vector-like containers within the builder pattern, -/// allowing for a unified and flexible approach to constructing collections. -/// -pub trait VectorLike2< E > -{ - /// Appends an element to the back of a formed. - fn push( &mut self, element : E ); -} - -impl< E > VectorLike2< E > for Vec< E > -{ - fn push( &mut self, element : E ) - { - Vec::push( self, element ); - } -} - -impl< E > StoragePerform for Vec< E > -{ - type Formed = Self; - fn preform( self ) -> Self::Formed - { - self - } -} -pub struct VectorSubformerDescriptor< E > -{ - _phantom : core::marker::PhantomData< E >, -} - -impl< E > VectorSubformerDescriptor< E > -{ - fn new() -> Self - { - Self { _phantom : core::marker::PhantomData } - } -} - -impl< E > FormerDescriptor -for VectorSubformerDescriptor< E > -{ - type Storage = Vec< E >; - type Formed = Vec< E >; - // type Former = VectorSubformer2< E, Context, End >; -} - -/// A builder for constructing `VectorLike2` containers, facilitating a fluent and flexible interface. -/// -/// `VectorSubformer2` leverages the `VectorLike2` trait to enable the construction and manipulation -/// of vector-like containers in a builder pattern style, promoting readability and ease of use. -#[ derive( Debug, Default ) ] -pub struct VectorSubformer2< E, Context, End > -where - End : FormingEnd2< VectorSubformerDescriptor< E >, Context >, -{ - formed : core::option::Option< < VectorSubformerDescriptor< E > as axiomatic2::FormerDescriptor >::Formed >, - context : core::option::Option< Context >, - on_end : core::option::Option< End >, -} - -impl< E, Context, End > VectorSubformer2< E, Context, End > -where - End : FormingEnd2< VectorSubformerDescriptor< E >, Context >, -{ - - /// Form current former into target structure. - #[ inline( always ) ] - pub fn form( mut self ) -> < VectorSubformerDescriptor< E > as axiomatic2::FormerDescriptor >::Formed - { - let formed = if self.formed.is_some() - { - self.formed.take().unwrap() - } - else - { - let val = Default::default(); - val - }; - formed - } - - /// Begins the building process, optionally initializing with a context and formed. - #[ inline( always ) ] - pub fn begin - ( - formed : core::option::Option< < VectorSubformerDescriptor< E > as axiomatic2::FormerDescriptor >::Formed >, - context : core::option::Option< Context >, - on_end : End - ) -> Self - { - Self - { - context, - formed, - on_end : Some( on_end ), - } - } - - /// Finalizes the building process, returning the formed or a context incorporating it. - #[ inline( always ) ] - pub fn end( mut self ) -> < VectorSubformerDescriptor< E > as axiomatic2::FormerDescriptor >::Formed - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) - } - - /// Replaces the current formed with a provided one, allowing for a reset or redirection of the building process. - #[ inline( always ) ] - pub fn replace( mut self, vector : < VectorSubformerDescriptor< E > as axiomatic2::FormerDescriptor >::Formed ) -> Self - { - self.formed = Some( vector ); - self - } - -} - -impl< E > VectorSubformer2< E, (), ReturnStorage2 > -where -{ - - /// Initializes a new `VectorSubformer2` instance, starting with an empty formed. - /// This function serves as the entry point for the builder pattern. - /// - /// # Returns - /// A new instance of `VectorSubformer2` with an empty internal formed. - /// - #[ inline( always ) ] - pub fn new() -> Self - { - Self::begin - ( - None, - None, - ReturnStorage2, - ) - } - -} - -impl< E, Context, End > VectorSubformer2< E, Context, End > -where - End : FormingEnd2< VectorSubformerDescriptor< E >, Context >, -{ - - /// Appends an element to the end of the formed, expanding the internal collection. - #[ inline( always ) ] - pub fn push< E2 >( mut self, element : E2 ) -> Self - where E2 : core::convert::Into< E >, - { - if self.formed.is_none() - { - self.formed = core::option::Option::Some( Default::default() ); - } - if let core::option::Option::Some( ref mut formed ) = self.formed - { - formed.push( element.into() ); - } - self - } - -} - -// - -// impl< Former, Context, End > FormerBegin< Formed, Formed, Context > -// for VectorSubformer2< Former, Context, End > -// where -// End : FormingEnd2< VectorSubformerDescriptor< E >, Context >, -// // Formed : VectorLike2< E > + Default, -// Former : FormerDescriptor, -// { -// type End = End; -// -// #[ inline( always ) ] -// fn _begin -// ( -// formed : core::option::Option< Formed >, -// context : core::option::Option< Context >, -// on_end : End, -// ) -> Self -// { -// Self::begin( formed, context, on_end ) -// } -// -// } diff --git a/module/core/former/src/vector3.rs b/module/core/former/src/vector3.rs deleted file mode 100644 index 4df4958ce3..0000000000 --- a/module/core/former/src/vector3.rs +++ /dev/null @@ -1,196 +0,0 @@ -use super::*; -use axiomatic2::*; - -#[ allow( unused ) ] -use collection_tools::Vec; - -/// Trait for containers that behave like a vector, providing an interface for element addition. -/// -/// This trait enables the use of custom or standard vector-like containers within the builder pattern, -/// allowing for a unified and flexible approach to constructing collections. -/// -pub trait VectorLike2< E > -{ - /// Appends an element to the back of a formed. - fn push( &mut self, element : E ); -} - -impl< E > VectorLike2< E > for Vec< E > -{ - fn push( &mut self, element : E ) - { - Vec::push( self, element ); - } -} - -impl< E > StoragePerform for Vec< E > -{ - type Formed = Self; - fn preform( self ) -> Self::Formed - { - self - } -} -pub struct VectorSubformerDescriptor< E > -{ - _phantom : core::marker::PhantomData< E >, -} - -impl< E > VectorSubformerDescriptor< E > -{ - fn new() -> Self - { - Self { _phantom : core::marker::PhantomData } - } -} - -impl< E > FormerDescriptor -for VectorSubformerDescriptor< E > -{ - type Storage = Vec< E >; - type Formed = Vec< E >; - // type Former = VectorSubformer2< E, Context, End >; -} - -/// A builder for constructing `VectorLike2` containers, facilitating a fluent and flexible interface. -/// -/// `VectorSubformer2` leverages the `VectorLike2` trait to enable the construction and manipulation -/// of vector-like containers in a builder pattern style, promoting readability and ease of use. -/// -#[ derive( Debug, Default ) ] -pub struct VectorSubformer2< E, Context, End > -where - End : FormingEnd2< VectorSubformerDescriptor< E >, Context >, -{ - formed : core::option::Option< < VectorSubformerDescriptor< E > as axiomatic2::FormerDescriptor >::Formed >, - context : core::option::Option< Context >, - on_end : core::option::Option< End >, -} - -impl< E, Context, End > VectorSubformer2< E, Context, End > -where - End : FormingEnd2< VectorSubformerDescriptor< E >, Context >, -{ - - /// Form current former into target structure. - #[ inline( always ) ] - pub fn form( mut self ) -> < VectorSubformerDescriptor< E > as axiomatic2::FormerDescriptor >::Formed - { - let formed = if self.formed.is_some() - { - self.formed.take().unwrap() - } - else - { - let val = Default::default(); - val - }; - formed - } - - /// Begins the building process, optionally initializing with a context and formed. - #[ inline( always ) ] - pub fn begin - ( - formed : core::option::Option< < VectorSubformerDescriptor< E > as axiomatic2::FormerDescriptor >::Formed >, - context : core::option::Option< Context >, - on_end : End - ) -> Self - { - Self - { - context, - formed, - on_end : Some( on_end ), - } - } - - /// Finalizes the building process, returning the formed or a context incorporating it. - #[ inline( always ) ] - pub fn end( mut self ) -> < VectorSubformerDescriptor< E > as axiomatic2::FormerDescriptor >::Formed - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) - } - - /// Replaces the current formed with a provided one, allowing for a reset or redirection of the building process. - #[ inline( always ) ] - pub fn replace( mut self, vector : < VectorSubformerDescriptor< E > as axiomatic2::FormerDescriptor >::Formed ) -> Self - { - self.formed = Some( vector ); - self - } - -} - -impl< E > VectorSubformer2< E, (), ReturnStorage2 > -where -{ - - /// Initializes a new `VectorSubformer2` instance, starting with an empty formed. - /// This function serves as the entry point for the builder pattern. - /// - /// # Returns - /// A new instance of `VectorSubformer2` with an empty internal formed. - /// - #[ inline( always ) ] - pub fn new() -> Self - { - Self::begin - ( - None, - None, - ReturnStorage2, - ) - } - -} - -impl< E, Context, End > VectorSubformer2< E, Context, End > -where - End : FormingEnd2< VectorSubformerDescriptor< E >, Context >, -{ - - /// Appends an element to the end of the formed, expanding the internal collection. - #[ inline( always ) ] - pub fn push< E2 >( mut self, element : E2 ) -> Self - where E2 : core::convert::Into< E >, - { - if self.formed.is_none() - { - self.formed = core::option::Option::Some( Default::default() ); - } - if let core::option::Option::Some( ref mut formed ) = self.formed - { - formed.push( element.into() ); - } - self - } - -} - -// - -// impl< Former, Context, End > FormerBegin< Formed, Formed, Context > -// for VectorSubformer2< Former, Context, End > -// where -// End : FormingEnd2< VectorSubformerDescriptor< E >, Context >, -// // Formed : VectorLike2< E > + Default, -// Former : FormerDescriptor, -// { -// type End = End; -// -// #[ inline( always ) ] -// fn _begin -// ( -// formed : core::option::Option< Formed >, -// context : core::option::Option< Context >, -// on_end : End, -// ) -> Self -// { -// Self::begin( formed, context, on_end ) -// } -// -// } diff --git a/module/core/former/tests/inc/compiletime/former_bad_attr.stderr b/module/core/former/tests/inc/compiletime/former_bad_attr.stderr deleted file mode 100644 index bc5a44f0c1..0000000000 --- a/module/core/former/tests/inc/compiletime/former_bad_attr.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: Unknown attribute #[defaultx(31)] - --> tests/inc/compiletime/former_bad_attr.rs:6:3 - | -6 | #[ defaultx( 31 ) ] - | ^^^^^^^^^^^^^^^^^^^ - -error: cannot find attribute `defaultx` in this scope - --> tests/inc/compiletime/former_bad_attr.rs:6:6 - | -6 | #[ defaultx( 31 ) ] - | ^^^^^^^^ help: a derive helper attribute with a similar name exists: `default` diff --git a/module/core/former/tests/inc/compiletime/components_component_from_debug.rs b/module/core/former/tests/inc/components_tests/compiletime/components_component_from_debug.rs similarity index 100% rename from module/core/former/tests/inc/compiletime/components_component_from_debug.rs rename to module/core/former/tests/inc/components_tests/compiletime/components_component_from_debug.rs 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 aa82903577..9ca13cbcba 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/components_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 b71e4f9624..d2aff86d2c 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/components_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 e716895ff5..965218114f 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/components_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 72912f2fb1..215a1f6ff5 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/components_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 2f95f9fb5c..e2dbb6fda4 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_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 11c499cd04..182ad0dacf 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_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 1fab721fe4..7105ba0b0e 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/components_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 14ba81afe1..34e77f09af 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/components_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 6a2a61d125..9f69aab624 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/components_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 3f01fe56cb..6a6c29e323 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/components_from_components.rs" ); diff --git a/module/core/former/tests/inc/only_test/components_component_assign.rs b/module/core/former/tests/inc/components_tests/only_test/components_component_assign.rs similarity index 100% rename from module/core/former/tests/inc/only_test/components_component_assign.rs rename to module/core/former/tests/inc/components_tests/only_test/components_component_assign.rs diff --git a/module/core/former/tests/inc/only_test/components_component_from.rs b/module/core/former/tests/inc/components_tests/only_test/components_component_from.rs similarity index 100% rename from module/core/former/tests/inc/only_test/components_component_from.rs rename to module/core/former/tests/inc/components_tests/only_test/components_component_from.rs diff --git a/module/core/former/tests/inc/only_test/components_components_assign.rs b/module/core/former/tests/inc/components_tests/only_test/components_components_assign.rs similarity index 100% rename from module/core/former/tests/inc/only_test/components_components_assign.rs rename to module/core/former/tests/inc/components_tests/only_test/components_components_assign.rs diff --git a/module/core/former/tests/inc/only_test/components_composite.rs b/module/core/former/tests/inc/components_tests/only_test/components_composite.rs similarity index 100% rename from module/core/former/tests/inc/only_test/components_composite.rs rename to module/core/former/tests/inc/components_tests/only_test/components_composite.rs diff --git a/module/core/former/tests/inc/only_test/components_from_components.rs b/module/core/former/tests/inc/components_tests/only_test/components_from_components.rs similarity index 100% rename from module/core/former/tests/inc/only_test/components_from_components.rs rename to module/core/former/tests/inc/components_tests/only_test/components_from_components.rs diff --git a/module/core/former/tests/inc/former_tests/a_basic.rs b/module/core/former/tests/inc/former_tests/a_basic.rs new file mode 100644 index 0000000000..a3f7e74e5f --- /dev/null +++ b/module/core/former/tests/inc/former_tests/a_basic.rs @@ -0,0 +1,18 @@ +#![ deny( missing_docs ) ] + +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, former::Former ) ] +// #[ derive( Debug, PartialEq, former::Former ) ] #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Struct1 +{ + pub int_1 : i32, +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/basic.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_basic_manual.rs b/module/core/former/tests/inc/former_tests/a_basic_manual.rs new file mode 100644 index 0000000000..4e0fd2aebc --- /dev/null +++ b/module/core/former/tests/inc/former_tests/a_basic_manual.rs @@ -0,0 +1,325 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq ) ] +pub struct Struct1 +{ + pub int_1 : i32, +} + +// == begin of generated + +// = formed + +#[ automatically_derived ] +impl Struct1 +{ + + #[ inline( always ) ] + pub fn former() -> Struct1Former< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > > + { + Struct1Former + ::< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > > + ::new( former::ReturnPreformed ) + } + +} + +// = entity to former + +impl< Definition > former::EntityToFormer< Definition > for Struct1 +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, +{ + type Former = Struct1Former< Definition >; +} + +impl former::EntityToStorage for Struct1 +{ + type Storage = Struct1FormerStorage; +} + +impl< Context, Formed, End > former::EntityToDefinition< Context, Formed, End > +for Struct1 +where + End : former::FormingEnd< Struct1FormerDefinitionTypes< Context, Formed > >, +{ + type Definition = Struct1FormerDefinition< Context, Formed, End >; + type Types = Struct1FormerDefinitionTypes< Context, Formed >; +} + +impl< Context, Formed > former::EntityToDefinitionTypes< Context, Formed > +for Struct1 +{ + type Types = Struct1FormerDefinitionTypes< Context, Formed >; +} + +// = definition types + +#[ derive( Debug ) ] +// pub struct Struct1FormerDefinitionTypes< Context = (), Formed = Struct1 > +pub struct Struct1FormerDefinitionTypes< Context, Formed > +{ + _phantom : core::marker::PhantomData< ( Context, Formed ) >, +} + +impl< Context, Formed > Default for Struct1FormerDefinitionTypes< Context, Formed > +{ + fn default() -> Self + { + Self { _phantom : core::marker::PhantomData, } + } +} + +impl< Context, Formed > former::FormerDefinitionTypes +for Struct1FormerDefinitionTypes< Context, Formed > +{ + type Storage = Struct1FormerStorage; + type Formed = Formed; + type Context = Context; +} + +// = definition + +#[ derive( Debug ) ] +// pub struct Struct1FormerDefinition< Context = (), Formed = Struct1, End = former::ReturnPreformed > +pub struct Struct1FormerDefinition< Context, Formed, End > +{ + _phantom : core::marker::PhantomData< ( Context, Formed, End ) >, +} + +impl< Context, Formed, End > Default for Struct1FormerDefinition< Context, Formed, End > +{ + fn default() -> Self + { + Self { _phantom : core::marker::PhantomData, } + } +} + +impl< Context, Formed, End > former::FormerDefinition for Struct1FormerDefinition< Context, Formed, End > +where + End : former::FormingEnd< Struct1FormerDefinitionTypes< Context, Formed > > +{ + type Storage = Struct1FormerStorage; + type Formed = Formed; + type Context = Context; + type Types = Struct1FormerDefinitionTypes< Context, Formed >; + type End = End; +} + +// pub type Struct1FormerWithClosure< Context, Formed > = +// Struct1FormerDefinition< Context, Formed, former::FormingEndClosure< Struct1FormerDefinitionTypes< Context, Formed > > >; + +// = storage + +pub struct Struct1FormerStorage +{ + pub int_1 : ::core::option::Option< i32 >, +} + +impl ::core::default::Default for Struct1FormerStorage +{ + #[ inline( always ) ] + fn default() -> Self + { + Self { int_1 : ::core::option::Option::None, } + } +} + +impl former::Storage for Struct1FormerStorage +{ + type Preformed = Struct1; +} + +impl former::StoragePreform for Struct1FormerStorage +{ + // type Preformed = < Self as former::Storage >::Formed; + fn preform( mut self ) -> Self::Preformed + { + let int_1 = if self.int_1.is_some() + { + self.int_1.take().unwrap() + } + else + { + { + trait MaybeDefault< T > + { + fn maybe_default( self : & Self ) -> T + { + panic!( "Field 'int_1' isn't initialized" ) + } + } + + impl< T > MaybeDefault< T > for & ::core::marker::PhantomData< T > {} + impl< T > MaybeDefault< T > for ::core::marker::PhantomData< T > + where T : ::core::default::Default, + { + fn maybe_default( self : & Self ) -> T { T::default() } + } + + (& ::core::marker::PhantomData::< i32 >).maybe_default() + } + }; + let result = Struct1 { int_1, }; + return result; + } +} + +// = former mutator + +impl< Context, Formed > former::FormerMutator +for Struct1FormerDefinitionTypes< Context, Formed > +{ +} + +// = former + +pub struct Struct1Former +< + Definition = Struct1FormerDefinition< (), Struct1, former::ReturnPreformed >, +> +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, +{ + storage : Definition::Storage, + context : ::core::option::Option< Definition::Context >, + on_end : ::core::option::Option< Definition::End >, +} + +#[ automatically_derived ] +impl< Definition > Struct1Former< Definition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, + +{ + + #[ inline( always ) ] + pub fn perform( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + let result = self.form(); + return result; + } + + #[ inline( always ) ] + pub fn new( on_end : Definition::End ) -> Self + { + Self::begin_coercing( None, None, on_end ) + } + + #[ inline( always ) ] + pub fn new_coercing< IntoEnd >( end : IntoEnd ) -> Self + where IntoEnd : Into< Definition::End >, + { + Self::begin_coercing( None, None, end, ) + } + + #[ inline( always ) ] + pub fn begin + ( + mut storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : < Definition as former::FormerDefinition >::End, + ) + -> Self + { + if storage.is_none() + { + storage = Some( ::core::default::Default::default() ); + } + Self + { + storage : storage.unwrap(), + context : context, + on_end : ::core::option::Option::Some( on_end ), + } + } + + #[ inline( always ) ] + pub fn begin_coercing< IntoEnd > + ( + mut storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : IntoEnd, + ) + -> Self + where + IntoEnd : ::core::convert::Into< < Definition as former::FormerDefinition >::End >, + { + if storage.is_none() + { + storage = Some( ::core::default::Default::default() ); + } + Self + { + storage : storage.unwrap(), + context : context, + on_end : ::core::option::Option::Some( ::core::convert::Into::into( on_end ) ), + } + } + + #[ inline( always ) ] + pub fn form( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + self.end() + } + + #[ inline( always ) ] + pub fn end( mut self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + let on_end = self.on_end.take().unwrap(); + let mut context = self.context.take(); + < Definition::Types as former::FormerMutator >::form_mutation( &mut self.storage, &mut context ); + former::FormingEnd::< Definition::Types >::call( & on_end, self.storage, context ) + } + + #[ inline ] + pub fn int_1< Src >( mut self, src : Src ) -> Self + where Src : ::core::convert::Into< i32 >, + { + debug_assert!( self.storage.int_1.is_none() ); + self.storage.int_1 = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); + self + } + +} + +// = preform with Storage::preform + +impl< Definition > Struct1Former< Definition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage, Formed = Struct1 >, + Definition::Storage : former::StoragePreform< Preformed = Struct1 >, + +{ + pub fn preform( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + former::StoragePreform::preform( self.storage ) + } +} + +impl< Definition > former::FormerBegin< Definition > +for Struct1Former< Definition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, + +{ + + #[ inline( always ) ] + fn former_begin + ( + storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : Definition::End, + ) + -> Self + { + debug_assert!( storage.is_none() ); + Self::begin( None, context, on_end ) + } + +} + +// == end of generated + +include!( "./only_test/basic.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_containers.rs b/module/core/former/tests/inc/former_tests/a_containers.rs new file mode 100644 index 0000000000..c2466841bf --- /dev/null +++ b/module/core/former/tests/inc/former_tests/a_containers.rs @@ -0,0 +1,26 @@ +#![ deny( missing_docs ) ] + +#[ allow( unused_imports ) ] +use super::*; + +// use std::collections::HashMap; +// use std::collections::HashSet; + +#[ derive( Default, Debug, PartialEq, former::Former ) ] +// #[ derive( Default, Debug, PartialEq, former::Former ) ] #[ debug ] +// #[ derive( Default, Debug, PartialEq ) ] +pub struct Struct1 +{ + #[ container( definition = former::VectorDefinition ) ] + vec_1 : Vec< String >, + #[ container( definition = former::HashMapDefinition ) ] + hashmap_1 : std::collections::HashMap< String, String >, + #[ container( definition = former::HashSetDefinition ) ] + hashset_1 : std::collections::HashSet< String >, +} + +// == generated begin + +// == generated end + +include!( "./only_test/containers_with_subformer.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_containers_manual.rs b/module/core/former/tests/inc/former_tests/a_containers_manual.rs new file mode 100644 index 0000000000..eed2cbfa9a --- /dev/null +++ b/module/core/former/tests/inc/former_tests/a_containers_manual.rs @@ -0,0 +1,672 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Default, Debug, PartialEq ) ] +pub struct Struct1 +{ + vec_1 : Vec< String >, + hashmap_1 : collection_tools::HashMap< String, String >, + hashset_1 : collection_tools::HashSet< String >, +} + +// == begin of generated + +#[automatically_derived] +impl< > Struct1< > +where +{ + + + + #[ inline( always ) ] + pub fn former() -> Struct1Former< + Struct1FormerDefinition<(), Struct1<>, former::ReturnPreformed> + > + { + Struct1Former::< Struct1FormerDefinition< (), Struct1<>, former::ReturnPreformed > >::new_coercing(former::ReturnPreformed) + } +} + +impl< Definition > former::EntityToFormer< Definition > +for Struct1< > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage<> >, +{ + type Former = Struct1Former< Definition >; +} + +impl< > former::EntityToStorage for Struct1< > +where +{ + type Storage = Struct1FormerStorage<>; +} + +#[derive(Debug)] +pub struct Struct1FormerDefinitionTypes< Context = (), Formed = Struct1<>, > +where +{ + _phantom : core::marker::PhantomData<(Context, Formed)>, +} + +impl< Context, Formed, > core::default::Default +for Struct1FormerDefinitionTypes< Context, Formed, > +where +{ + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } +} + +impl< Context, Formed, > former::FormerDefinitionTypes +for Struct1FormerDefinitionTypes< Context, Formed, > +where +{ + type Storage = Struct1FormerStorage<>; + type Formed = Formed; + type Context = Context; +} + +impl< Context, Formed > former::FormerMutator +for Struct1FormerDefinitionTypes< Context, Formed > +{ +} + +#[derive(Debug)] +pub struct Struct1FormerDefinition< Context = (), Formed = Struct1<>, End = former::ReturnPreformed, > +where +{ + _phantom : core::marker::PhantomData<(Context, Formed, End)>, +} + +impl< Context, Formed, End, > core::default::Default for Struct1FormerDefinition< Context, Formed, End, > +where +{ + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } +} + +impl< Context, Formed, End, > former::FormerDefinition for Struct1FormerDefinition< Context, Formed, End, > +where + End : former::FormingEnd< Struct1FormerDefinitionTypes< Context, Formed, > >, +{ + type Types = Struct1FormerDefinitionTypes< Context, Formed, >; + type End = End; + type Storage = Struct1FormerStorage<>; + type Formed = Formed; + type Context = Context; +} + + +pub struct Struct1FormerStorage<> +where +{ + + pub vec_1 : core::option::Option>, + + pub hashmap_1 : core::option::Option>, + + pub hashset_1 : core::option::Option>, +} + +impl< > core::default::Default for Struct1FormerStorage<> +where +{ + #[ inline( always ) ] + fn default() -> Self + { + Self + { + vec_1 : core::option::Option::None, + hashmap_1 : core::option::Option::None, + hashset_1 : core::option::Option::None, + } + } +} + +impl< > former::Storage for Struct1FormerStorage<> +where +{ + type Preformed = Struct1<>; +} + +impl< > former::StoragePreform for Struct1FormerStorage<> +where +{ + // type Preformed = Struct1<>; + + fn preform(mut self) -> Self::Preformed + { + let vec_1 = if self.vec_1.is_some() + { + self.vec_1.take().unwrap() + } + else + { + { + trait MaybeDefault + { + fn maybe_default(self: &Self) -> T + { + panic!("Field 'vec_1' isn't initialized") + } + } + + impl MaybeDefault for &core::marker::PhantomData {} + + impl MaybeDefault for core::marker::PhantomData + where + T : core::default::Default, + { + fn maybe_default(self: &Self) -> T + { + T::default() + } + } + + (&core::marker::PhantomData::>).maybe_default() + } + }; + + let hashmap_1 = if self.hashmap_1.is_some() + { + self.hashmap_1.take().unwrap() + } + else + { + { + trait MaybeDefault + { + fn maybe_default(self: &Self) -> T + { + panic!("Field 'hashmap_1' isn't initialized") + } + } + + impl MaybeDefault for &core::marker::PhantomData {} + + impl MaybeDefault for core::marker::PhantomData + where + T : core::default::Default, + { + fn maybe_default(self: &Self) -> T + { + T::default() + } + } + + (&core::marker::PhantomData::>).maybe_default() + } + }; + + let hashset_1 = if self.hashset_1.is_some() + { + self.hashset_1.take().unwrap() + } + else + { + { + trait MaybeDefault + { + fn maybe_default(self: &Self) -> T + { + panic!("Field 'hashset_1' isn't initialized") + } + } + + impl MaybeDefault for &core::marker::PhantomData {} + + impl MaybeDefault for core::marker::PhantomData + where + T : core::default::Default, + { + fn maybe_default(self: &Self) -> T + { + T::default() + } + } + + (&core::marker::PhantomData::>).maybe_default() + } + }; + + let result = Struct1::<> + { + vec_1, hashmap_1, hashset_1, + }; + + return result; + } +} + +pub struct Struct1Former< Definition = Struct1FormerDefinition<(), Struct1<>, former::ReturnPreformed>, > +where + Definition : former::FormerDefinition, + Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage<> >, +{ + storage : ::Storage, + context : core::option::Option<::Context>, + on_end : core::option::Option, +} + +#[automatically_derived] +impl< Definition, > Struct1Former< Definition, > +where + Definition : former::FormerDefinition, + Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage<> >, +{ + + + + #[ inline( always ) ] + pub fn new(on_end: Definition::End) -> Self + { + Self::begin_coercing(None, None, on_end) + } + + + + + #[ inline( always ) ] + pub fn new_coercing(end: IntoEnd) -> Self + where + IntoEnd : Into, + { + Self::begin_coercing(None, None, end,) + } + + + + + #[ inline( always ) ] + pub fn begin(mut storage: core::option::Option<::Storage>, context: core::option::Option<::Context>, on_end: ::End,) -> Self + { + if storage.is_none() + { + storage = Some(core::default::Default::default()); + } + Self + { + storage: storage.unwrap(), + context: context, + on_end: core::option::Option::Some(on_end), + } + } + + + + + #[ inline( always ) ] + pub fn begin_coercing(mut storage: core::option::Option<::Storage>, context: core::option::Option<::Context>, on_end: IntoEnd,) -> Self + where + IntoEnd : core::convert::Into<::End>, + { + if storage.is_none() + { + storage = Some(core::default::Default::default()); + } + Self + { + storage: storage.unwrap(), + context: context, + on_end: core::option::Option::Some(core::convert::Into::into(on_end)), + } + } + + + + + #[ inline( always ) ] + pub fn form(self) -> ::Formed + { + self.end() + } + + #[ inline( always ) ] + pub fn end(mut self) -> ::Formed + { + let on_end = self.on_end.take().unwrap(); + let context = self.context.take(); + former::FormingEnd::::call(&on_end, self.storage, context) + } + + #[ inline( always ) ] + pub fn _vec_1_assign< Former2 >( self ) -> Former2 + where + Former2 : former::FormerBegin + < + former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End< Definition > >, + >, + former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End< Definition > > : former::FormerDefinition + < + // Storage : former::ContainerAdd< Entry = < collection_tools::Vec< String > as former::Container >::Entry >, + Storage = Vec< String >, + Context = Struct1Former< Definition >, + End = Struct1FormerAssignVec1End< Definition >, + >, + Struct1FormerAssignVec1End< Definition > : former::FormingEnd + < + < collection_tools::Vec< String > as former::EntityToDefinitionTypes< Self, Self > >::Types + >, + { + Former2::former_begin( None, Some( self ), Struct1FormerAssignVec1End::< Definition >::default() ) + } + + #[ inline( always ) ] + pub fn vec_1( self ) -> former::ContainerFormer:: + < + String, + former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End< Definition > >, + > + where + former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End< Definition > > : former::FormerDefinition + < + // Storage : former::ContainerAdd< Entry = < collection_tools::Vec< String > as former::Container >::Entry >, + Storage = Vec< String >, + Context = Struct1Former< Definition >, + End = Struct1FormerAssignVec1End< Definition >, + >, + Struct1FormerAssignVec1End< Definition > : former::FormingEnd + < + < collection_tools::Vec< String > as former::EntityToDefinitionTypes< Self, Self > >::Types + >, + { + self._vec_1_assign::< former::ContainerFormer:: + < + String, + former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End< Definition > >, + > > () + } + + #[ inline( always ) ] + pub fn _hashmap_1_assign< Former2 >( self ) -> Former2 + where + Former2 : former::FormerBegin + < + former::HashMapDefinition< String, String, Self, Self, Struct1FormerAssignHashmap1End< Definition > >, + >, + former::HashMapDefinition< String, String, Self, Self, Struct1FormerAssignHashmap1End< Definition > > : former::FormerDefinition + < + // Storage : former::ContainerAdd< Entry = < collection_tools::HashMap< String, String > as former::Container >::Entry >, + Storage = collection_tools::HashMap< String, String >, + Context = Struct1Former< Definition >, + End = Struct1FormerAssignHashmap1End< Definition >, + >, + Struct1FormerAssignHashmap1End< Definition > : former::FormingEnd + < + < collection_tools::HashMap< String, String > as former::EntityToDefinitionTypes< Self, Self > >::Types + >, + { + Former2::former_begin( None, Some( self ), Struct1FormerAssignHashmap1End::< Definition >::default() ) + } + + #[ inline( always ) ] + pub fn hashmap_1( self ) -> former::ContainerFormer:: + < + ( String, String ), + former::HashMapDefinition< String, String, Self, Self, Struct1FormerAssignHashmap1End< Definition > >, + > + where + former::HashMapDefinition< String, String, Self, Self, Struct1FormerAssignHashmap1End< Definition > > : former::FormerDefinition + < + // Storage : former::ContainerAdd< Entry = < collection_tools::HashMap< String, String > as former::Container >::Entry >, + Storage = collection_tools::HashMap< String, String >, + Context = Struct1Former< Definition >, + End = Struct1FormerAssignHashmap1End< Definition >, + >, + Struct1FormerAssignHashmap1End< Definition > : former::FormingEnd + < + < collection_tools::HashMap< String, String > as former::EntityToDefinitionTypes< Self, Self > >::Types + >, + { + self._hashmap_1_assign::< former::ContainerFormer:: + < + ( String, String ), + former::HashMapDefinition< String, String, Self, Self, Struct1FormerAssignHashmap1End< Definition > >, + > > () + } + + #[ inline( always ) ] + pub fn _hashset_1_assign< Former2 >( self ) -> Former2 + where + Former2 : former::FormerBegin + < + former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, + >, + former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > > : former::FormerDefinition + < + // Storage : former::ContainerAdd< Entry = < collection_tools::HashSet< String > as former::Container >::Entry >, + Storage = collection_tools::HashSet< String >, + Context = Struct1Former< Definition >, + End = Struct1FormerAssignHashset1End< Definition >, + >, + Struct1FormerAssignHashset1End< Definition > : former::FormingEnd + < + < collection_tools::HashSet< String > as former::EntityToDefinitionTypes< Self, Self > >::Types + >, + { + Former2::former_begin( None, Some( self ), Struct1FormerAssignHashset1End::< Definition >::default() ) + } + + #[ inline( always ) ] + pub fn hashset_1( self ) -> former::ContainerFormer:: + < + String, + former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, + > + where + former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > > : former::FormerDefinition + < + // Storage : former::ContainerAdd< Entry = < collection_tools::HashSet< String > as former::Container >::Entry >, + Storage = collection_tools::HashSet< String >, + Context = Struct1Former< Definition >, + End = Struct1FormerAssignHashset1End< Definition >, + >, + Struct1FormerAssignHashset1End< Definition > : former::FormingEnd + < + < collection_tools::HashSet< String > as former::EntityToDefinitionTypes< Self, Self > >::Types + >, + { + self._hashset_1_assign::< former::ContainerFormer:: + < + String, + former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, + > > () + } + +} + +impl< Definition, > Struct1Former< Definition, > +where + Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage<>, Formed = Struct1<> >, + Definition : former::FormerDefinition, + Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage<> >, +{ + pub fn preform(self) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + former::StoragePreform::preform(self.storage) + } +} + +impl< Definition, > Struct1Former< Definition, > +where + Definition : former::FormerDefinition, + Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage<>, Formed = Struct1<> >, +{ + + #[ inline( always ) ] + pub fn perform(self) -> ::Formed + { + let result = self.form(); + return result; + } +} + +impl< Definition > former::FormerBegin< Definition > for Struct1Former< Definition, > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage<> >, +{ + #[ inline( always ) ] + fn former_begin(storage: core::option::Option, context: core::option::Option, on_end: Definition::End,) -> Self + { + debug_assert!(storage.is_none()); + Self::begin(None, context, on_end) + } +} + +#[ allow( dead_code ) ] +pub type Struct1AsSubformer< Superformer, End > = Struct1Former +< + Struct1FormerDefinition< Superformer, Superformer, End, >, +>; + +#[ allow( dead_code ) ] +pub trait Struct1AsSubformerEnd +where Self : former::FormingEnd< Struct1FormerDefinitionTypes, > +{} + +impl Struct1AsSubformerEnd for T +where + Self : former::FormingEnd< Struct1FormerDefinitionTypes, >, +{} + +// = former assign end + +pub struct Struct1FormerAssignVec1End< Definition > +{ + _phantom : core::marker::PhantomData< ( Definition, ) >, +} + +impl Default for Struct1FormerAssignVec1End< Definition > +{ + #[ inline( always ) ] + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } +} + +// Struct1Former< Definition = Struct1FormerDefinition<(), Struct1<>, former::ReturnPreformed>, > + +impl< Definition > former::FormingEnd +< + former::VectorDefinitionTypes< String, Struct1Former< Definition >, Struct1Former< Definition > > +> +for Struct1FormerAssignVec1End< Definition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, + Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage >, +{ + #[ inline( always ) ] + fn call( &self, storage : collection_tools::Vec< String >, super_former : Option< Struct1Former< Definition > > ) + -> Struct1Former< Definition, > + { + let mut super_former = super_former.unwrap(); + if let Some( ref mut field ) = super_former.storage.vec_1 + { + former::ContainerAssign::assign( field, storage ); + } + else + { + super_former.storage.vec_1 = Some( storage ); + } + super_former + } +} + +pub struct Struct1FormerAssignHashmap1End +{ + _phantom : core::marker::PhantomData<(Definition,)>, +} + +impl Default for Struct1FormerAssignHashmap1End +{ + #[ inline( always ) ] + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } +} + +impl< Definition, > former::FormingEnd +< former::HashMapDefinitionTypes< String, String, Struct1Former< Definition >, Struct1Former< Definition > > > +for Struct1FormerAssignHashmap1End< Definition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, + Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage >, +{ + #[ inline( always ) ] + fn call( &self, storage : collection_tools::HashMap< String, String >, super_former : Option< Struct1Former< Definition > > ) + -> Struct1Former< Definition, > + { + let mut super_former = super_former.unwrap(); + if let Some( ref mut field ) = super_former.storage.hashmap_1 + { + former::ContainerAssign::assign( field, storage ); + } + else + { + super_former.storage.hashmap_1 = Some( storage ); + } + super_former + } +} + +pub struct Struct1FormerAssignHashset1End +{ + _phantom : core::marker::PhantomData<(Definition,)>, +} + +impl Default for Struct1FormerAssignHashset1End +{ + #[ inline( always ) ] + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } +} + +impl< Definition, > former::FormingEnd +< former::HashSetDefinitionTypes< String, Struct1Former< Definition >, Struct1Former< Definition > > > +for Struct1FormerAssignHashset1End< Definition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, + Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage >, +{ + #[ inline( always ) ] + fn call( &self, storage : collection_tools::HashSet< String >, super_former : Option< Struct1Former< Definition >, > ) + -> Struct1Former< Definition, > + { + let mut super_former = super_former.unwrap(); + if let Some( ref mut field ) = super_former.storage.hashset_1 + { + former::ContainerAssign::assign( field, storage ); + } + else + { + super_former.storage.hashset_1 = Some( storage ); + } + super_former + } +} + +// == end of generated + +include!( "./only_test/containers_with_subformer.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_containers_scalar.rs b/module/core/former/tests/inc/former_tests/a_containers_scalar.rs new file mode 100644 index 0000000000..4b3efafb88 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/a_containers_scalar.rs @@ -0,0 +1,23 @@ +#![ deny( missing_docs ) ] + +#[ allow( unused_imports ) ] +use super::*; + +use std::collections::HashMap; +use std::collections::HashSet; + +#[ derive( Debug, PartialEq, the_module::Former ) ] +// #[ derive( Debug, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Struct1 +{ + vec_1 : Vec< String >, + hashmap_1 : HashMap< String, String >, + hashset_1 : HashSet< String >, +} + +// = begin_coercing of generated + +// == end of generated + +include!( "./only_test/containers_without_subformer.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_containers_with_runtime.rs b/module/core/former/tests/inc/former_tests/a_containers_with_runtime.rs deleted file mode 100644 index ead3284c94..0000000000 --- a/module/core/former/tests/inc/former_tests/a_containers_with_runtime.rs +++ /dev/null @@ -1,18 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -// use std::collections::HashMap; -// use std::collections::HashSet; - -#[ derive( Debug, PartialEq, the_module::Former ) ] -pub struct Struct1 -{ - #[ subformer( the_module::VectorSubformer ) ] - vec_1 : Vec< String >, - #[ subformer( the_module::HashMapSubformer ) ] - hashmap_strings_1 : std::collections::HashMap< String, String >, - #[ subformer( the_module::HashSetSubformer ) ] - hashset_strings_1 : std::collections::HashSet< String >, -} - -include!( "../only_test/containers_with_runtime.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_containers_with_runtime_manual.rs b/module/core/former/tests/inc/former_tests/a_containers_with_runtime_manual.rs deleted file mode 100644 index 87a3bec1eb..0000000000 --- a/module/core/former/tests/inc/former_tests/a_containers_with_runtime_manual.rs +++ /dev/null @@ -1,295 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -#[ derive( Debug, PartialEq ) ] -pub struct Struct1 -{ - vec_1 : Vec< String >, - hashmap_strings_1 : std::collections::HashMap< String, String >, - hashset_strings_1 : std::collections::HashSet< String >, -} - -// - -impl Struct1 -{ - pub fn former() -> Struct1Former< Struct1, the_module::ReturnFormed > - { - Struct1Former::< Struct1, the_module::ReturnFormed >::new() - } -} - -// generated by former -pub struct Struct1FormerStorage -{ - pub vec_1 : ::core::option::Option< Vec< String > >, - pub hashmap_strings_1 : ::core::option::Option< std::collections::HashMap< String, String > >, - pub hashset_strings_1 : ::core::option::Option< std::collections::HashSet< String > >, -} - -impl Default for Struct1FormerStorage -{ - - #[ inline( always ) ] - fn default() -> Self - { - Self - { - vec_1 : None, - hashmap_strings_1 : None, - hashset_strings_1 : None, - } - } - -} - -// - -pub struct Struct1Former -< - Context = Struct1, - End = the_module::ReturnFormed, -> -where - End : the_module::FormingEnd< Struct1, Context >, -{ - storage : Struct1FormerStorage, - context : ::core::option::Option< Context >, - on_end : ::core::option::Option< End >, -} - -impl< Context, End > Struct1Former< Context, End > -where - End : the_module::FormingEnd< Struct1, Context >, -{ - - #[ inline( always ) ] - fn form( mut self ) -> Struct1 - { - - let vec_1 = if self.storage.vec_1.is_some() - { - self.storage.vec_1.take().unwrap() - } - else - { - let val : Vec< String > = Default::default(); - val - }; - - let hashmap_strings_1 = if self.storage.hashmap_strings_1.is_some() - { - self.storage.hashmap_strings_1.take().unwrap() - } - else - { - let val : std::collections::HashMap< String, String > = Default::default(); - val - }; - - let hashset_strings_1 = if self.storage.hashset_strings_1.is_some() - { - self.storage.hashset_strings_1.take().unwrap() - } - else - { - let val : std::collections::HashSet< String > = Default::default(); - val - }; - - Struct1 - { - vec_1, - hashmap_strings_1, - hashset_strings_1, - } - - } - - #[ inline( always ) ] - pub fn perform(self) -> Struct1 - { - let result = self.form(); - return result; - } - - // #[ inline( always ) ] - // pub fn new() -> Struct1Former - // { - // Struct1Former:: - // < - // Struct1, - // the_module::ReturnFormed, - // >::begin(None, the_module::ReturnFormed) - // } - - #[ inline( always ) ] - pub fn begin - ( - mut storage : ::core::option::Option< Struct1FormerStorage >, - context : ::core::option::Option< Context >, - on_end : End, - ) -> Self - { - if storage.is_none() - { - storage = Some( Default::default() ); - } - Self - { - storage : storage.unwrap(), - context, - on_end : ::core::option::Option::Some( on_end ), - } - } - - #[ inline( always ) ] - pub fn end( mut self ) -> Context - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) - } - - #[ inline( always ) ] - pub fn __vec_1< Former2 >( self ) -> - Former2 - where - Former2 : former::FormerBegin - < - Vec< String >, - Vec< String >, - Self, End = former::FormingEndWrapper< Vec< String >, Self >, - >, - { - let on_end = | formed : Vec< String >, super_former : ::core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - if let Some( ref mut field ) = super_former.storage.vec_1 - { - former::ContainerAssign::assign( field, formed ); - } - else - { - super_former.storage.vec_1 = Some( formed ); - } - super_former - }; - Former2::_begin( None, Some( self ), former::FormingEndWrapper::new( on_end ) ) - } - - // xxx2 : continue - pub fn vec_1( self ) -> the_module::VectorSubformer - < - String, - Vec< String >, - Self, - impl the_module::FormingEnd< Vec< String >, Self >, - > - { - self.__vec_1::< the_module::VectorSubformer::< _, _, _, _ > >() - } - - // pub fn vec_1( mut self ) -> the_module::VectorSubformer - // < - // String, - // Vec< String >, - // Self, - // impl the_module::FormingEnd< Vec< String >, Self >, - // > - // { - // let formed = self.storage.vec_1.take(); - // let on_end = | formed : Vec< String >, super_former : ::core::option::Option< Self > | -> Self - // { - // let mut super_former = super_former.unwrap(); - // super_former.storage.vec_1 = Some( formed ); - // super_former - // }; - // the_module::VectorSubformer::< String, Vec< String >, Self, _ >::begin( Some( self ), formed, on_end ) - // } - - pub fn hashmap_strings_1( mut self ) -> the_module::HashMapSubformer - < - String, - String, - std::collections::HashMap< String, String >, - Self, - impl the_module::FormingEnd< std::collections::HashMap< String, String >, Self >, - > - { - let formed = self.storage.hashmap_strings_1.take(); - let on_end = | formed : std::collections::HashMap< String, String >, super_former : ::core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - super_former.storage.hashmap_strings_1 = Some( formed ); - super_former - }; - the_module::HashMapSubformer::begin( formed, Some( self ), on_end ) - } - - pub fn hashset_strings_1( mut self ) -> the_module::HashSetSubformer - < - String, - std::collections::HashSet< String >, - Self, - impl the_module::FormingEnd< std::collections::HashSet< String >, Self >, - > - { - let formed = self.storage.hashset_strings_1.take(); - let on_end = | formed : std::collections::HashSet< String >, super_former : ::core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - super_former.storage.hashset_strings_1 = Some( formed ); - super_former - }; - the_module::HashSetSubformer::begin( formed, Some( self ), on_end ) - } - -} - -// impl< Context, End > Struct1Former< Context, End > -// where -// End: the_module::FormingEnd, - -impl Struct1Former< Struct1, the_module::ReturnFormed > -{ - - #[ inline( always ) ] - pub fn new() -> Self - { - Self::begin( None, None, the_module::ReturnFormed ) - } - -} - -// - -// impl< Context, End > Struct1Former< Context, End > -// where -// End : the_module::FormingEnd< Struct1, Context >, - -impl< Context, End > former::FormerBegin< Struct1FormerStorage, Struct1, Context > -for Struct1Former< Context, End > -where - End : the_module::FormingEnd< Struct1, Context >, -{ - type End = End; - - #[ inline( always ) ] - fn _begin - ( - storage : core::option::Option< Struct1FormerStorage >, /* xxx2 : that should be storage */ - context : core::option::Option< Context >, - on_end : End, - ) -> Self - { - debug_assert!( storage.is_none() ); - Self::begin( None, context, on_end ) - } - -} - -// - -include!( "../only_test/containers_with_runtime.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_containers_without_runtime.rs b/module/core/former/tests/inc/former_tests/a_containers_without_runtime.rs deleted file mode 100644 index 26b9e6ef34..0000000000 --- a/module/core/former/tests/inc/former_tests/a_containers_without_runtime.rs +++ /dev/null @@ -1,18 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -use std::collections::HashMap; -use std::collections::HashSet; - -#[ derive( Debug, PartialEq, the_module::Former ) ] -// #[ debug ] -pub struct Struct1 -{ - vec_1 : Vec< String >, - hashmap_strings_1 : HashMap< String, String >, - hashset_strings_1 : HashSet< String >, -} - -// - -include!( "../only_test/containers_without_runtime.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_containers_without_runtime_manual.rs b/module/core/former/tests/inc/former_tests/a_containers_without_runtime_manual.rs deleted file mode 100644 index 9bea46cff7..0000000000 --- a/module/core/former/tests/inc/former_tests/a_containers_without_runtime_manual.rs +++ /dev/null @@ -1,178 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -#[ derive( Debug, PartialEq ) ] -pub struct Struct1 -{ - vec_1 : Vec< String >, - hashmap_strings_1 : std::collections::HashMap< String, String >, - hashset_strings_1 : std::collections::HashSet< String >, -} - -// - -impl Struct1 -{ - pub fn former() -> Struct1Former< Struct1, the_module::ReturnFormed > - { - Struct1Former::< Struct1, the_module::ReturnFormed >::new() - } -} - -// generated by former -pub struct Struct1FormerStorage -{ - pub vec_1 : core::option::Option< Vec< String > >, - pub hashmap_strings_1 : core::option::Option< std::collections::HashMap< String, String > >, - pub hashset_strings_1 : core::option::Option< std::collections::HashSet< String > >, -} - -impl Default for Struct1FormerStorage -{ - - #[ inline( always ) ] - fn default() -> Self - { - Self - { - vec_1 : None, - hashmap_strings_1 : None, - hashset_strings_1 : None, - } - } - -} - -// - -pub struct Struct1Former -< - __FormerContext = Struct1, - __FormerEnd = the_module::ReturnFormed, -> -where - __FormerEnd : the_module::FormingEnd< Struct1, __FormerContext >, -{ - storage : Struct1FormerStorage, - context : core::option::Option< __FormerContext >, - on_end : core::option::Option< __FormerEnd >, -} - -impl< __FormerContext, __FormerEnd > Struct1Former< __FormerContext, __FormerEnd > -where - __FormerEnd: the_module::FormingEnd, -{ - - #[ inline( always ) ] - fn form( mut self ) -> Struct1 - { - - let vec_1 = if self.storage.vec_1.is_some() - { - self.storage.vec_1.take().unwrap() - } - else - { - let val : Vec< String > = Default::default(); - val - }; - - let hashmap_strings_1 = if self.storage.hashmap_strings_1.is_some() - { - self.storage.hashmap_strings_1.take().unwrap() - } - else - { - let val : std::collections::HashMap< String, String > = Default::default(); - val - }; - - let hashset_strings_1 = if self.storage.hashset_strings_1.is_some() - { - self.storage.hashset_strings_1.take().unwrap() - } - else - { - let val : std::collections::HashSet< String > = Default::default(); - val - }; - - Struct1 - { - vec_1, - hashmap_strings_1, - hashset_strings_1, - } - - } - - #[ inline( always ) ] - pub fn perform(self) -> Struct1 - { - let result = self.form(); - return result; - } - - #[ inline( always ) ] - pub fn new() -> Struct1Former - { - Struct1Former:: - < - Struct1, - the_module::ReturnFormed, - >::begin(None, the_module::ReturnFormed) - } - - #[ inline( always ) ] - pub fn begin - ( - context : core::option::Option< __FormerContext >, - on_end : __FormerEnd, - ) -> Self - { - Self - { - storage : core::default::Default::default(), - context : context, - on_end : ::core::option::Option::Some( on_end ), - } - } - - #[ inline( always ) ] - pub fn end( mut self ) -> __FormerContext - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) - } - - pub fn vec_1< Src >( mut self, src : Src ) -> Self - where Src : core::convert::Into< Vec< String > > - { - debug_assert!( self.storage.vec_1.is_none() ); - self.storage.vec_1 = Some( src.into() ); - self - } - - pub fn hashmap_strings_1< Src >( mut self, src : Src ) -> Self - where Src : core::convert::Into< std::collections::HashMap< String, String > > - { - debug_assert!( self.storage.hashmap_strings_1.is_none() ); - self.storage.hashmap_strings_1 = Some( src.into() ); - self - } - - pub fn hashset_strings_1< Src >( mut self, src : Src ) -> Self - where Src : core::convert::Into< std::collections::HashSet< String > > - { - debug_assert!( self.storage.hashset_strings_1.is_none() ); - self.storage.hashset_strings_1 = Some( src.into() ); - self - } - -} - -// - -include!( "../only_test/containers_without_runtime.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_primitives.rs b/module/core/former/tests/inc/former_tests/a_primitives.rs new file mode 100644 index 0000000000..658420597c --- /dev/null +++ b/module/core/former/tests/inc/former_tests/a_primitives.rs @@ -0,0 +1,21 @@ +#![ deny( missing_docs ) ] + +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, former::Former ) ] +// #[ derive( Debug, PartialEq, former::Former ) ] #[ debug ] +// #[ derive( Debug, PartialEq ) ] #[ debug ] +pub struct Struct1 +{ + pub int_1 : i32, + string_1 : String, + int_optional_1 : core::option::Option< i32 >, + string_optional_1 : Option< String >, +} + +// = begin_coercing of generated + +// == end of generated + +include!( "./only_test/primitives.rs" ); diff --git a/module/core/former/tests/inc/former_tests/a_primitives_manual.rs b/module/core/former/tests/inc/former_tests/a_primitives_manual.rs index 0c5a011178..90e1290d6b 100644 --- a/module/core/former/tests/inc/former_tests/a_primitives_manual.rs +++ b/module/core/former/tests/inc/former_tests/a_primitives_manual.rs @@ -10,18 +10,81 @@ pub struct Struct1 string_optional_1 : Option< String >, } -// +// = formed // generated by former impl Struct1 { - pub fn former() -> Struct1Former< Struct1, the_module::ReturnFormed > + pub fn former() -> Struct1Former { - Struct1Former::< Struct1, the_module::ReturnFormed >::new() + Struct1Former::new_coercing( former::ReturnPreformed ) } } -// +// = definition + +#[ derive( Debug ) ] +pub struct Struct1FormerDefinition< Context = (), Formed = Struct1, End = former::ReturnPreformed > +{ + _phantom : core::marker::PhantomData< ( Context, Formed, End ) >, +} + +impl< Context, Formed, End > Default +for Struct1FormerDefinition< Context, Formed, End > +{ + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } +} + +#[ derive( Debug ) ] +pub struct Struct1FormerDefinitionTypes< Context = (), Formed = Struct1 > +{ + _phantom : core::marker::PhantomData< ( Context, Formed ) >, +} + +impl< Context, Formed > Default +for Struct1FormerDefinitionTypes< Context, Formed > +{ + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } +} + +impl< Context, Formed > former::FormerDefinitionTypes +for Struct1FormerDefinitionTypes< Context, Formed > +{ + type Storage = Struct1FormerStorage; + type Formed = Formed; + type Context = Context; +} + +impl< Context, Formed > former::FormerMutator +for Struct1FormerDefinitionTypes< Context, Formed > +{ +} + +impl< Context, Formed, End > former::FormerDefinition +for Struct1FormerDefinition< Context, Formed, End > +where + End : former::FormingEnd< Struct1FormerDefinitionTypes< Context, Formed > >, +{ + type Types = Struct1FormerDefinitionTypes< Context, Formed >; + type End = End; + type Storage = Struct1FormerStorage; + type Formed = Formed; + type Context = Context; +} + +// = storage // generated by former pub struct Struct1FormerStorage @@ -49,32 +112,24 @@ impl Default for Struct1FormerStorage } -// - -pub struct Struct1Former -< - __FormerContext = Struct1, - __FormerEnd = the_module::ReturnFormed, -> -where - __FormerEnd : the_module::FormingEnd< Struct1, __FormerContext >, +impl former::Storage +for Struct1FormerStorage { - storage : Struct1FormerStorage, - context : core::option::Option< __FormerContext >, - on_end : core::option::Option< __FormerEnd >, + type Preformed = Struct1; } -impl< __FormerContext, __FormerEnd > Struct1Former< __FormerContext, __FormerEnd > -where - __FormerEnd: the_module::FormingEnd, +impl former::StoragePreform +for Struct1FormerStorage { + // type Preformed = Struct1; - fn form( mut self ) -> Struct1 + // fn preform( mut self ) -> < Self as former::Storage >::Formed + fn preform( mut self ) -> Self::Preformed { - let int_1 = if self.storage.int_1.is_some() + let int_1 = if self.int_1.is_some() { - self.storage.int_1.take().unwrap() + self.int_1.take().unwrap() } else { @@ -82,9 +137,9 @@ where val }; - let string_1 = if self.storage.string_1.is_some() + let string_1 = if self.string_1.is_some() { - self.storage.string_1.take().unwrap() + self.string_1.take().unwrap() } else { @@ -92,24 +147,26 @@ where val }; - let int_optional_1 = if self.storage.int_optional_1.is_some() + let int_optional_1 = if self.int_optional_1.is_some() { - Some( self.storage.int_optional_1.take().unwrap() ) + Some( self.int_optional_1.take().unwrap() ) } else { None }; - let string_optional_1 = if self.storage.string_optional_1.is_some() + let string_optional_1 = if self.string_optional_1.is_some() { - Some( self.storage.string_optional_1.take().unwrap() ) + Some( self.string_optional_1.take().unwrap() ) } else { None }; + // Rust failt to use parameter here + // < < Self as former::Storage >::Definition::Types as former::FormerDefinitionTypes >::Formed Struct1 { int_1, @@ -120,52 +177,113 @@ where } +} + +// = former + +pub struct Struct1Former< Definition = Struct1FormerDefinition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, + // Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage >, +{ + storage : Definition::Storage, + context : core::option::Option< Definition::Context >, + on_end : core::option::Option< Definition::End >, +} + +impl< Definition > Struct1Former< Definition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage >, +{ + #[ inline( always ) ] - pub fn perform(self) -> Struct1 + pub fn perform(self) -> < Definition::Types as former::FormerDefinitionTypes >::Formed { let result = self.form(); return result; } + // xxx : update description + #[ inline( always ) ] + pub fn new( on_end : Definition::End ) -> Self + { + Self::begin( None, None, on_end ) + } + #[ inline( always ) ] - pub fn new() -> Struct1Former + pub fn new_coercing< IntoEnd >( end : IntoEnd ) -> Self + where + IntoEnd : Into< Definition::End >, { - Struct1Former:: - < - Struct1, - the_module::ReturnFormed, - >::begin(None, the_module::ReturnFormed) + Self::begin_coercing + ( + None, + None, + end, + ) } #[ inline( always ) ] pub fn begin ( - context : core::option::Option< __FormerContext >, - on_end : __FormerEnd, + mut storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : < Definition as former::FormerDefinition >::End, ) -> Self { + if storage.is_none() + { + storage = Some( core::default::Default::default() ); + } Self { - storage : core::default::Default::default(), - context : context, + storage : storage.unwrap(), + context, on_end : ::core::option::Option::Some( on_end ), } } #[ inline( always ) ] - pub fn end( mut self ) -> __FormerContext + pub fn begin_coercing< IntoEnd > + ( + mut storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : IntoEnd, + ) -> Self + where + IntoEnd : ::core::convert::Into< < Definition as former::FormerDefinition >::End > + { + if storage.is_none() + { + storage = Some( core::default::Default::default() ); + } + Self + { + storage : storage.unwrap(), + context, + on_end : ::core::option::Option::Some( ::core::convert::Into::into( on_end ) ), + } + } + + #[ inline( always ) ] + pub fn end( mut self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed { let on_end = self.on_end.take().unwrap(); let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) + former::FormingEnd::< Definition::Types >::call( &on_end, self.storage, context ) + } + + #[ inline( always ) ] + pub fn form( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + self.end() } pub fn int_1< Src >( mut self, src : Src ) -> Self where Src : core::convert::Into< i32 >, { debug_assert!( self.storage.int_1.is_none() ); - self.storage.int_1 = Some( src.into() ); + self.storage.int_1 = Some( ::core::convert::Into::into( src ) ); self } @@ -173,7 +291,7 @@ where where Src : core::convert::Into< String >, { debug_assert!( self.storage.string_1.is_none() ); - self.storage.string_1 = Some( src.into() ); + self.storage.string_1 = Some( ::core::convert::Into::into( src ) ); self } @@ -181,12 +299,24 @@ where where Src : core::convert::Into< String > { debug_assert!( self.storage.string_optional_1.is_none() ); - self.storage.string_optional_1 = Some( src.into() ); + self.storage.string_optional_1 = Some( ::core::convert::Into::into( src ) ); self } } +impl< Definition > Struct1Former< Definition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage, Formed = Struct1 >, + Definition::Storage : former::StoragePreform, + Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage, Formed = Struct1 >, +{ + pub fn preform( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + former::StoragePreform::preform( self.storage ) + } +} + // -include!( "../only_test/primitives.rs" ); +include!( "./only_test/primitives.rs" ); diff --git a/module/core/former/tests/inc/former_tests/attribute_alias.rs b/module/core/former/tests/inc/former_tests/attribute_alias.rs index 1d5206bf94..a173d57182 100644 --- a/module/core/former/tests/inc/former_tests/attribute_alias.rs +++ b/module/core/former/tests/inc/former_tests/attribute_alias.rs @@ -10,18 +10,25 @@ tests_impls! fn test_alias() { #[ derive( Debug, PartialEq, the_module::Former ) ] + // #[ derive( Debug, PartialEq, the_module::Former ) ] #[ debug ] + // #[ derive( Debug, PartialEq ) ] pub struct AliasTestStruct { - #[ alias( first_field ) ] + #[ scalar( name = first_field ) ] string_field : String, - #[ alias( second_field ) ] + #[ scalar( name = second_field ) ] i32_field : i32, i8_field : i8, } + // == begin of generated + + // == end of generated + let test_struct = AliasTestStruct::former() .first_field( "first_field" ) - .i32_field( 2 ) + .second_field( 2 ) + // .i32_field( 2 ) .i8_field( 1 ) .form(); diff --git a/module/core/former/tests/inc/former_tests/attribute_default_conflict.rs b/module/core/former/tests/inc/former_tests/attribute_default_conflict.rs new file mode 100644 index 0000000000..6a930e1014 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/attribute_default_conflict.rs @@ -0,0 +1,32 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, Default, the_module::Former ) ] +pub struct Struct1 +{ + #[ former( default = 31 ) ] + pub int_1 : i32, +} + +// + +tests_impls! +{ + fn test_complex() + { + let command = Struct1::former().form(); + + let expected = Struct1 + { + int_1 : 31, + }; + a_id!( command, expected ); + } +} + +// + +tests_index! +{ + test_complex, +} diff --git a/module/core/former/tests/inc/former_tests/attribute_default_container.rs b/module/core/former/tests/inc/former_tests/attribute_default_container.rs index fab0ba40cf..a1e1e07132 100644 --- a/module/core/former/tests/inc/former_tests/attribute_default_container.rs +++ b/module/core/former/tests/inc/former_tests/attribute_default_container.rs @@ -8,18 +8,18 @@ use std::collections::HashSet; pub struct Struct1 { - #[ default( vec![ 1, 2, 3 ] ) ] + #[ former( default = vec![ 1, 2, 3 ] ) ] vec_ints : Vec< i32 >, - #[ default( hmap!{ 1 => 11 } ) ] + #[ former( default = hmap!{ 1 => 11 } ) ] hashmap_ints : HashMap< i32, i32 >, - #[ default( hset!{ 11 } ) ] + #[ former( default = hset!{ 11 } ) ] hashset_ints : HashSet< i32 >, - #[ default( vec![ "abc".to_string(), "def".to_string() ] ) ] + #[ former( default = vec![ "abc".to_string(), "def".to_string() ] ) ] vec_strings : Vec< String >, - #[ default( hmap!{ "k1".to_string() => "v1".to_string() } ) ] + #[ former( default = hmap!{ "k1".to_string() => "v1".to_string() } ) ] hashmap_strings : HashMap< String, String >, - #[ default( hset!{ "k1".to_string() } ) ] + #[ former( default = hset!{ "k1".to_string() } ) ] hashset_strings : HashSet< String >, } diff --git a/module/core/former/tests/inc/former_tests/attribute_default_primitive.rs b/module/core/former/tests/inc/former_tests/attribute_default_primitive.rs index e81ac264bf..609915ad5a 100644 --- a/module/core/former/tests/inc/former_tests/attribute_default_primitive.rs +++ b/module/core/former/tests/inc/former_tests/attribute_default_primitive.rs @@ -1,43 +1,24 @@ #[ allow( unused_imports ) ] use super::*; -// #[ allow( unused_imports ) ] -// use test_tools::exposed::*; -// -// only_for_aggregating_module! -// { -// #[ allow( unused_imports ) ] -// use wtools::meta::*; -// #[ allow( unused_imports ) ] -// use wtools::the_module::Former; -// } -// -// only_for_terminal_module! -// { -// #[ allow( unused_imports ) ] -// use meta_tools::*; -// #[ allow( unused_imports ) ] -// use the_module::Former; -// } - use std::collections::HashMap; use std::collections::HashSet; #[ derive( Debug, PartialEq, the_module::Former ) ] pub struct Struct1 { - #[ default( 31 ) ] + #[ former( default = 31 ) ] pub int_1 : i32, - #[ default( "abc" ) ] + #[ former( default = "abc" ) ] string_1 : String, - #[ default( 31 ) ] + #[ former( default = 31 ) ] int_optional_1 : Option< i32 >, - #[ default( "abc" ) ] + #[ former( default = "abc" ) ] string_optional_1 : Option< String >, vec_1 : Vec< String >, - hashmap_strings_1 : HashMap< String, String >, - hashset_strings_1 : HashSet< String >, + hashmap_1 : HashMap< String, String >, + hashset_1 : HashSet< String >, } // @@ -55,8 +36,8 @@ tests_impls! int_optional_1 : Some( 31 ), string_optional_1 : Some( "abc".to_string() ), vec_1 : vec![], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{}, + hashmap_1 : hmap!{}, + hashset_1 : hset!{}, }; a_id!( command, expected ); } diff --git a/module/core/former/tests/inc/former_tests/attribute_feature.rs b/module/core/former/tests/inc/former_tests/attribute_feature.rs new file mode 100644 index 0000000000..20dea37cf8 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/attribute_feature.rs @@ -0,0 +1,43 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq ) ] +pub struct BaseCase +{ + #[ cfg( feature = "enabled" ) ] + enabled : i32, + #[ cfg( feature = "disabled" ) ] + disabled : i32, +} + +#[ derive( Debug, PartialEq, former::Former ) ] +// #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Foo +{ + #[ cfg( feature = "enabled" ) ] + #[ allow( dead_code ) ] + enabled : i32, + #[ cfg( feature = "disabled" ) ] + disabled : i32, +} + +// == begin of generated + +// == end of generated + +#[ test ] +fn basecase() +{ + let got = BaseCase { enabled : 13 }; + let exp = BaseCase { enabled : 13 }; + a_id!( got, exp ); +} + +#[ test ] +fn basic() +{ + let got = Foo::former().enabled( 13 ).form(); + let exp = Foo { enabled : 13 }; + a_id!( got, exp ); +} diff --git a/module/core/former/tests/inc/former_tests/attribute_perform.rs b/module/core/former/tests/inc/former_tests/attribute_perform.rs index b7e645bbfc..2eaaa75fa0 100644 --- a/module/core/former/tests/inc/former_tests/attribute_perform.rs +++ b/module/core/former/tests/inc/former_tests/attribute_perform.rs @@ -7,6 +7,8 @@ pub struct Struct0 pub int_1 : i32, } +// #[ derive( Debug, PartialEq ) ] +// #[ derive( Debug, PartialEq, the_module::Former ) ] #[ debug ] #[ derive( Debug, PartialEq, the_module::Former ) ] #[ perform( fn perform1< 'a >() -> Option< &'a str > ) ] pub struct Struct1 @@ -14,7 +16,9 @@ pub struct Struct1 pub int_1 : i32, } -// +// == begin of generated + +// == end of generated impl Struct1 { diff --git a/module/core/former/tests/inc/former_tests/attribute_setter.rs b/module/core/former/tests/inc/former_tests/attribute_setter.rs index 14e852373e..ee18f78657 100644 --- a/module/core/former/tests/inc/former_tests/attribute_setter.rs +++ b/module/core/former/tests/inc/former_tests/attribute_setter.rs @@ -5,13 +5,13 @@ use super::*; pub struct StructWithCustomSetters { ordinary : String, - #[ setter( false ) ] + #[ scalar( setter = false ) ] magic : String, } -impl< Context, End > StructWithCustomSettersFormer< Context, End > +impl< Definition > StructWithCustomSettersFormer< Definition > where - End: the_module::FormingEnd< StructWithCustomSetters, Context >, + Definition : former::FormerDefinition< Storage = StructWithCustomSettersFormerStorage >, { /// Custom alternative setter of ordinary field. diff --git a/module/core/former/tests/inc/former_tests/attribute_storage_with_end.rs b/module/core/former/tests/inc/former_tests/attribute_storage_with_end.rs new file mode 100644 index 0000000000..57d5f5f7da --- /dev/null +++ b/module/core/former/tests/inc/former_tests/attribute_storage_with_end.rs @@ -0,0 +1,96 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, the_module::Former ) ] +#[ storage_fields( a : i32, b : Option< String > ) ] +// #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Struct1 +{ + c : String, +} + +pub struct Struct1CustomEnd +{ + _phantom : core::marker::PhantomData< ( (), ) >, +} + +// impl< Definition > Default for Struct1CustomEnd< Definition > +impl Default for Struct1CustomEnd +{ + + #[ inline( always ) ] + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } + +} + +#[ automatically_derived ] +impl< Context, > former::FormingEnd +< + Struct1FormerDefinitionTypes< Context, Struct1 > +> +for Struct1CustomEnd +{ + #[ inline( always ) ] + fn call + ( + &self, + storage : Struct1FormerStorage, + super_former : Option< Context >, + ) + -> Struct1 + { + let a = if let Some( a ) = storage.a + { + a + } + else + { + Default::default() + }; + let b = if let Some( b ) = storage.b + { + b + } + else + { + Default::default() + }; + Struct1 { c : format!( "{:?} - {}", a, b ) } + } +} + +// == begin of generated + +// == end of generated + +tests_impls! +{ + + fn test_complex() + { + // let got = Struct1::former().a( 13 ).b( "abc" ).c( "def" ).form(); + let end = Struct1CustomEnd::default(); + let got = Struct1Former + ::< Struct1FormerDefinition< (), Struct1, _ > > + ::new( end ) + .a( 13 ).b( "abc" ).c( "def" ).form(); + let exp = Struct1 + { + c : "13 - abc".to_string(), + }; + a_id!( got, exp ); + } + +} + +tests_index! +{ + test_complex, +} diff --git a/module/core/former/tests/inc/former_tests/attribute_storage_with_mutator.rs b/module/core/former/tests/inc/former_tests/attribute_storage_with_mutator.rs new file mode 100644 index 0000000000..57936294d3 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/attribute_storage_with_mutator.rs @@ -0,0 +1,51 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, the_module::Former ) ] +#[ storage_fields( a : i32, b : Option< String > ) ] +#[ mutator( custom = true ) ] +// #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Struct1 +{ + c : String, +} + +// = former mutator + +impl< Context, Formed > former::FormerMutator +for Struct1FormerDefinitionTypes< Context, Formed > +{ + /// Mutates the context and storage of the entity just before the formation process completes. + #[ inline ] + fn form_mutation( storage : &mut Self::Storage, _context : &mut ::core::option::Option< Self::Context > ) + { + storage.a.get_or_insert_with( Default::default ); + storage.b.get_or_insert_with( Default::default ); + storage.c = Some( format!( "{:?} - {}", storage.a.unwrap(), storage.b.as_ref().unwrap() ) ); + } +} + +// == begin of generated + +// == end of generated + +tests_impls! +{ + + fn test_complex() + { + let got = Struct1::former().a( 13 ).b( "abc" ).c( "def" ).form(); + let exp = Struct1 + { + c : "13 - abc".to_string(), + }; + a_id!( got, exp ); + } + +} + +tests_index! +{ + test_complex, +} diff --git a/module/core/former/tests/inc/compiletime/former_bad_attr.rs b/module/core/former/tests/inc/former_tests/compiletime/field_attr_bad.rs similarity index 100% rename from module/core/former/tests/inc/compiletime/former_bad_attr.rs rename to module/core/former/tests/inc/former_tests/compiletime/field_attr_bad.rs diff --git a/module/core/former/tests/inc/former_tests/compiletime/field_attr_bad.stderr b/module/core/former/tests/inc/former_tests/compiletime/field_attr_bad.stderr new file mode 100644 index 0000000000..8162f72bf2 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/compiletime/field_attr_bad.stderr @@ -0,0 +1,11 @@ +error: Unknown field attribute #[defaultx(31)] + --> tests/inc/former_tests/compiletime/field_attr_bad.rs:6:3 + | +6 | #[ defaultx( 31 ) ] + | ^^^^^^^^^^^^^^^^^^^ + +error: cannot find attribute `defaultx` in this scope + --> tests/inc/former_tests/compiletime/field_attr_bad.rs:6:6 + | +6 | #[ defaultx( 31 ) ] + | ^^^^^^^^ diff --git a/module/core/former/tests/inc/compiletime/former_hashmap_without_parameter.rs b/module/core/former/tests/inc/former_tests/compiletime/hashmap_without_parameter.rs similarity index 100% rename from module/core/former/tests/inc/compiletime/former_hashmap_without_parameter.rs rename to module/core/former/tests/inc/former_tests/compiletime/hashmap_without_parameter.rs diff --git a/module/core/former/tests/inc/former_tests/compiletime/struct_attr_bad.rs b/module/core/former/tests/inc/former_tests/compiletime/struct_attr_bad.rs new file mode 100644 index 0000000000..a08670ab93 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/compiletime/struct_attr_bad.rs @@ -0,0 +1,11 @@ +use former::Former; + +#[ derive( Former ) ] +#[ defaultx ] +pub struct Struct1 +{ + int_1 : i32, +} + +fn main() +{} \ No newline at end of file diff --git a/module/core/former/tests/inc/former_tests/compiletime/struct_attr_bad.stderr b/module/core/former/tests/inc/former_tests/compiletime/struct_attr_bad.stderr new file mode 100644 index 0000000000..7425033f39 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/compiletime/struct_attr_bad.stderr @@ -0,0 +1,12 @@ +error: Known structure attirbutes are : `storage_fields`, `perform`, `debug`. + Unknown structure attribute : #[defaultx] + --> tests/inc/former_tests/compiletime/struct_attr_bad.rs:4:1 + | +4 | #[ defaultx ] + | ^^^^^^^^^^^^^ + +error: cannot find attribute `defaultx` in this scope + --> tests/inc/former_tests/compiletime/struct_attr_bad.rs:4:4 + | +4 | #[ defaultx ] + | ^^^^^^^^ diff --git a/module/core/former/tests/inc/compiletime/former_vector_without_parameter.rs b/module/core/former/tests/inc/former_tests/compiletime/vector_without_parameter.rs similarity index 100% rename from module/core/former/tests/inc/compiletime/former_vector_without_parameter.rs rename to module/core/former/tests/inc/former_tests/compiletime/vector_without_parameter.rs diff --git a/module/core/former/tests/inc/former_tests/container_former_common.rs b/module/core/former/tests/inc/former_tests/container_former_common.rs new file mode 100644 index 0000000000..3bc9c84dfc --- /dev/null +++ b/module/core/former/tests/inc/former_tests/container_former_common.rs @@ -0,0 +1,301 @@ +// #![ allow( dead_code ) ] + +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::Vec; + +// + +#[ test ] +fn definitions() +{ + + pub fn f1< Definition >( _x : Definition ) + where + Definition : former::FormerDefinitionTypes, + { + } + + pub fn f2< Definition >( _x : Definition ) + where + Definition : former::FormerDefinition, + { + } + + pub fn f3< Definition, End >( _x : End ) + where + Definition : former::FormerDefinitionTypes, + End : former::FormingEnd< Definition >, + { + } + + f1( former::VectorDefinitionTypes::< String, (), Vec< String > >::default() ); + f2( former::VectorDefinition::< String, (), Vec< String >, the_module::NoEnd >::default() ); + f3::< former::VectorDefinitionTypes< String, (), Vec< String > >, the_module::ReturnStorage >( the_module::ReturnStorage ); + f3::< < former::VectorDefinition< String, (), Vec< String >, the_module::NoEnd > as the_module::FormerDefinition >::Types, the_module::ReturnStorage >( the_module::ReturnStorage ); + +} + +// + +#[ test ] +fn begin_and_custom_end() +{ + + // basic case + + fn return_13( _storage : Vec< String >, _context : Option< () > ) -> f32 + { + 13.1 + } + let got = the_module::VectorFormer::begin( None, None, return_13 ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13.1; + a_id!( got, exp ); + + let got = the_module::VectorFormer::new( return_13 ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13.1; + a_id!( got, exp ); + + // with a context + + fn context_plus_13( _storage : Vec< String >, context : Option< f32 > ) -> f32 + { + if let Some( context ) = context + { + 13.1 + context + } + else + { + 13.1 + } + } + let got = the_module::VectorFormer::begin( None, Some( 10.0 ), context_plus_13 ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 23.1; + a_id!( got, exp ); + + // + +} + +// + +#[ test ] +fn custom_definition() +{ + + struct Return13; + impl former::FormerDefinitionTypes for Return13 + { + type Storage = Vec< String >; + type Formed = i32; + type Context = (); + } + + impl former::FormerMutator + for Return13 + { + } + + impl former::FormerDefinition for Return13 + { + type Types = Return13; + type End = Return13; + type Storage = Vec< String >; + type Formed = i32; + type Context = (); + } + + // - + + impl former::FormingEnd< Return13 > + for Return13 + { + fn call + ( + &self, + _storage : < Return13 as former::FormerDefinitionTypes >::Storage, + _context : Option< < Return13 as former::FormerDefinitionTypes >::Context > + ) -> < Return13 as former::FormerDefinitionTypes >::Formed + { + 13 + } + } + + // + + let got = former::ContainerFormer::< String, Return13 >::begin( None, None, Return13 ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13; + a_id!( got, exp ); + + let got = former::ContainerFormer::< String, Return13 >::new( Return13 ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13; + a_id!( got, exp ); + + // + +} + +// + +#[ test ] +fn custom_definition_parametrized() +{ + + struct Return13< E >( ::core::marker::PhantomData< E > ); + + impl< E > Return13< E > + { + pub fn new() -> Self + { + Self ( ::core::marker::PhantomData ) + } + } + + impl< E > former::FormerDefinitionTypes for Return13< E > + { + type Storage = Vec< E >; + type Formed = i32; + type Context = (); + } + + impl< E > former::FormerMutator + for Return13< E > + { + } + + impl< E > former::FormerDefinition for Return13< E > + { + type Types = Return13< E >; + type End = Return13< E >; + type Storage = Vec< E >; + type Formed = i32; + type Context = (); + } + + // - + + impl< E > the_module::FormingEnd< Return13< E > > + for Return13< E > + { + fn call + ( + &self, + _storage : < Return13< E > as the_module::FormerDefinitionTypes >::Storage, + _context : Option< < Return13< E > as the_module::FormerDefinitionTypes >::Context > + ) -> < Return13< E > as the_module::FormerDefinitionTypes >::Formed + { + 13 + } + } + + // + + let got = the_module::ContainerFormer::< String, Return13< String > >::begin_coercing( None, None, Return13::new() ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13; + a_id!( got, exp ); + + let got = the_module::ContainerFormer::< String, Return13< String > >::new_coercing( Return13::new() ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13; + a_id!( got, exp ); + + // + + type MyContainer< E > = the_module::ContainerFormer::< E, Return13< E > >; + + let got = MyContainer::< String >::begin_coercing( None, None, Return13::new() ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13; + a_id!( got, exp ); + + let got = MyContainer::< String >::new_coercing( Return13::new() ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13; + a_id!( got, exp ); + + // + +} + +// + +#[ test ] +fn custom_definition_custom_end() +{ + + struct Return13; + impl former::FormerDefinitionTypes for Return13 + { + type Storage = Vec< String >; + type Formed = i32; + type Context = (); + } + impl former::FormerMutator + for Return13 + { + } + impl former::FormerDefinition for Return13 + { + type Types = Return13; + type End = former::FormingEndClosure< < Self as former::FormerDefinition >::Types >; + type Storage = Vec< String >; + type Formed = i32; + type Context = (); + } + + fn return_13( _storage : Vec< String >, _context : Option< () > ) -> i32 + { + 13 + } + + let end_wrapper : the_module::FormingEndClosure< Return13 > = the_module::FormingEndClosure::new( return_13 ); + let got = the_module::ContainerFormer::< String, Return13 >::new( end_wrapper ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13; + a_id!( got, exp ); + + let got = the_module::ContainerFormer::< String, Return13 >::new( return_13.into() ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13; + a_id!( got, exp ); + + let got = the_module::ContainerFormer::< String, Return13 >::new_coercing( return_13 ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = 13; + a_id!( got, exp ); + + // + +} + +// diff --git a/module/core/former/tests/inc/former_tests/container_former_hashmap.rs b/module/core/former/tests/inc/former_tests/container_former_hashmap.rs new file mode 100644 index 0000000000..1b34ebbb3b --- /dev/null +++ b/module/core/former/tests/inc/former_tests/container_former_hashmap.rs @@ -0,0 +1,140 @@ +#![ allow( dead_code ) ] + +#[ allow( unused_imports ) ] +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::HashMap; + +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] +#[ cfg( not( feature = "use_alloc" ) ) ] +#[ test ] +fn add() +{ + + // expliccit with ContainerFormer + + let got : HashMap< String, String > = the_module + ::ContainerFormer + ::< ( String, String ), former::HashMapDefinition< String, String, (), HashMap< String, String >, the_module::ReturnStorage > > + ::new( former::ReturnStorage ) + .add( ( "a".into(), "x".into() ) ) + .add( ( "b".into(), "y".into() ) ) + .form(); + let exp = hmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + + // expliccit with HashMapFormer + + let got : HashMap< String, String > = the_module::HashMapFormer::< String, String, (), HashMap< String, String >, the_module::ReturnStorage > + ::new( former::ReturnStorage ) + .add( ( "a".into(), "x".into() ) ) + .add( ( "b".into(), "y".into() ) ) + .form(); + let exp = hmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + + // compact with HashMapFormer + + let got : HashMap< String, String > = the_module::HashMapFormer::new( former::ReturnStorage ) + .add( ( "a".into(), "x".into() ) ) + .add( ( "b".into(), "y".into() ) ) + .form(); + let exp = hmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + + // with begin + + let got : HashMap< String, String > = the_module::HashMapFormer + ::begin( Some( hmap![ "a".to_string() => "x".to_string() ] ), Some( () ), former::ReturnStorage ) + .add( ( "b".into(), "y".into() ) ) + .form(); + let exp = hmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + + // with help of ext + + use the_module::HashMapExt; + let got : HashMap< String, String > = HashMap::former() + .add( ( "a".into(), "x".into() ) ) + .add( ( "b".into(), "y".into() ) ) + .form(); + let exp = hmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + + // + +} + +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] +#[ cfg( not( feature = "use_alloc" ) ) ] +#[ test ] +fn replace() +{ + + let got : HashMap< String, String > = the_module::HashMapFormer::new( former::ReturnStorage ) + .add( ( "x".to_string(), "y".to_string() ) ) + .replace( hmap![ "a".to_string() => "x".to_string(), "b".to_string() => "y".to_string(), ] ) + .form(); + let exp = hmap! + [ + "a".to_string() => "x".to_string(), + "b".to_string() => "y".to_string(), + ]; + a_id!( got, exp ); + +} + +#[ test ] +fn entry_to_val() +{ + let got = former::EntryToVal::< HashMap< u32, i32 > >::entry_to_val( ( 1u32, 13i32 ) ); + let exp = 13i32; + a_id!( got, exp ) +} + +#[ test ] +fn val_to_entry() +{ + + #[ derive( Clone, Copy, Debug, PartialEq ) ] + struct Val + { + key : u32, + data : i32, + } + + impl former::ValToEntry< HashMap< u32, Val > > for Val + { + type Entry = ( u32, Val ); + #[ inline( always ) ] + fn val_to_entry( self ) -> Self::Entry + { + ( self.key, self ) + } + } + + let got = former::ValToEntry::< HashMap< u32, Val > >::val_to_entry( Val { key : 1u32, data : 13i32 } ); + let exp = ( 1u32, Val { key : 1u32, data : 13i32 } ); + a_id!( got, exp ) + +} diff --git a/module/core/former/tests/inc/former_tests/container_former_hashset.rs b/module/core/former/tests/inc/former_tests/container_former_hashset.rs new file mode 100644 index 0000000000..83b8e7a994 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/container_former_hashset.rs @@ -0,0 +1,121 @@ +#![ allow( dead_code ) ] + +#[ allow( unused_imports ) ] +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::HashSet; + +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] +#[ cfg( not( feature = "use_alloc" ) ) ] +#[ test ] +fn add() +{ + + // expliccit with ContainerFormer + + let got : HashSet< String > = the_module + ::ContainerFormer + ::< String, former::HashSetDefinition< String, (), HashSet< String >, the_module::ReturnStorage > > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = hset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // expliccit with HashSetFormer + + let got : HashSet< String > = the_module::HashSetFormer::< String, (), HashSet< String >, the_module::ReturnStorage > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = hset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // compact with HashSetFormer + + let got : HashSet< String > = the_module::HashSetFormer::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = hset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with begin_coercing + + let got : HashSet< String > = the_module::HashSetFormer + ::begin( Some( hset![ "a".to_string() ] ), Some( () ), former::ReturnStorage ) + .add( "b" ) + .form(); + let exp = hset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with help of ext + + use the_module::HashSetExt; + let got : HashSet< String > = HashSet::former() + .add( "a" ) + .add( "b" ) + .form(); + let exp = hset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // + +} + +// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] +#[ cfg( not( feature = "use_alloc" ) ) ] +#[ test ] +fn replace() +{ + + let got : HashSet< String > = the_module::HashSetFormer::new( former::ReturnStorage ) + .add( "x" ) + .replace( hset![ "a".to_string(), "b".to_string() ] ) + .form(); + let exp = hset! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + +} + +#[ test ] +fn entry_to_val() +{ + let got = former::EntryToVal::< HashSet< i32 > >::entry_to_val( 13i32 ); + let exp = 13i32; + a_id!( got, exp ) +} + +#[ test ] +fn val_to_entry() +{ + let got = former::ValToEntry::< HashSet< i32 > >::val_to_entry( 13i32 ); + let exp = 13i32; + a_id!( got, exp ) +} diff --git a/module/core/former/tests/inc/former_tests/container_former_vec.rs b/module/core/former/tests/inc/former_tests/container_former_vec.rs new file mode 100644 index 0000000000..ec76210448 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/container_former_vec.rs @@ -0,0 +1,158 @@ +// #![ allow( dead_code ) ] + +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::Vec; + +// + +#[ test ] +fn add() +{ + + // expliccit with ContainerFormer + + let got : Vec< String > = the_module + ::ContainerFormer + ::< String, former::VectorDefinition< String, (), Vec< String >, the_module::ReturnStorage > > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = vec! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // expliccit with VectorFormer + + let got : Vec< String > = the_module::VectorFormer::< String, (), Vec< String >, the_module::ReturnStorage > + ::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = vec! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // compact with VectorFormer + + let got : Vec< String > = the_module::VectorFormer::new( former::ReturnStorage ) + .add( "a" ) + .add( "b" ) + .form(); + let exp = vec! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with begin_coercing + + let got : Vec< String > = the_module::VectorFormer + ::begin( Some( vec![ "a".to_string() ] ), Some( () ), former::ReturnStorage ) + .add( "b" ) + .form(); + let exp = vec! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // with help of ext + + use the_module::VecExt; + let got : Vec< String > = Vec::former() + .add( "a" ) + .add( "b" ) + .form(); + let exp = vec! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + + // + +} + +// + +#[ test ] +fn replace() +{ + + let got : Vec< String > = the_module::VectorFormer::new( former::ReturnStorage ) + .add( "x" ) + .replace( vec![ "a".to_string(), "b".to_string() ] ) + .form(); + let exp = vec! + [ + "a".to_string(), + "b".to_string(), + ]; + a_id!( got, exp ); + +} + +// + +// qqq : make similar test for all containers +#[ test ] +fn entity_to() +{ + + // qqq : uncomment and make it working + // let got = < Vec< i32 > as former::EntityToFormer< former::VectorDefinition< i32, (), Vec< i32 >, former::ReturnPreformed > > > + // ::Former::new( former::ReturnPreformed ) + // .add( 13 ) + // .form(); + // let exp = vec![ 13 ]; + // a_id!( got, exp ); + +// qqq : uncomment and make it working +// let got = < Vec< i32 > as former::EntityToStorage >::Storage::default(); +// let exp = +// < +// Vec< i32 > as former::EntityToFormer +// < +// Vec< i32 >FormerDefinition< (), Vec< i32 >, former::ReturnPreformed > +// > +// >::Former::new( former::ReturnPreformed ); +// a_id!( got.int_1, exp.storage.int_1 ); +// +// let got = < Vec< i32 > as former::EntityToStorage >::Storage::default(); +// let exp = +// < +// Vec< i32 > as former::EntityToFormer +// < +// < Vec< i32 > as former::EntityToDefinition< (), Vec< i32 >, former::ReturnPreformed > >::Definition +// > +// >::Former::new( former::ReturnPreformed ); +// a_id!( got.int_1, exp.storage.int_1 ); + +} + +#[ test ] +fn entry_to_val() +{ + let got = former::EntryToVal::< Vec< i32 > >::entry_to_val( 13i32 ); + let exp = 13i32; + a_id!( got, exp ) +} + +#[ test ] +fn val_to_entry() +{ + let got = former::ValToEntry::< Vec< i32 > >::val_to_entry( 13i32 ); + let exp = 13i32; + a_id!( got, exp ) +} diff --git a/module/core/former/tests/inc/former_tests/name_collision_context.rs b/module/core/former/tests/inc/former_tests/name_collision_context.rs index e9f7024853..8123626a1d 100644 --- a/module/core/former/tests/inc/former_tests/name_collision_context.rs +++ b/module/core/former/tests/inc/former_tests/name_collision_context.rs @@ -5,6 +5,7 @@ use super::*; pub trait CloneAny{} pub trait End{} +pub trait Formed{} pub trait OnEnd{} #[ derive( Clone, the_module::Former ) ] diff --git a/module/core/former/tests/inc/former_tests/name_collision_end.rs b/module/core/former/tests/inc/former_tests/name_collision_end.rs index f0200c1c34..99f736019d 100644 --- a/module/core/former/tests/inc/former_tests/name_collision_end.rs +++ b/module/core/former/tests/inc/former_tests/name_collision_end.rs @@ -5,10 +5,17 @@ use super::*; pub trait CloneAny{} pub trait Context{} +pub trait Formed{} pub trait OnEnd{} #[ derive( Clone, the_module::Former ) ] +// #[ derive( Clone, the_module::Former ) ] #[ debug ] +// #[ derive( Clone ) ] pub struct End { inner : std::sync::Arc< core::cell::RefCell< dyn CloneAny > > } + +// = begin_coercing of generated + +// == end of generated \ No newline at end of file diff --git a/module/core/former/tests/inc/former_tests/name_collisions.rs b/module/core/former/tests/inc/former_tests/name_collisions.rs index 5aef9ca9f1..843af61803 100644 --- a/module/core/former/tests/inc/former_tests/name_collisions.rs +++ b/module/core/former/tests/inc/former_tests/name_collisions.rs @@ -28,10 +28,10 @@ type HashMap = (); pub struct Struct1 { vec_1 : Vec< String >, - hashmap_strings_1 : std::collections::HashMap< String, String >, - hashset_strings_1 : std::collections::HashSet< String >, + hashmap_1 : std::collections::HashMap< String, String >, + hashset_1 : std::collections::HashSet< String >, } // -include!( "../only_test/containers_without_runtime.rs" ); +include!( "./only_test/containers_without_subformer.rs" ); diff --git a/module/core/former/tests/inc/former_tests/only_test/basic.rs b/module/core/former/tests/inc/former_tests/only_test/basic.rs new file mode 100644 index 0000000000..a4b4dbf907 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/basic.rs @@ -0,0 +1,562 @@ +#[ allow( unused_imports ) ] +use super::*; + +// + +tests_impls! +{ + + // + + fn internals() + { + + let former = Struct1::former(); + a_id!( former.storage.int_1, None ); + a_id!( former.context, None ); + a_id!( print!( "{:?}", former.on_end ), print!( "{:?}", Some( the_module::ReturnPreformed ) ) ); + let former2 = Struct1Former::< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > >::new_coercing( former::ReturnPreformed ); + a_id!( std::mem::size_of_val( &former ), std::mem::size_of_val( &former2 ) ); + let former2 = Struct1Former::< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > >::new( former::ReturnPreformed ); + a_id!( std::mem::size_of_val( &former ), std::mem::size_of_val( &former2 ) ); + + let command = Struct1::former().form(); + a_id!( command.int_1, 0 ); + + let command = Struct1::former().perform(); + a_id!( command.int_1, 0 ); + + let command = Struct1::former().end(); + a_id!( command.int_1, 0 ); + + } + + // + + fn entity_to() + { + + let got = < Struct1 as former::EntityToFormer< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > > >::Former::new( former::ReturnPreformed ) + .int_1( 13 ) + .form(); + let exp = Struct1 { int_1 : 13 }; + a_id!( got, exp ); + + let got = < Struct1 as former::EntityToStorage >::Storage::default(); + let exp = + < + Struct1 as former::EntityToFormer + < + Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > + > + >::Former::new( former::ReturnPreformed ); + a_id!( got.int_1, exp.storage.int_1 ); + + let got = < Struct1 as former::EntityToStorage >::Storage::default(); + let exp = + < + Struct1 as former::EntityToFormer + < + < Struct1 as former::EntityToDefinition< (), Struct1, former::ReturnPreformed > >::Definition + > + >::Former::new( former::ReturnPreformed ); + a_id!( got.int_1, exp.storage.int_1 ); + + } + + // + + fn former_begin() + { + + let former = < Struct1Former as former::FormerBegin< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > > > + ::former_begin( None, None, former::ReturnPreformed ); + let got = former + .int_1( 13 ) + .form(); + let exp = Struct1 { int_1 : 13 }; + a_id!( got, exp ); + + } + + // + + fn custom_definition_params() + { + + // custom params + let got = Struct1Former + :: + < + Struct1FormerDefinition< i32, i32, former::FormingEndClosure< Struct1FormerDefinitionTypes< i32, i32 > > > + > + ::begin_coercing + ( + None, + Some( 3 ), + | storage : Struct1FormerStorage, context : Option< i32 > | { 2 * ( storage.int_1.unwrap() + context.unwrap() ) }, + ) + .int_1( 13 ) + .form(); + a_id!( got, 32 ); + + // custom params with into + let got = Struct1Former + :: + < + Struct1FormerDefinition< i32, i32, former::FormingEndClosure< Struct1FormerDefinitionTypes< i32, i32 > > > + > + ::begin_coercing + ( + None, + Some( 3 ), + | storage : Struct1FormerStorage, context : Option< i32 > | { 2 * ( storage.int_1.unwrap() + context.unwrap() ) }, + ) + .int_1( 13 ) + .form(); + a_id!( got, 32 ); + + // custom params begin_coercing + let got = Struct1Former + :: + < + + Struct1FormerDefinition< i32, i32, former::FormingEndClosure< Struct1FormerDefinitionTypes< i32, i32 > > > + > + ::begin_coercing + ( + None, + Some( 3 ), + | storage : Struct1FormerStorage, context : Option< i32 > | { 2 * ( storage.int_1.unwrap() + context.unwrap() ) } + ) + .int_1( 13 ) + .form(); + a_id!( got, 32 ); + + // custom params begin_coercing with Struct1FormerWithClosure + let got = Struct1Former + :: + < + + Struct1FormerDefinition< i32, i32, former::FormingEndClosure< Struct1FormerDefinitionTypes< i32, i32 > > > + > + ::begin_coercing + ( + None, + Some( 3 ), + | storage : Struct1FormerStorage, context : Option< i32 > | { 2 * ( storage.int_1.unwrap() + context.unwrap() ) } + ) + .int_1( 13 ) + .form(); + a_id!( got, 32 ); + + // less explicit + let got = Struct1Former + :: + < + + Struct1FormerDefinition< i32, i32, former::FormingEndClosure< _ > > + > + ::begin_coercing + ( + None, + Some( 3 ), + | storage : Struct1FormerStorage, context : Option< i32 > | { 2 * ( storage.int_1.unwrap() + context.unwrap() ) } + ) + .int_1( 13 ) + .form(); + a_id!( got, 32 ); + + } + + // + + fn begin_coercing() + { + + // begin_coercing with none + let got = Struct1Former::< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > >::begin_coercing( None, None, the_module::ReturnPreformed ).int_1( 13 ).form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + + // begin_coercing with storage + let mut storage = Struct1FormerStorage::default(); + storage.int_1 = Some( 13 ); + let exp = Struct1Former::< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > >::begin_coercing( Some( storage ), None, the_module::ReturnPreformed ).form(); + a_id!( got, exp ); + + // begin_coercing with context + let mut storage = Struct1FormerStorage::default(); + storage.int_1 = Some( 13 ); + let exp = Struct1Former + :: + < + Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > + > + ::begin_coercing( Some( storage ), Some( () ), the_module::ReturnPreformed ) + .form(); + a_id!( got, exp ); + + } + + // + + fn begin() + { + + // custom params + let got = Struct1Former + // ::< Struct1FormerDefinition< i32, i32, _ > > + :: + < + + Struct1FormerDefinition< i32, i32, _ > + > + ::begin + ( + None, + Some( 3 ), + former::FormingEndClosure::new + ( + | storage : Struct1FormerStorage, context | { 2 * ( storage.int_1.unwrap() + context.unwrap() ) } + ), + ) + .int_1( 13 ) + .form(); + a_id!( got, 32 ); + + // custom params with into + let got = Struct1Former + // ::< Struct1FormerDefinition< i32, i32, former::FormingEndClosure< Struct1FormerDefinitionTypes< i32, i32 > > > > + :: + < + + Struct1FormerDefinition< i32, i32, former::FormingEndClosure< Struct1FormerDefinitionTypes< i32, i32 > > > + > + ::begin + ( + None, + Some( 3 ), + ( + | storage : Struct1FormerStorage, context : Option< i32 > | { 2 * ( storage.int_1.unwrap() + context.unwrap() ) } + ).into(), + ) + .int_1( 13 ) + .form(); + a_id!( got, 32 ); + + } + + // + + fn new_coercing() + { + + // basic case + let former = Struct1::former(); + let former2 = Struct1Former::< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > >::new( former::ReturnPreformed ); + a_id!( std::mem::size_of_val( &former ), std::mem::size_of_val( &former2 ) ); + let exp = former.form(); + let got = former2.form(); + a_id!( got, exp ); + + // default explicit params + let got = Struct1Former + ::< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > > + ::new_coercing( former::ReturnPreformed ) + .int_1( 13 ) + .form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + + // default explicit params with wrapper + fn f1( storage : Struct1FormerStorage, _context : Option< () > ) -> Struct1 + { + former::StoragePreform::preform( storage ) + } + let end_wrapper : former::FormingEndClosure< Struct1FormerDefinitionTypes< (), Struct1 > > = former::FormingEndClosure::new( f1 ); + let got = Struct1Former + // ::< Struct1FormerDefinition< (), Struct1, former::FormingEndClosure< Struct1FormerDefinitionTypes< (), Struct1 > > > > + :: + < + Struct1FormerDefinition< (), Struct1, former::FormingEndClosure< Struct1FormerDefinitionTypes< (), Struct1 > > > + > + ::new_coercing( end_wrapper ) + .int_1( 13 ) + .form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + + // default explicit params with wrapper and closure + let got = Struct1Former + // ::< Struct1FormerDefinition< (), Struct1, former::FormingEndClosure< Struct1FormerDefinitionTypes< (), Struct1 > > > > + :: + < + + Struct1FormerDefinition< (), Struct1, former::FormingEndClosure< Struct1FormerDefinitionTypes< (), Struct1 > > > + > + ::new_coercing( former::FormingEndClosure::new( | storage, _context | { former::StoragePreform::preform( storage ) } ) ) + .int_1( 13 ) + .form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + + // default explicit params with wrapper and closure, auto types + let got = Struct1Former + // ::< Struct1FormerDefinition< _, _, former::FormingEndClosure< Struct1FormerDefinitionTypes< (), Struct1 > > > > + :: + < + Struct1FormerDefinition< (), Struct1, former::FormingEndClosure< Struct1FormerDefinitionTypes< (), Struct1 > > > + > + ::new_coercing( former::FormingEndClosure::new( | storage, _context : Option< () > | { former::StoragePreform::preform( storage ) } ) ) + .int_1( 13 ) + .form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + + } + + // + + fn new() + { + + // basic case + let former = Struct1::former(); + let former2 = Struct1Former::< Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > >::new( former::ReturnPreformed ); + a_id!( std::mem::size_of_val( &former ), std::mem::size_of_val( &former2 ) ); + let exp = former.form(); + let got = former2.form(); + a_id!( got, exp ); + + // default explicit params + let got = Struct1Former + // ::< Struct1FormerDefinition< (), Struct1, _ > > + :: + < + + Struct1FormerDefinition< (), Struct1, _ >, + > + ::new( former::ReturnPreformed ) + .int_1( 13 ) + .form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + + // default explicit params with wrapper + fn f1( storage : Struct1FormerStorage, _context : Option< () > ) -> Struct1 + { + former::StoragePreform::preform( storage ) + } + let end_wrapper : former::FormingEndClosure< Struct1FormerDefinitionTypes< (), Struct1 > > = former::FormingEndClosure::new( f1 ); + let got = Struct1Former + // ::< Struct1FormerDefinition< (), Struct1, _ > > + :: + < + Struct1FormerDefinition< (), Struct1, _ >, + > + ::new( end_wrapper ) + .int_1( 13 ) + .form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + + // + + // default explicit params with wrapper and closure + let got = Struct1Former + // ::< Struct1FormerWithClosure< (), Struct1 > > + :: + < + Struct1FormerDefinition< (), Struct1, _ > + > + ::new( | storage, _context | { former::StoragePreform::preform( storage ) } ) + .int_1( 13 ) + .form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + + // + + // default explicit params with wrapper and closure + let got = Struct1Former + // ::< Struct1FormerDefinition< (), Struct1, _ > > + :: + < + Struct1FormerDefinition< (), Struct1, _ >, + > + ::new( former::FormingEndClosure::new( | storage, _context | { former::StoragePreform::preform( storage ) } ) ) + .int_1( 13 ) + .form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + + // default explicit params with wrapper and closure, auto types + let got = Struct1Former + // ::< Struct1FormerDefinition< _, _, _ > > + :: + < + Struct1FormerDefinition< _, _, _ >, + > + ::new( former::FormingEndClosure::new( | storage, _context : Option< () > | { former::StoragePreform::preform( storage ) } ) ) + .int_1( 13 ) + .form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + + } + + // + + fn preform() + { + + // formation should have method preform + let got = Struct1::former().preform(); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + // storage should have method preform + let got = the_module::StoragePreform::preform( Struct1::former().storage ); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + // storage should have method preform + use the_module::StoragePreform; + let got = Struct1::former().storage.preform(); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + } + + // + + fn definition() + { + + // default is implemented for definition + let _default = Struct1FormerDefinition::< (), Struct1, former::ReturnPreformed >::default(); + // let _default = Struct1FormerDefinition::default(); // why does not work? + + // definition types exists and has Formed + let got = < Struct1FormerDefinitionTypes< (), Struct1 > as the_module::FormerDefinitionTypes >::Formed::former().form(); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + // definition types exists and has Formed + let got = < Struct1FormerDefinitionTypes< (), Struct1 > as the_module::FormerDefinitionTypes >::Formed::former().form(); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + // definition types exists and has Storage + use former::StoragePreform; + let got = < Struct1FormerDefinitionTypes< (), Struct1 > as the_module::FormerDefinitionTypes >::Storage + ::preform( Struct1::former().storage ); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + // definition exists and has Storage + let got = < < Struct1FormerDefinition< (), Struct1, former::ReturnPreformed > as the_module::FormerDefinition >::Types as the_module::FormerDefinitionTypes >::Formed + ::former().form(); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + } + + // + + fn storage() + { + + // definition exists and has Storage + let got = < Struct1FormerStorage as the_module::StoragePreform >::preform( Struct1::former().storage ); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + // default is implemented for Storage + let got = Struct1FormerStorage::default().preform(); + let exp = Struct1::former().storage.preform(); + a_id!( got, exp ); + + // definition exists and has Storage + use former::StoragePreform; + let got = Struct1::former().storage.preform(); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + // storage exists + let got = < Struct1FormerStorage as the_module::Storage >::Preformed::former().form(); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + } + + // + + fn test_int() + { + + // test.case( "basic" ); + + let command = Struct1::former() + .int_1( 13 ) + .form(); + // dbg!( &command ); + + let expected = Struct1 + { + int_1 : 13, + }; + a_id!( command, expected ); + + // test.case( "rewriting" ); + + // should_throw( || + // { + // let _command = Struct1::former() + // .int_1( 1 ) + // .int_1( 3 ) + // .form(); + // Ok( () ) + // })?; + } + + // + + fn test_underscored_form() + { + // test.case( "basic" ); + let command = Struct1::former() + .int_1( 13 ) + .form(); + + let expected = Struct1 + { + int_1 : 13, + }; + a_id!( command, expected ); + } + + // + + +} + +// + +tests_index! +{ + internals, + entity_to, + former_begin, + custom_definition_params, + begin_coercing, + begin, + new_coercing, + new, + preform, + definition, + storage, + test_int, + test_underscored_form, +} diff --git a/module/core/former/tests/inc/former_tests/only_test/containers_with_subformer.rs b/module/core/former/tests/inc/former_tests/only_test/containers_with_subformer.rs new file mode 100644 index 0000000000..042d36e538 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/containers_with_subformer.rs @@ -0,0 +1,365 @@ +#[ allow( unused_imports ) ] +use super::*; + +// + +tests_impls! +{ + + // + + fn internals() + { + + // test.case( "vector : construction" ); + + // fields + let former = Struct1::former(); + a_id!( former.storage.vec_1, None ); + a_id!( former.storage.hashmap_1, None ); + a_id!( former.storage.hashset_1, None ); + a_id!( former.context, None ); + + // form + let got = Struct1::former().form(); + let exp = Struct1::default(); + a_id!( got, exp ); + + // preform + let got = Struct1::former().preform(); + let exp = Struct1::default(); + a_id!( got, exp ); + + // perform + let got = Struct1::former().perform(); + let exp = Struct1::default(); + a_id!( got, exp ); + + // end + let got = Struct1::former().end(); + let exp = Struct1::default(); + a_id!( got, exp ); + + } + + // + + fn new() + { + + // former with explicit definition + let former = Struct1::former(); + a_id!( print!( "{:?}", former.on_end ), print!( "{:?}", Some( the_module::ReturnPreformed ) ) ); + let former2 = Struct1Former::< Struct1FormerDefinition >::new_coercing( former::ReturnPreformed ); + a_id!( std::mem::size_of_val( &former ), std::mem::size_of_val( &former2 ) ); + + // default parameters + let former = Struct1::former(); + let former2 : Struct1Former = Struct1Former::new_coercing( former::ReturnPreformed ); + a_id!( std::mem::size_of_val( &former ), std::mem::size_of_val( &former2 ) ); + + // closure without helper + let got : Struct1 = Struct1Former + ::< Struct1FormerDefinition< _, _, former::FormingEndClosure< Struct1FormerDefinitionTypes< (), Struct1 > > > > + ::new_coercing( | storage : Struct1FormerStorage, _context | { former::StoragePreform::preform( storage ) } ) + .vec_1().replace( vec![ "a".to_string(), "b".to_string() ] ).end() + .form(); + let exp : Struct1 = Struct1 + { + vec_1 : vec![ "a".to_string(), "b".to_string() ], + hashmap_1 : hmap!{}, + hashset_1 : hset!{}, + }; + a_id!( got, exp ); + + // closure with helper + let got : Struct1 = Struct1Former + ::< Struct1FormerDefinition< (), Struct1, _ > > + ::new( | storage, _context | { former::StoragePreform::preform( storage ) } ) + .vec_1().replace( vec![ "a".to_string(), "b".to_string() ] ).end() + .form(); + let exp : Struct1 = Struct1 + { + vec_1 : vec![ "a".to_string(), "b".to_string() ], + hashmap_1 : hmap!{}, + hashset_1 : hset!{}, + }; + a_id!( got, exp ); + + // // closure with helper + // let got : Struct1 = Struct1Former + // ::< Struct1FormerWithClosure< (), Struct1 > > + // ::new_coercing( | storage : Struct1FormerStorage, _context | { former::StoragePreform::preform( storage ) } ) + // .vec_1().replace( vec![ "a".to_string(), "b".to_string() ] ).end() + // .form(); + // let exp : Struct1 = Struct1 + // { + // vec_1 : vec![ "a".to_string(), "b".to_string() ], + // hashmap_1 : hmap!{}, + // hashset_1 : hset!{}, + // }; + // a_id!( got, exp ); + + // closure with helper + let got : Struct1 = Struct1Former + ::< Struct1FormerDefinition< (), Struct1, _ > > + ::begin( None, None, | storage, _context | { former::StoragePreform::preform( storage ) } ) + .vec_1().replace( vec![ "a".to_string(), "b".to_string() ] ).end() + .form(); + let exp : Struct1 = Struct1 + { + vec_1 : vec![ "a".to_string(), "b".to_string() ], + hashmap_1 : hmap!{}, + hashset_1 : hset!{}, + }; + a_id!( got, exp ); + + } + + // + + fn field_forming_end() + { + + // Container subformers are defined + let _got = Struct1FormerAssignVec1End::< Struct1FormerDefinition >::default(); + let _got = Struct1FormerAssignHashmap1End::< Struct1FormerDefinition >::default(); + let _got = Struct1FormerAssignHashset1End::< Struct1FormerDefinition >::default(); + + // AsSubformerEnd is defined + fn _f1< End : Struct1AsSubformerEnd< Struct1Former > > + ( + _end : End, + _subformer : Struct1AsSubformer< Struct1Former, impl Struct1AsSubformerEnd< Struct1Former > > + ) + { + } + + } + + // + + fn test_vector() + { + + // test.case( "vector : implicit construction" ); + + let command = Struct1::former() + .vec_1().add( "ghi" ).add( "klm" ).end() + .form() + ; + // dbg!( &command ); + + let expected = Struct1 + { + vec_1 : vec![ "ghi".to_string(), "klm".to_string() ], + hashmap_1 : hmap!{}, + hashset_1 : hset!{}, + }; + a_id!( command, expected ); + + // test.case( "vector : replace" ); + + let command = Struct1::former() + .vec_1().replace( vec![ "a".to_string(), "bc".to_string(), "def".to_string() ] ).end() + .form(); + let expected = Struct1 + { + vec_1 : vec![ "a".to_string(), "bc".to_string(), "def".to_string() ], + hashmap_1 : hmap!{}, + hashset_1 : hset!{}, + }; + a_id!( command, expected ); + + let command = Struct1::former() + .vec_1().add( "x" ).replace( vec![ "a".to_string(), "bc".to_string(), "def".to_string() ] ).end() + .form(); + let expected = Struct1 + { + vec_1 : vec![ "a".to_string(), "bc".to_string(), "def".to_string() ], + hashmap_1 : hmap!{}, + hashset_1 : hset!{}, + }; + a_id!( command, expected ); + + // test.case( "vector : replace and add" ); + + let command = Struct1::former() + .vec_1().replace( vec![ "a".to_string(), "bc".to_string(), "def".to_string() ] ).add( "gh" ).end() + .form(); + // dbg!( &command ); + + let expected = Struct1 + { + vec_1 : vec![ "a".to_string(), "bc".to_string(), "def".to_string(), "gh".to_string() ], + hashmap_1 : hmap!{}, + hashset_1 : hset!{}, + }; + a_id!( command, expected ); + } + + // + + fn test_hashmap() + { + + // test.case( "implicit construction" ); + + let command = Struct1::former() + .hashmap_1().add( ( "k1".to_string(), "v1".to_string() ) ).add( ( "k2".to_string(), "v2".to_string() ) ).end() + .form() + ; + // dbg!( &command ); + + let expected = Struct1 + { + vec_1 : vec![], + hashmap_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : hset!{}, + }; + a_id!( command, expected ); + + // test.case( "replace" ); + + let command = Struct1::former() + .hashmap_1().replace( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ).end() + .form() + ; + let expected = Struct1 + { + vec_1 : vec![], + hashmap_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : hset!{}, + }; + a_id!( command, expected ); + + let command = Struct1::former() + .hashmap_1().add( ( "x".to_string(), "v1".to_string() ) ).replace( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ).end() + .form() + ; + let expected = Struct1 + { + vec_1 : vec![], + hashmap_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : hset!{}, + }; + a_id!( command, expected ); + + // test.case( "replace and add" ); + + let command = Struct1::former() + .hashmap_1().replace( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) + .add( ( "k3".to_string(), "v3".to_string() ) ).end() + .form() + ; + // dbg!( &command ); + + let expected = Struct1 + { + vec_1 : vec![], + hashmap_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string(), "k3".to_string() => "v3".to_string() }, + hashset_1 : hset!{}, + }; + a_id!( command, expected ); + } + + // + + fn test_hashset() + { + + // test.case( "implicit construction" ); + + let command = Struct1::former() + .hashset_1().add( "v1" ).add( "v2" ).end() + .form() + ; + // dbg!( &command ); + + let expected = Struct1 + { + vec_1 : vec![], + hashmap_1 : hmap!{}, + hashset_1 : hset!{ "v1".to_string(), "v2".to_string() }, + }; + a_id!( command, expected ); + + // test.case( "replace" ); + + let command = Struct1::former() + .hashset_1().replace( hset!{ "v1".to_string(), "v2".to_string() } ).end() + .form() + ; + let expected = Struct1 + { + vec_1 : vec![], + hashmap_1 : hmap!{}, + hashset_1 : hset!{ "v1".to_string(), "v2".to_string() }, + }; + a_id!( command, expected ); + + let command = Struct1::former() + .hashset_1().add( "x" ).replace( hset!{ "v1".to_string(), "v2".to_string() } ).end() + .form() + ; + let expected = Struct1 + { + vec_1 : vec![], + hashmap_1 : hmap!{}, + hashset_1 : hset!{ "v1".to_string(), "v2".to_string() }, + }; + a_id!( command, expected ); + + // test.case( "replace and add" ); + + let command = Struct1::former() + .hashset_1().replace( hset!{ "v1".to_string(), "v2".to_string() } ).add( "v3" ).end() + .form() + ; + // dbg!( &command ); + + let expected = Struct1 + { + vec_1 : vec![], + hashmap_1 : hmap!{}, + hashset_1 : hset!{ "v1".to_string(), "v2".to_string(), "v3".to_string() }, + }; + a_id!( command, expected ); + } + + // + + fn test_complex() + { + + let command = Struct1::former() + .vec_1().add( "ghi" ).add( "klm" ).end() + .hashmap_1().add( ( "k1".to_string(), "v1".to_string() ) ).add( ( "k2".to_string(), "v2".to_string() ) ).end() + .hashset_1().add( "k1" ).end() + .form(); + // dbg!( &command ); + + let expected = Struct1 + { + vec_1 : vec![ "ghi".to_string(), "klm".to_string() ], + hashmap_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : hset!{ "k1".to_string() }, + }; + a_id!( command, expected ); + + } + +} + +// + +tests_index! +{ + internals, + new, + field_forming_end, + test_vector, + test_hashmap, + test_hashset, + test_complex, +} diff --git a/module/core/former/tests/inc/only_test/containers_without_runtime.rs b/module/core/former/tests/inc/former_tests/only_test/containers_without_subformer.rs similarity index 63% rename from module/core/former/tests/inc/only_test/containers_without_runtime.rs rename to module/core/former/tests/inc/former_tests/only_test/containers_without_subformer.rs index 8141ddc9fd..c19e92eead 100644 --- a/module/core/former/tests/inc/only_test/containers_without_runtime.rs +++ b/module/core/former/tests/inc/former_tests/only_test/containers_without_subformer.rs @@ -15,27 +15,27 @@ tests_impls! let former = Struct1::former(); a_id!( former.storage.vec_1, None ); - a_id!( former.storage.hashmap_strings_1, None ); - a_id!( former.storage.hashset_strings_1, None ); + a_id!( former.storage.hashmap_1, None ); + a_id!( former.storage.hashset_1, None ); a_id!( former.context, None ); - a_id!( print!( "{:?}", former.on_end ), print!( "{:?}", Some( the_module::ReturnFormed ) ) ); - let former2 = Struct1Former::< Struct1, the_module::ReturnFormed >::new(); + a_id!( print!( "{:?}", former.on_end ), print!( "{:?}", Some( the_module::ReturnPreformed ) ) ); + let former2 = Struct1Former::< Struct1FormerDefinition >::new_coercing( the_module::ReturnPreformed ); a_id!( std::mem::size_of_val( &former ), std::mem::size_of_val( &former2 ) ); let command = Struct1::former().form(); a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_strings_1, hmap!{} ); - a_id!( command.hashset_strings_1, hset![] ); + a_id!( command.hashmap_1, hmap!{} ); + a_id!( command.hashset_1, hset![] ); let command = Struct1::former().perform(); a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_strings_1, hmap!{} ); - a_id!( command.hashset_strings_1, hset![] ); + a_id!( command.hashmap_1, hmap!{} ); + a_id!( command.hashset_1, hset![] ); let command = Struct1::former().end(); a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_strings_1, hmap!{} ); - a_id!( command.hashset_strings_1, hset![] ); + a_id!( command.hashmap_1, hmap!{} ); + a_id!( command.hashset_1, hset![] ); } @@ -55,8 +55,8 @@ tests_impls! let expected = Struct1 { vec_1 : vec![ "ghi".to_string(), "klm".to_string() ], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{}, + hashmap_1 : hmap!{}, + hashset_1 : hset!{}, }; a_id!( command, expected ); } @@ -69,7 +69,7 @@ tests_impls! // test.case( "construction" ); let command = Struct1::former() - .hashmap_strings_1( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) + .hashmap_1( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) .form() ; // dbg!( &command ); @@ -77,8 +77,8 @@ tests_impls! let expected = Struct1 { vec_1 : vec![], - hashmap_strings_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, - hashset_strings_1 : hset!{}, + hashmap_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : hset!{}, }; a_id!( command, expected ); } @@ -90,7 +90,7 @@ tests_impls! // test.case( "construction" ); let command = Struct1::former() - .hashset_strings_1( hset!{ "v1".to_string(), "v2".to_string() } ) + .hashset_1( hset!{ "v1".to_string(), "v2".to_string() } ) .form() ; // dbg!( &command ); @@ -98,8 +98,8 @@ tests_impls! let expected = Struct1 { vec_1 : vec![], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{ "v1".to_string(), "v2".to_string() }, + hashmap_1 : hmap!{}, + hashset_1 : hset!{ "v1".to_string(), "v2".to_string() }, }; a_id!( command, expected ); } @@ -115,8 +115,8 @@ tests_impls! let expected = Struct1 { vec_1 : vec![], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{}, + hashmap_1 : hmap!{}, + hashset_1 : hset!{}, }; a_id!( command, expected ); } @@ -127,15 +127,15 @@ tests_impls! { let command = Struct1::former() .vec_1( vec![ "ghi".to_string(), "klm".to_string() ] ) - .hashmap_strings_1( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) + .hashmap_1( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) .form(); // dbg!( &command ); let expected = Struct1 { vec_1 : vec![ "ghi".to_string(), "klm".to_string() ], - hashmap_strings_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, - hashset_strings_1 : hset!{}, + hashmap_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : hset!{}, }; a_id!( command, expected ); @@ -150,10 +150,12 @@ tests_impls! tests_index! { + internals, test_vector, test_hashmap, test_hashset, test_underscored_form, test_complex, + } diff --git a/module/core/former/tests/inc/former_tests/only_test/parametrized_field.rs b/module/core/former/tests/inc/former_tests/only_test/parametrized_field.rs new file mode 100644 index 0000000000..7449ec7129 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/parametrized_field.rs @@ -0,0 +1,8 @@ + +#[ test ] +fn basic() +{ + let got = Child::< 'static, str >::former().name( "abc" ).arg( "arg1" ).end(); + let exp = Child::< 'static, str >{ name : "abc".into(), arg : "arg1" }; + a_id!( got, exp ); +} diff --git a/module/core/former/tests/inc/only_test/parametrized_struct.rs b/module/core/former/tests/inc/former_tests/only_test/parametrized_struct.rs similarity index 67% rename from module/core/former/tests/inc/only_test/parametrized_struct.rs rename to module/core/former/tests/inc/former_tests/only_test/parametrized_struct.rs index 01c03c9800..620e42198b 100644 --- a/module/core/former/tests/inc/only_test/parametrized_struct.rs +++ b/module/core/former/tests/inc/former_tests/only_test/parametrized_struct.rs @@ -3,10 +3,10 @@ fn command_form() { // form - let got = Command::< &str >::former() + let got = Child::< &str >::former() .name( "a" ) .form(); - let exp = Command::< &str > + let exp = Child::< &str > { name : "a".to_string(), properties : collection_tools::HashMap::< &str, Property< &str > >::new(), @@ -14,10 +14,10 @@ fn command_form() a_id!( got, exp ); // perform - let got = Command::< &str >::former() + let got = Child::< &str >::former() .name( "a" ) .perform(); - let exp = Command::< &str > + let exp = Child::< &str > { name : "a".to_string(), properties : collection_tools::HashMap::< &str, Property< &str > >::new(), @@ -25,10 +25,10 @@ fn command_form() a_id!( got, exp ); // end - let got = Command::< &str >::former() + let got = Child::< &str >::former() .name( "a" ) .end(); - let exp = Command::< &str > + let exp = Child::< &str > { name : "a".to_string(), properties : collection_tools::HashMap::< &str, Property< &str > >::new(), @@ -45,16 +45,16 @@ fn command_form() fn command_properties() { - // with HashMapSubformer - let got = Command::< &str >::former() + // with HashMapFormer + let got = Child::< &str >::former() .name( "a" ) .properties() - .insert( "property1", Property::< &str >::new( "property1", 13isize ) ) - .insert( "property2", Property::new( "property2", 13isize ) ) - .insert( "property2", Property::new( "property2", 113isize ) ) + .add( ( "property1", Property::< &str >::new( "property1", 13isize ) ) ) + .add( ( "property2", Property::new( "property2", 13isize ) ) ) + .add( ( "property2", Property::new( "property2", 113isize ) ) ) .end() .form(); - let exp = Command::< &str > + let exp = Child::< &str > { name : "a".to_string(), properties : hmap! diff --git a/module/core/former/tests/inc/only_test/primitives.rs b/module/core/former/tests/inc/former_tests/only_test/primitives.rs similarity index 84% rename from module/core/former/tests/inc/only_test/primitives.rs rename to module/core/former/tests/inc/former_tests/only_test/primitives.rs index 728105b6ad..c38fea9bf8 100644 --- a/module/core/former/tests/inc/only_test/primitives.rs +++ b/module/core/former/tests/inc/former_tests/only_test/primitives.rs @@ -8,45 +8,48 @@ tests_impls! // - fn internals() + fn api() { - // // test.case( "vector : construction" ); - // int_1, - // string_1, - // int_optional_1, - // string_optional_1, - - let former = Struct1::former(); - a_id!( former.storage.int_1, None ); - a_id!( former.storage.string_1, None ); - a_id!( former.storage.int_optional_1, None ); - a_id!( former.storage.string_optional_1, None ); - a_id!( former.context, None ); - a_id!( print!( "{:?}", former.on_end ), print!( "{:?}", Some( the_module::ReturnFormed ) ) ); - let former2 = Struct1Former::< Struct1, the_module::ReturnFormed >::new(); - a_id!( std::mem::size_of_val( &former ), std::mem::size_of_val( &former2 ) ); - + // form let command = Struct1::former().form(); a_id!( command.int_1, 0 ); a_id!( command.string_1, "".to_string() ); a_id!( command.int_optional_1, None ); a_id!( command.string_optional_1, None ); - let command = Struct1::former().perform(); + // end + let command = Struct1::former().end(); a_id!( command.int_1, 0 ); a_id!( command.string_1, "".to_string() ); a_id!( command.int_optional_1, None ); a_id!( command.string_optional_1, None ); - let command = Struct1::former().end(); + // perform + let command = Struct1::former().perform(); a_id!( command.int_1, 0 ); a_id!( command.string_1, "".to_string() ); a_id!( command.int_optional_1, None ); a_id!( command.string_optional_1, None ); + // formation should have method preform + let got = Struct1::former().preform(); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + // default explicit params with wrapper and closure + let got = Struct1Former + ::< Struct1FormerDefinition< (), Struct1, _ > > + ::new( | storage, _context | { former::StoragePreform::preform( storage ) } ) + .int_1( 13 ) + .form(); + let exp = Struct1::former().int_1( 13 ).form(); + a_id!( got, exp ); + } + // + fn test_int() { @@ -202,7 +205,7 @@ tests_impls! .int_1( 13 ) .string_1( "Abcd".to_string() ) // .vec_1().push( "ghi" ).push( "klm" ).end() - // .hashmap_strings_1().insert( "k1", "v1" ).insert( "k2", "v2" ).end() + // .hashmap_1().insert( "k1", "v1" ).insert( "k2", "v2" ).end() .string_optional_1( "dir1" ) .form(); // dbg!( &command ); @@ -228,7 +231,8 @@ tests_impls! tests_index! { - internals, + api, + test_int, test_string, test_optional_string, diff --git a/module/core/former/tests/inc/former_tests/only_test/string_slice.rs b/module/core/former/tests/inc/former_tests/only_test/string_slice.rs new file mode 100644 index 0000000000..2ed7eb90c5 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/string_slice.rs @@ -0,0 +1,101 @@ +#[ allow( unused_imports ) ] +use super::*; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +// + +tests_impls! +{ + + + // + + fn api() + { + + // form + let command = Struct1::former().form(); + a_id!( command.string_slice_1, "" ); + + // end + let command = Struct1::former().end(); + a_id!( command.string_slice_1, "" ); + + // perform + let command = Struct1::former().perform(); + a_id!( command.string_slice_1, "" ); + + // formation should have method preform + let got = Struct1::former().preform(); + let exp = Struct1::former().form(); + a_id!( got, exp ); + + // default explicit params with wrapper and closure + let got = Struct1Former + ::< Struct1FormerDefinition< (), Struct1, _ > > + ::new( | storage, _context | { former::StoragePreform::preform( storage ) } ) + .string_slice_1( "abc" ) + .form(); + let exp = Struct1::former().string_slice_1( "abc" ).form(); + a_id!( got, exp ); + + // closure with helper + let got : Struct1 = Struct1Former + ::< Struct1FormerDefinition< (), Struct1, _ > > + ::begin( None, None, | storage, _context | { former::StoragePreform::preform( storage ) } ) + .string_slice_1( "abc" ) + .form(); + let exp = Struct1::former().string_slice_1( "abc" ).form(); + a_id!( got, exp ); + + } + + // + + fn test_complex() + { + // test.case( "default" ); + + let command = Struct1::former().form(); + let expected = Struct1 + { + string_slice_1 : "", + }; + a_id!( command, expected ); + + // test.case( "from slice" ); + + let command = Struct1::former() + .string_slice_1( "abc" ) + .form(); + let expected = Struct1 + { + string_slice_1 : "abc", + }; + a_id!( command, expected ); + +// // test.case( "from string" ); +// +// let command = Struct1::former() +// .string_slice_1( "abc".to_string() ) +// .form(); +// let expected = Struct1 +// { +// string_slice_1 : "abc", +// }; +// a_id!( command, expected ); + + } + + // + +} + +// + +tests_index! +{ + api, + test_complex, +} diff --git a/module/core/former/tests/inc/only_test/subformer_basic.rs b/module/core/former/tests/inc/former_tests/only_test/subformer_basic.rs similarity index 53% rename from module/core/former/tests/inc/only_test/subformer_basic.rs rename to module/core/former/tests/inc/former_tests/only_test/subformer_basic.rs index 6d8c236e49..ea039d9835 100644 --- a/module/core/former/tests/inc/only_test/subformer_basic.rs +++ b/module/core/former/tests/inc/former_tests/only_test/subformer_basic.rs @@ -1,11 +1,11 @@ -// let ca = wca::CommandsAggregator::former() -// .command( "echo" ) +// let ca = wca::ChildsParent::former() +// .command_with_closure( "echo" ) // .name( "prints all subjects and properties" ) // .subject( "Subject", wca::Type::String, true ) // .property( "property", "simple property", wca::Type::String, true ) // .routine( f1 ) // .perform() -// .command( "exit" ) +// .command_with_closure( "exit" ) // .name( "just exit" ) // .routine( || exit() ) // .perform() @@ -13,17 +13,17 @@ // ; // ca.execute( input ).unwrap(); -// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] +// qqq : for Antont : zzz : here and in all similar tests remove `#[ cfg( not( feature = "use_alloc" ) ) ]` #[ cfg( not( feature = "use_alloc" ) ) ] #[ test ] -fn command() +fn command_with_closure() { - let got = Command::< &str >::former() + let got = Child::< &str >::former() .name( "a" ) .subject( "b" ) .form(); - let exp = Command::< &str > + let exp = Child::< &str > { name : "a".to_string(), subject : "b".to_string(), @@ -31,11 +31,11 @@ fn command() }; a_id!( got, exp ); - let got = Command::< &str >::former() + let got = Child::< &str >::former() .name( "a" ) .subject( "b" ) .perform(); - let exp = Command::< &str > + let exp = Child::< &str > { name : "a".to_string(), subject : "b".to_string(), @@ -43,11 +43,11 @@ fn command() }; a_id!( got, exp ); - let got = Command::< &str >::former() + let got = Child::< &str >::former() .name( "a" ) .subject( "b" ) .end(); - let exp = Command::< &str > + let exp = Child::< &str > { name : "a".to_string(), subject : "b".to_string(), @@ -66,14 +66,14 @@ fn command_properties() { // with helper - let got = Command::< &str >::former() + let got = Child::< &str >::former() .name( "a" ) .subject( "b" ) .property( "property1", "simple property", 13isize ) .property( "property2", "simple property 2", 13isize ) .property( "property2", "simple property 3", 113isize ) .form(); - let exp = Command::< &str > + let exp = Child::< &str > { name : "a".to_string(), subject : "b".to_string(), @@ -86,17 +86,17 @@ fn command_properties() }; a_id!( got, exp ); - // with HashMapSubformer - let got = Command::< &str >::former() + // with HashMapFormer + let got = Child::< &str >::former() .name( "a" ) .subject( "b" ) .properties() - .insert( "property1", Property::new( "property1", "simple property", 13isize ) ) - .insert( "property2", Property::new( "property2", "simple property 2", 13isize ) ) - .insert( "property2", Property::new( "property2", "simple property 3", 113isize ) ) + .add( ( "property1", Property::new( "property1", "simple property", 13isize ) ) ) + .add( ( "property2", Property::new( "property2", "simple property 2", 13isize ) ) ) + .add( ( "property2", Property::new( "property2", "simple property 3", 113isize ) ) ) .end() .form(); - let exp = Command::< &str > + let exp = Child::< &str > { name : "a".to_string(), subject : "b".to_string(), @@ -120,50 +120,22 @@ fn aggregator() { // with helper - let got = Aggregator::< &str >::former() + let got = Parent::< &str >::former() .parameter1( "p1" ) - .commands().insert( "name1", CommandFormer::< &str >::new().name( "name1" ).subject( "s" ).end() ).end() - .command( "command1".to_string() ) - .subject( "b" ) - .property( "property1", "simple property", 13isize ) - .property( "property2", "simple property 3", 113isize ) - .end() - .command( "command2".to_string() ) - .subject( "c" ) - .property( "property3", "x", 113isize ) - .end() + .commands().add( ( "name1".to_string(), ChildFormer::< &str >::new_coercing( former::ReturnPreformed ).name( "name1" ).subject( "s" ).end() ) ).end() .form() ; - let name1 = Command::< &str > + let name1 = Child::< &str > { name : "name1".to_string(), subject : "s".to_string(), properties : hmap!{}, }; - let command1 = Command::< &str > - { - name : "command1".to_string(), - subject : "b".to_string(), - properties : hmap! - { - "property1" => Property::new( "property1", "simple property", 13isize ), - "property2" => Property::new( "property2", "simple property 3", 113isize ), - }, - }; - let command2 = Command::< &str > - { - name : "command2".to_string(), - subject : "c".to_string(), - properties : hmap! - { - "property3" => Property::new( "property3", "x", 113isize ), - }, - }; - let exp = Aggregator + let exp = Parent { parameter1 : "p1".to_string(), - commands : hmap!{ "name1" => name1, "command1" => command1, "command2" => command2 }, + commands : hmap!{ "name1" => name1 }, }; dbg!( &got ); dbg!( &exp ); @@ -177,31 +149,19 @@ fn aggregator() fn aggregator_alternative_form() { - let exp = Aggregator::< &str >::former() + let exp = Parent::< &str >::former() .parameter1( "p1" ) - .command( "command1".to_string() ) - .subject( "b" ) - .property( "property2", "simple property 3", 113isize ) - .end() .form() ; - let got = Aggregator::< &str >::former() + let got = Parent::< &str >::former() .parameter1( "p1" ) - .command( "command1".to_string() ) - .subject( "b" ) - .property( "property2", "simple property 3", 113isize ) - .end() .perform() ; a_id!( got, exp ); - let got = Aggregator::< &str >::former() + let got = Parent::< &str >::former() .parameter1( "p1" ) - .command( "command1".to_string() ) - .subject( "b" ) - .property( "property2", "simple property 3", 113isize ) - .end() .end() ; a_id!( got, exp ); diff --git a/module/core/former/tests/inc/former_tests/only_test/subformer_container.rs b/module/core/former/tests/inc/former_tests/only_test/subformer_container.rs new file mode 100644 index 0000000000..ce7f0f08dd --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/subformer_container.rs @@ -0,0 +1,21 @@ + +#[ test ] +fn basic() +{ + + let got = Parent::former() + .children() + .add( Child::former().name( "a" ).form() ) + .add( Child::former().name( "b" ).form() ) + .end() + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), data : false }, + Child { name : "b".to_string(), data : false }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} diff --git a/module/core/former/tests/inc/former_tests/only_test/subformer_container_children2.rs b/module/core/former/tests/inc/former_tests/only_test/subformer_container_children2.rs new file mode 100644 index 0000000000..46fae25331 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/subformer_container_children2.rs @@ -0,0 +1,21 @@ + +#[ test ] +fn container() +{ + + 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(), data : false }, + Child { name : "b".to_string(), data : false }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} diff --git a/module/core/former/tests/inc/former_tests/only_test/subformer_scalar_children.rs b/module/core/former/tests/inc/former_tests/only_test/subformer_scalar_children.rs new file mode 100644 index 0000000000..76fe8fa988 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/subformer_scalar_children.rs @@ -0,0 +1,23 @@ + +#[ test ] +fn scalar() +{ + + let children = vec! + [ + Child { name : "a".to_string(), data : false }, + Child { name : "b".to_string(), data : false }, + ]; + let got = Parent::former() + .children( children ) + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), data : false }, + Child { name : "b".to_string(), data : false }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} diff --git a/module/core/former/tests/inc/former_tests/only_test/subformer_scalar_children3.rs b/module/core/former/tests/inc/former_tests/only_test/subformer_scalar_children3.rs new file mode 100644 index 0000000000..ba2c97b459 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/subformer_scalar_children3.rs @@ -0,0 +1,23 @@ + +#[ test ] +fn scalar() +{ + + let children = vec! + [ + Child { name : "a".to_string(), data : false }, + Child { name : "b".to_string(), data : false }, + ]; + let got = Parent::former() + .children3( children ) + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), data : false }, + Child { name : "b".to_string(), data : false }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} diff --git a/module/core/former/tests/inc/former_tests/only_test/subformer_subform_child.rs b/module/core/former/tests/inc/former_tests/only_test/subformer_subform_child.rs new file mode 100644 index 0000000000..f4167ee982 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/subformer_subform_child.rs @@ -0,0 +1,38 @@ + +#[ test ] +fn child() +{ + + let got = Parent::former() + .child( "a" ).end() + .child( "b" ).end() + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), data : false }, + Child { name : "b".to_string(), data : false }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} + +#[ test ] +fn _child() +{ + + let got = Parent::former() + ._child().name( "a" ).end() + ._child().name( "b" ).end() + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), data : false }, + Child { name : "b".to_string(), data : false }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} diff --git a/module/core/former/tests/inc/former_tests/only_test/subformer_subform_children2.rs b/module/core/former/tests/inc/former_tests/only_test/subformer_subform_children2.rs new file mode 100644 index 0000000000..c869113680 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/only_test/subformer_subform_children2.rs @@ -0,0 +1,19 @@ + +#[ test ] +fn subform() +{ + + let got = Parent::former() + .children2( "a" ).end() + .children2( "b" ).end() + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), data : false }, + Child { name : "b".to_string(), data : false }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} diff --git a/module/core/former/tests/inc/former_tests/parametrized_field.rs b/module/core/former/tests/inc/former_tests/parametrized_field.rs new file mode 100644 index 0000000000..fce1a22818 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/parametrized_field.rs @@ -0,0 +1,20 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; + +/// Parameter description. +#[ allow( explicit_outlives_requirements ) ] +#[ derive( Debug, PartialEq, the_module::Former ) ] +// #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Child< 'child, T : ?Sized + 'child > +{ + name : String, + arg : &'child T, +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/parametrized_field.rs" ); diff --git a/module/core/former/tests/inc/former_tests/parametrized_field_where.rs b/module/core/former/tests/inc/former_tests/parametrized_field_where.rs new file mode 100644 index 0000000000..baaaed538f --- /dev/null +++ b/module/core/former/tests/inc/former_tests/parametrized_field_where.rs @@ -0,0 +1,22 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; + +/// Parameter description. +#[ allow( explicit_outlives_requirements ) ] +#[ derive( Debug, PartialEq, the_module::Former ) ] +// #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Child< 'child, T > +where + T : ?Sized + 'child, +{ + name : String, + arg : &'child T, +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/parametrized_field.rs" ); diff --git a/module/core/former/tests/inc/former_tests/parametrized_struct_imm.rs b/module/core/former/tests/inc/former_tests/parametrized_struct_imm.rs index 2212f4c08b..d216575504 100644 --- a/module/core/former/tests/inc/former_tests/parametrized_struct_imm.rs +++ b/module/core/former/tests/inc/former_tests/parametrized_struct_imm.rs @@ -23,13 +23,17 @@ impl< Name > Property< Name > } #[ derive( Debug, PartialEq, the_module::Former ) ] -pub struct Command< K : core::hash::Hash + std::cmp::Eq > +// #[ derive( Debug, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Child< K : core::hash::Hash + std::cmp::Eq > { pub name : String, - #[ subformer( the_module::HashMapSubformer ) ] + #[ container( definition = former::HashMapDefinition ) ] pub properties : collection_tools::HashMap< K, Property< K > >, } -// == +// == begin_coercing of generated -include!( "../only_test/parametrized_struct.rs" ); +// == end of generated + +include!( "./only_test/parametrized_struct.rs" ); diff --git a/module/core/former/tests/inc/former_tests/parametrized_struct_manual.rs b/module/core/former/tests/inc/former_tests/parametrized_struct_manual.rs index ae57ca7e08..95e081b39e 100644 --- a/module/core/former/tests/inc/former_tests/parametrized_struct_manual.rs +++ b/module/core/former/tests/inc/former_tests/parametrized_struct_manual.rs @@ -22,182 +22,350 @@ impl< Name > Property< Name > } } +// #[ derive( Debug, PartialEq, the_module::Former ) ] +// #[ derive( Debug, PartialEq, the_module::Former ) ] #[ debug ] #[ derive( Debug, PartialEq ) ] -pub struct Command< K > +pub struct Child< K > where K : core::hash::Hash + std::cmp::Eq, { pub name : String, + // #[ container( definition = former::HashMapDefinition ) ] pub properties : collection_tools::HashMap< K, Property< K > >, } -// generated by former -impl< K > Command< K > -where - K : core::hash::Hash + std::cmp::Eq, +// == begin_coercing of generated + +#[ automatically_derived ] +impl< K, > Child< K, > where K : core :: hash :: Hash + std :: cmp :: Eq, { + + #[ inline( always ) ] - pub fn former() -> CommandFormer< K > + pub fn former() -> ChildFormer< K, ChildFormerDefinition< K, (), Child< K, >, former :: ReturnPreformed > > { - CommandFormer::< K >::new() + ChildFormer + :: + < K, ChildFormerDefinition< K, (), Child< K, >, former :: ReturnPreformed > > + :: new( former :: ReturnPreformed ) } +} +#[ derive( Debug ) ] +pub struct ChildFormerDefinitionTypes< K, __Context = (), __Formed = Child< K, >, > +where K : core :: hash :: Hash + std :: cmp :: Eq, +{ + _phantom : core :: marker :: PhantomData< ( K, __Context, __Formed ) >, } -// generated by former -pub struct CommandFormerStorage< K > +impl< K, __Context, __Formed, > :: core :: default :: Default +for ChildFormerDefinitionTypes< K, __Context, __Formed, > where - K : core::hash::Hash + std::cmp::Eq, + K : core :: hash :: Hash + std :: cmp :: Eq, +{ + fn default() -> Self + { + Self + { + _phantom : core :: marker :: PhantomData, + } + } +} + +impl< K, __Context, __Formed, > former :: FormerDefinitionTypes +for ChildFormerDefinitionTypes< K, __Context, __Formed, > +where + K : core :: hash :: Hash + std :: cmp :: Eq, { - name : core::option::Option< String >, - properties : core::option::Option< collection_tools::HashMap< K, Property< K > > >, + type Storage = ChildFormerStorage< K, >; + type Formed = __Formed; + type Context = __Context; } -impl< K > Default for CommandFormerStorage< K > +impl< K, Context, Formed > former::FormerMutator +for ChildFormerDefinitionTypes< K, Context, Formed > where - K : core::hash::Hash + std::cmp::Eq, + K : core :: hash :: Hash + std :: cmp :: Eq, { +} - #[ inline( always ) ] +#[ derive( Debug ) ] +pub struct ChildFormerDefinition +< K, __Context = (), __Formed = Child< K, >, __End = former :: ReturnPreformed, > +where + K : core :: hash :: Hash + std :: cmp :: Eq, +{ + _phantom : core :: marker :: PhantomData< ( K, __Context, __Formed, __End ) >, +} + +impl< K, __Context, __Formed, __End, > :: core :: default :: Default +for ChildFormerDefinition< K, __Context, __Formed, __End, > +where + K : core :: hash :: Hash + std :: cmp :: Eq, +{ fn default() -> Self { Self { - name : None, - properties : None, + _phantom : core :: marker :: PhantomData, } } - } -// generated by former -// #[ derive( Debug, Default ) ] -pub struct CommandFormer< K, Context = Command< K >, End = the_module::ReturnFormed > +impl< K, __Context, __Formed, __End, > former :: FormerDefinition +for ChildFormerDefinition< K, __Context, __Formed, __End, > where - K : core::hash::Hash + std::cmp::Eq, - End : the_module::FormingEnd< Command< K >, Context >, + __End : former :: FormingEnd< ChildFormerDefinitionTypes< K, __Context, __Formed, > >, + K : core :: hash :: Hash + std :: cmp :: Eq, { - storage : CommandFormerStorage< K >, - context : core::option::Option< Context >, - on_end : core::option::Option< End >, + type Types = ChildFormerDefinitionTypes< K, __Context, __Formed, >; + type End = __End; + type Storage = ChildFormerStorage< K, >; + type Formed = __Formed; + type Context = __Context; } -// generated by former -impl< K, Context, End > -CommandFormer< K, Context, End > -where - K : core::hash::Hash + std::cmp::Eq, - End : the_module::FormingEnd< Command< K >, Context >, +// pub type ChildFormerWithClosure< K, __Context, __Formed, > = ChildFormerDefinition< K, __Context, __Formed, former :: FormingEndClosure< ChildFormerDefinitionTypes< K, __Context, __Formed, > > >; + +pub struct ChildFormerStorage< K, > where K : core :: hash :: Hash + std :: cmp :: Eq, { + pub name : :: core :: option :: Option< String >, + + pub properties : :: core :: option :: Option< collection_tools :: HashMap< K, Property< K > > >, +} + +impl< K, > :: core :: default :: Default for ChildFormerStorage< K, > where K : core :: hash :: Hash + std :: cmp :: Eq, +{ #[ inline( always ) ] - fn form( mut self ) -> Command< K > + fn default() -> Self { + Self + { + name : :: core :: option :: Option :: None, + properties : :: core :: option :: Option :: None, + } + } +} - let name = if self.storage.name.is_some() +impl< K, > former :: Storage for ChildFormerStorage< K, > where K : core :: hash :: Hash + std :: cmp :: Eq, +{ + type Preformed = Child< K, >; +} + +impl< K, > former :: StoragePreform for ChildFormerStorage< K, > where K : core :: hash :: Hash + std :: cmp :: Eq, +{ + // type Preformed = Child< K, >; + + fn preform( mut self ) -> Self::Preformed + // fn preform( mut self ) -> < Self as former :: Storage > :: Formed + { + let name = if self.name.is_some() { - self.storage.name.take().unwrap() + self.name.take().unwrap() } else { - let val = Default::default(); - val + { + trait MaybeDefault< T > + { + fn maybe_default( self : & Self ) -> T + { + panic!( "Field 'name' isn't initialized" ) + } + } + impl< T > MaybeDefault< T > for & :: core :: marker :: PhantomData< T > {} + impl< T > MaybeDefault< T > for :: core :: marker :: PhantomData< T > where T : :: core :: default :: Default, + { + fn maybe_default( self : & Self ) -> T { T :: default() } + } + ( & :: core :: marker :: PhantomData :: < String > ).maybe_default() + } }; - let properties = if self.storage.properties.is_some() + let properties = if self.properties.is_some() { - self.storage.properties.take().unwrap() + self.properties.take().unwrap() } else { - let val = Default::default(); - val + { + trait MaybeDefault< T > + { + fn maybe_default( self : & Self ) -> T + { + panic!( "Field 'properties' isn't initialized" ) + } + } + impl< T > MaybeDefault< T > for & :: core :: marker :: PhantomData< T > {} + impl< T > MaybeDefault< T > for :: core :: marker :: PhantomData< T > where T : :: core :: default :: Default, + { + fn maybe_default( self : & Self ) -> T { T :: default() } + } + ( & :: core :: marker :: PhantomData :: < collection_tools :: HashMap< K, Property< K > > > ).maybe_default() + } }; - Command - { - name, - properties, - } + let result = Child :: < K, > { name, properties, }; + return result; + } +} + +pub struct ChildFormer< K, Definition = ChildFormerDefinition< K, (), Child< K, >, former::ReturnPreformed >, > +where + K : core::hash::Hash + std::cmp::Eq, + Definition : former::FormerDefinition< Storage = ChildFormerStorage< K, > >, + // Definition::Types : former::FormerDefinitionTypes< Storage = ChildFormerStorage< K, > > +{ + storage : Definition::Storage, + context : core::option::Option< Definition::Context >, + on_end : core::option::Option< Definition::End >, +} + +#[ automatically_derived ] +impl< K, Definition, > ChildFormer< K, Definition, > +where + K : core::hash::Hash + std::cmp::Eq, + Definition : former::FormerDefinition< Storage = ChildFormerStorage< K, > > + // Definition::Types : former::FormerDefinitionTypes< Storage = ChildFormerStorage< K, > >, +{ + + #[ inline( always ) ] + pub fn perform( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + let result = self.form(); + return result; } #[ inline( always ) ] - pub fn perform( self ) -> Command< K > + pub fn new( on_end : Definition::End ) -> Self { - self.form() + Self::begin_coercing( None, None, on_end ) } #[ inline( always ) ] - pub fn new() -> CommandFormer< K > + pub fn new_coercing< IntoEnd >( end : IntoEnd ) -> Self + where IntoEnd : Into< Definition::End > { - CommandFormer::< K >::begin - ( - None, - None, - the_module::ReturnFormed, - ) + Self::begin_coercing( None, None, end ) + } + + #[ inline( always ) ] + pub fn begin( mut storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : < Definition as former::FormerDefinition >::End, ) -> Self + { + if storage.is_none() + { + storage = Some( ::core::default::Default::default() ); + } + Self + { + storage : storage.unwrap(), + context : context, + on_end : ::core::option::Option::Some( on_end ), + } } #[ inline( always ) ] - pub fn begin + pub fn begin_coercing< IntoEnd > ( - storage : core::option::Option< CommandFormerStorage< K > >, - context : core::option::Option< Context >, - on_end : End, - ) -> Self + mut storage : core::option::Option< Definition::Storage >, context : core::option::Option< Definition::Context >, on_end : IntoEnd, + ) + -> Self + where + IntoEnd : ::core::convert::Into< < Definition as former::FormerDefinition >::End > { - // xxx : fix - debug_assert!( storage.is_none() ); + if storage.is_none() + { + storage = Some( ::core::default::Default::default() ); + } Self { - storage : Default::default(), + storage : storage.unwrap(), context : context, - on_end : Some( on_end ), + on_end : ::core::option::Option::Some( ::core::convert::Into::into( on_end ) ), } } - /// Return former of your struct moving container there. Should be called after configuring the container. #[ inline( always ) ] - pub fn end( mut self ) -> Context + pub fn form( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + self.end() + } + + #[ inline( always ) ] + pub fn end( mut self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed { let on_end = self.on_end.take().unwrap(); let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) + former::FormingEnd::< Definition::Types >::call( &on_end, self.storage, context ) } - #[ inline( always ) ] + #[ inline ] pub fn name< Src >( mut self, src : Src ) -> Self - where Src : core::convert::Into< String >, + where Src : ::core::convert::Into< String > { debug_assert!( self.storage.name.is_none() ); - self.storage.name = Some( src.into() ); + self.storage.name = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); self } #[ inline( always ) ] - pub fn properties( mut self ) -> the_module::HashMapSubformer - < - K, - Property< K >, - collection_tools::HashMap< K, Property< K > >, - CommandFormer< K, Context, End >, - impl the_module::FormingEnd< collection_tools::HashMap< K, Property< K > >, Self >, - > - { - let formed = self.storage.properties.take(); - let on_end = | formed : collection_tools::HashMap< K, Property< K > >, super_former : core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - super_former.storage.properties = Some( formed ); - super_former - }; - the_module::HashMapSubformer::begin( formed, Some( self ), on_end ) + pub fn properties_set< Former2 >( self ) -> Former2 + where Former2 : former::FormerBegin< former::HashMapDefinition< K, Property< K >, Self, Self, ChildFormerPropertiesEnd, > > + { + Former2::former_begin( None, Some( self ), ChildFormerPropertiesEnd ) } + #[ inline( always ) ] + pub fn properties( self ) -> former::ContainerFormer::< ( K, Property< K >, ), former::HashMapDefinition< K, Property< K >, Self, Self, ChildFormerPropertiesEnd > > + { + self.properties_set::< former::ContainerFormer::< ( K, Property< K >, ), former::HashMapDefinition< K, Property< K >, Self, Self, ChildFormerPropertiesEnd > >>() + } +} + +// + +impl< K, Definition, > ChildFormer< K, Definition, > +where + K : core::hash::Hash + std::cmp::Eq, + Definition : former::FormerDefinition< Storage = ChildFormerStorage< K, >, Formed = Child< K, > >, + // Definition::Types : former::FormerDefinitionTypes< Storage = ChildFormerStorage< K, >, Formed = Child< K, > >, + Definition::Storage : former::StoragePreform< Preformed = Child< K, > > +{ + pub fn preform( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + former::StoragePreform::preform( self.storage ) + } +} + +#[ allow( non_camel_case_types ) ] +pub struct ChildFormerPropertiesEnd; + +#[ automatically_derived ] +impl< K, Definition, > former::FormingEnd< former::HashMapDefinitionTypes< K, Property< K >, ChildFormer< K, Definition, >, ChildFormer< K, Definition, > >, > +for ChildFormerPropertiesEnd +where + K : core::hash::Hash + std::cmp::Eq, + Definition : former::FormerDefinition< Storage = ChildFormerStorage< K, > >, + // Definition::Types : former::FormerDefinitionTypes< Storage = ChildFormerStorage< K, > >, +{ + #[ inline( always ) ] + fn call( &self, storage : collection_tools::HashMap< K, Property< K > >, super_former : Option< ChildFormer< K, Definition, > >, ) -> ChildFormer< K, Definition, > + { + let mut super_former = super_former.unwrap(); + if let Some( ref mut field ) = super_former.storage.properties + { + former::ContainerAssign::assign( field, storage ); + } + else + { + super_former.storage.properties = Some( storage ); + } + super_former + } } -// == +// == end of generated -include!( "../only_test/parametrized_struct.rs" ); +include!( "./only_test/parametrized_struct.rs" ); diff --git a/module/core/former/tests/inc/former_tests/parametrized_struct_where.rs b/module/core/former/tests/inc/former_tests/parametrized_struct_where.rs index c3b12924b7..063e58be1c 100644 --- a/module/core/former/tests/inc/former_tests/parametrized_struct_where.rs +++ b/module/core/former/tests/inc/former_tests/parametrized_struct_where.rs @@ -23,15 +23,19 @@ impl< Name > Property< Name > } #[ derive( Debug, PartialEq, the_module::Former ) ] -pub struct Command< K > +// #[ derive( Debug, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Child< K > where K : core::hash::Hash + std::cmp::Eq, { pub name : String, - #[ subformer( the_module::HashMapSubformer ) ] + #[ container( definition = former::HashMapDefinition ) ] pub properties : collection_tools::HashMap< K, Property< K > >, } -// == +// == begin_coercing of generated -include!( "../only_test/parametrized_struct.rs" ); +// == end of generated + +include!( "./only_test/parametrized_struct.rs" ); diff --git a/module/core/former/tests/inc/former_tests/string_slice.rs b/module/core/former/tests/inc/former_tests/string_slice.rs index b0c032d9cd..70466144db 100644 --- a/module/core/former/tests/inc/former_tests/string_slice.rs +++ b/module/core/former/tests/inc/former_tests/string_slice.rs @@ -1,11 +1,15 @@ use super::*; -#[ derive( Debug, PartialEq, the_module::Former ) ] +#[ derive( Debug, PartialEq, former::Former ) ] +// #[ derive( Debug, PartialEq, former::Former ) ] #[ debug ] +// #[ derive( Debug, PartialEq ) ] pub struct Struct1< 'a > { pub string_slice_1 : &'a str, } -// +// === begin_coercing of generated -include!( "../only_test/string_slice.rs" ); +// === end of generated + +include!( "./only_test/string_slice.rs" ); diff --git a/module/core/former/tests/inc/former_tests/string_slice_manual.rs b/module/core/former/tests/inc/former_tests/string_slice_manual.rs index 5d3143d25f..6b9632f366 100644 --- a/module/core/former/tests/inc/former_tests/string_slice_manual.rs +++ b/module/core/former/tests/inc/former_tests/string_slice_manual.rs @@ -7,27 +7,111 @@ pub struct Struct1< 'a > pub string_slice_1 : &'a str, } +// === begin_coercing of generated + +#[ automatically_derived ] impl< 'a > Struct1< 'a > { - #[ inline ] + + #[ inline( always ) ] pub fn former() -> Struct1Former< 'a > { - Struct1Former - { - string_slice_1 : ::core::option::Option::None, - } + Struct1Former::new_coercing( former::ReturnPreformed ) } } -pub struct Struct1Former< 'a > +// = definition types + +#[ derive( Debug ) ] +// pub struct Struct1FormerDefinitionTypes< 'a, Context = (), Formed = Struct1< 'a > > +pub struct Struct1FormerDefinitionTypes< 'a, Context, Formed > { - string_slice_1 : ::core::option::Option< &'a str >, + _phantom : core::marker::PhantomData< ( &'a(), Context, Formed ) >, } -impl< 'a > Struct1Former< 'a > +impl< 'a, Context, Formed > Default for Struct1FormerDefinitionTypes< 'a, Context, Formed > { - #[ inline ] - pub fn form( mut self ) -> Struct1< 'a > + fn default() -> Self + { + Self { _phantom : core::marker::PhantomData, } + } +} + +impl< 'a, Context, Formed > former::FormerDefinitionTypes +for Struct1FormerDefinitionTypes< 'a, Context, Formed > +{ + type Storage = Struct1FormerStorage< 'a >; + type Formed = Formed; + type Context = Context; +} + +// = former mutator + +impl< 'a, Context, Formed > former::FormerMutator +for Struct1FormerDefinitionTypes< 'a, Context, Formed > +{ +} + +// = definition + +#[ derive( Debug ) ] +// pub struct Struct1FormerDefinition< 'a, Context = (), Formed = Struct1< 'a >, End = former::ReturnPreformed > +pub struct Struct1FormerDefinition< 'a, Context, Formed, End > +{ + _phantom : core::marker::PhantomData< ( &'a(), Context, Formed, End ) >, +} + +impl< 'a, Context, Formed, End > Default for Struct1FormerDefinition< 'a, Context, Formed, End > +{ + fn default() -> Self + { + Self { _phantom : core::marker::PhantomData, } + } +} + +impl< 'a, Context, Formed, End > former::FormerDefinition +for Struct1FormerDefinition< 'a, Context, Formed, End > +where + End : former::FormingEnd< Struct1FormerDefinitionTypes< 'a, Context, Formed > > +{ + type Types = Struct1FormerDefinitionTypes< 'a, Context, Formed >; + type End = End; + type Storage = Struct1FormerStorage< 'a >; + type Formed = Formed; + type Context = Context; +} + +// pub type Struct1FormerWithClosure< 'a, Context, Formed > = +// Struct1FormerDefinition< 'a, Context, Formed, former::FormingEndClosure< Struct1FormerDefinitionTypes< 'a, Context, Formed > > >; + +// = storage + +pub struct Struct1FormerStorage< 'a > +{ + pub string_slice_1 : ::core::option::Option< &'a str >, +} + +impl< 'a > ::core::default::Default for Struct1FormerStorage< 'a > +{ + #[ inline( always ) ] + fn default() -> Self + { + Self { string_slice_1 : ::core::option::Option::None, } + } +} + +impl< 'a > former::Storage for Struct1FormerStorage< 'a > +{ + type Preformed = Struct1< 'a >; +} + +impl< 'a > former::StoragePreform for Struct1FormerStorage< 'a > +{ + // type Preformed = Struct1< 'a >; + + fn preform( mut self ) -> Self::Preformed + // fn preform( mut self ) -> < Self as former::Storage >::Formed + // fn preform( mut self ) -> Struct1< 'a > { let string_slice_1 = if self.string_slice_1.is_some() { @@ -35,23 +119,150 @@ impl< 'a > Struct1Former< 'a > } else { - let val : &'a str = ::core::default::Default::default(); - val + { + trait MaybeDefault< T > + { + fn maybe_default( self : & Self ) -> T + { + panic!( "Field 'string_slice_1' isn't initialized" ) + } + } + + impl< T > MaybeDefault< T > for & ::core::marker::PhantomData< T > {} + impl< T > MaybeDefault< T > for ::core::marker::PhantomData< T > + where T : ::core::default::Default, + { + fn maybe_default( self : & Self ) -> T { T::default() } + } + + (& ::core::marker::PhantomData::< &'a str >).maybe_default() + } }; - Struct1 { string_slice_1 } + let result = Struct1 { string_slice_1, }; + return result; + } +} + +// = former + +pub struct Struct1Former< 'a, Definition = Struct1FormerDefinition< 'a, (), Struct1< 'a >, former::ReturnPreformed > > +where + // End : former::FormingEnd::< Definition::Types >, + // Definition : former::FormerDefinition< End = End >, + // Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage< 'a >, Formed = Formed, Context = Context >, + Definition : former::FormerDefinition, + Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage< 'a > >, +{ + storage : Definition::Storage, + context : core::option::Option< Definition::Context >, + on_end : core::option::Option< Definition::End >, +} + +#[ automatically_derived ] +impl< 'a, Definition > Struct1Former< 'a, Definition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage< 'a > >, + // Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage< 'a > >, +{ + + #[ inline( always ) ] + pub fn perform( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + let result = self.form(); + return result; + } + + #[ inline( always ) ] + pub fn new( on_end : Definition::End ) -> Self + { + Self::begin_coercing( None, None, on_end ) + } + + #[ inline( always ) ] + pub fn new_coercing< IntoEnd >( end : IntoEnd ) -> Self + where IntoEnd : Into< Definition::End >, + { + Self::begin_coercing( None, None, end, ) + } + + #[ inline( always ) ] + pub fn begin + ( + mut storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : < Definition as former::FormerDefinition >::End, + ) -> Self + { + if storage.is_none() + { + storage = Some( ::core::default::Default::default() ); + } + Self + { + storage : storage.unwrap(), + context : context, + on_end : ::core::option::Option::Some( on_end ), + } + } + + #[ inline( always ) ] + pub fn begin_coercing< IntoEnd > + ( + mut storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : IntoEnd, + ) -> Self + where IntoEnd : ::core::convert::Into< < Definition as former::FormerDefinition >::End >, + { + if storage.is_none() + { + storage = Some( ::core::default::Default::default() ); + } + Self + { + storage : storage.unwrap(), + context : context, + on_end : ::core::option::Option::Some( ::core::convert::Into::into( on_end ) ), + } + } + + #[ inline( always ) ] + pub fn form( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + self.end() + } + + #[ inline( always ) ] + pub fn end( mut self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + let on_end = self.on_end.take().unwrap(); + let context = self.context.take(); + former::FormingEnd::< Definition::Types >::call( & on_end, self.storage, context ) } #[ inline ] pub fn string_slice_1< Src >( mut self, src : Src ) -> Self - where - Src : ::core::convert::Into< &'a str >, + where Src : ::core::convert::Into< &'a str >, { - debug_assert!( self.string_slice_1.is_none() ); - self.string_slice_1 = ::core::option::Option::Some( src.into() ); + debug_assert!( self.storage.string_slice_1.is_none() ); + self.storage.string_slice_1 = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); self } } -// +impl< 'a, Definition > Struct1Former< 'a, Definition > +where + Definition : former::FormerDefinition< Storage = Struct1FormerStorage< 'a >, Formed = Struct1< 'a > >, + // Definition::Types : former::FormerDefinitionTypes< Storage = Struct1FormerStorage< 'a >, Formed = Struct1< 'a > >, + Definition::Storage : former::StoragePreform< Preformed = Struct1< 'a > >, +{ + pub fn preform( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + // panic!(); + former::StoragePreform::preform( self.storage ) + } +} + +// === end of generated -include!( "../only_test/string_slice.rs" ); +include!( "./only_test/string_slice.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_basic.rs b/module/core/former/tests/inc/former_tests/subformer_basic.rs index e847a543df..11f5a65779 100644 --- a/module/core/former/tests/inc/former_tests/subformer_basic.rs +++ b/module/core/former/tests/inc/former_tests/subformer_basic.rs @@ -1,10 +1,11 @@ +#![ deny( missing_docs ) ] #![ allow( dead_code ) ] use super::*; // // this should work // -// let ca = Aggregator::former() +// let ca = Parent::former() // .parameter1( "val" ) // .command( "echo" ) // .name( "prints all subjects and properties" ) @@ -47,22 +48,22 @@ impl< Name > Property< Name > // == command #[ derive( Debug, PartialEq, the_module::Former ) ] -pub struct Command< K > +pub struct Child< K > where K : core::hash::Hash + std::cmp::Eq, { pub name : String, pub subject : String, - #[ subformer( the_module::HashMapSubformer ) ] + #[ container( definition = former::HashMapDefinition ) ] pub properties : collection_tools::HashMap< K, Property< K > >, } // manual -impl< K, Context, End > -CommandFormer< K, Context, End > +impl< K, Definition > ChildFormer< K, Definition > where K : core::hash::Hash + std::cmp::Eq, - End : the_module::FormingEnd< Command< K >, Context >, + Definition : former::FormerDefinition< Storage = ChildFormerStorage< K > >, + Definition::Storage : former::StoragePreform, { /// Inserts a key-value pair into the map. Make a new container if it was not made so far. @@ -96,51 +97,15 @@ where // == aggregator #[ derive( Debug, PartialEq, the_module::Former ) ] -pub struct Aggregator< K > +pub struct Parent< K > where K : core::hash::Hash + std::cmp::Eq, { pub parameter1 : String, - #[ subformer( the_module::HashMapSubformer ) ] - pub commands : collection_tools::HashMap< String, Command< K > >, -} - -// manual -impl< K, Context, End > -AggregatorFormer< K, Context, End > -where - K : core::hash::Hash + std::cmp::Eq, - End : the_module::FormingEnd< Aggregator< K >, Context >, -{ - - #[ inline( always ) ] - pub fn command< IntoName >( self, name : IntoName ) - -> CommandFormer< K, Self, impl the_module::FormingEnd< Command< K >, Self > > - where - K : core::hash::Hash + std::cmp::Eq, - IntoName : core::convert::Into< String >, - { - let on_end = | command : Command< K >, super_former : core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - if let Some( ref mut commands ) = super_former.storage.commands - { - commands.insert( command.name.clone(), command ); - } - else - { - let mut commands : collection_tools::HashMap< String, Command< K > > = Default::default(); - commands.insert( command.name.clone(), command ); - super_former.storage.commands = Some( commands ); - } - super_former - }; - let former = CommandFormer::begin( None, Some( self ), on_end ); - former.name( name ) - } - + #[ container( definition = former::HashMapDefinition ) ] + pub commands : collection_tools::HashMap< String, Child< K > >, } // == -include!( "../only_test/subformer_basic.rs" ); +include!( "./only_test/subformer_basic.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_basic_manual.rs b/module/core/former/tests/inc/former_tests/subformer_basic_manual.rs deleted file mode 100644 index cbb94f6e73..0000000000 --- a/module/core/former/tests/inc/former_tests/subformer_basic_manual.rs +++ /dev/null @@ -1,465 +0,0 @@ -#![ allow( dead_code ) ] -use super::*; - -// let ca = Aggregator::former() -// .parameter1( "val" ) -// .command( "echo" ) -// .name( "prints all subjects and properties" ) -// .subject( "Subject", wca::Type::String, true ) -// .property( "property", "simple property", wca::Type::String, true ) -// .routine( f1 ) -// .end() -// .command( "exit" ) -// .name( "just exit" ) -// .routine( || exit() ) -// .end() -// .perform() -// ; -// ca.execute( input ).unwrap(); - -#[ derive( Debug, PartialEq, Default ) ] -pub struct Property< Name > -{ - name : Name, - description : String, - code : isize, -} - -/// generated by new -impl< Name > Property< Name > -{ - #[ inline ] - pub fn new< Description, Code >( name : Name, description : Description, code : Code ) -> Self - where - Name : core::convert::Into< Name >, - Description : core::convert::Into< String >, - Code : core::convert::Into< isize >, - { - Self { name : name.into(), description : description.into(), code : code.into() } - } -} - -#[ derive( Debug, PartialEq ) ] -pub struct Command< K > -where - K : core::hash::Hash + std::cmp::Eq, -{ - pub name : String, - pub subject : String, - pub properties : collection_tools::HashMap< K, Property< K > >, -} - -// generated by former -impl< K > Command< K > -where - K : core::hash::Hash + std::cmp::Eq, -{ - - #[ inline( always ) ] - pub fn former() -> CommandFormer< K > - { - CommandFormer::< K >::new() - } - -} - -// generated by former -pub struct CommandFormerStorage< K > -where - K : core::hash::Hash + std::cmp::Eq, -{ - name : core::option::Option< String >, - subject : core::option::Option< String >, - properties : core::option::Option< collection_tools::HashMap< K, Property< K > > >, -} - -impl< K > Default for CommandFormerStorage< K > -where - K : core::hash::Hash + std::cmp::Eq, -{ - - #[ inline( always ) ] - fn default() -> Self - { - Self - { - name : None, - subject : None, - properties : None, - } - } - -} - -// generated by former -// #[ derive( Debug, Default ) ] -pub struct CommandFormer< K, Context = Command< K >, End = the_module::ReturnFormed > -where - K : core::hash::Hash + std::cmp::Eq, - End : the_module::FormingEnd< Command< K >, Context >, -{ - storage : CommandFormerStorage< K >, - context : core::option::Option< Context >, - on_end : core::option::Option< End >, -} - -// generated by former -impl< K, Context, End > -CommandFormer< K, Context, End > -where - K : core::hash::Hash + std::cmp::Eq, - End : the_module::FormingEnd< Command< K >, Context >, -{ - - #[ inline( always ) ] - fn form( mut self ) -> Command< K > - { - - let name = if self.storage.name.is_some() - { - self.storage.name.take().unwrap() - } - else - { - let val = Default::default(); - val - }; - - let subject = if self.storage.subject.is_some() - { - self.storage.subject.take().unwrap() - } - else - { - let val = Default::default(); - val - }; - - let properties = if self.storage.properties.is_some() - { - self.storage.properties.take().unwrap() - } - else - { - let val = Default::default(); - val - }; - - Command - { - name, - subject, - properties, - } - } - - #[ inline( always ) ] - pub fn perform( self ) -> Command< K > - { - self.form() - } - - #[ inline( always ) ] - pub fn new() -> CommandFormer< K > - { - CommandFormer::< K >::begin - ( - None, - None, - the_module::ReturnFormed, - ) - } - - #[ inline( always ) ] - pub fn begin - ( - storage : core::option::Option< CommandFormerStorage< K > >, - context : core::option::Option< Context >, - on_end : End, - ) -> Self - { - // qqq : fix - debug_assert!( storage.is_none() ); - Self - { - storage : Default::default(), - context : context, - on_end : Some( on_end ), - } - } - - /// Return former of your struct moving container there. Should be called after configuring the container. - #[ inline( always ) ] - pub fn end( mut self ) -> Context - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) - } - - #[ inline( always ) ] - pub fn name< Src >( mut self, src : Src ) -> Self - where Src : core::convert::Into< String >, - { - debug_assert!( self.storage.name.is_none() ); - self.storage.name = Some( src.into() ); - self - } - - #[ inline( always ) ] - pub fn subject< Src >( mut self, src : Src ) -> Self - where Src : core::convert::Into< String >, - { - debug_assert!( self.storage.subject.is_none() ); - self.storage.subject = Some( src.into() ); - self - } - - #[ inline( always ) ] - pub fn properties( mut self ) -> the_module::HashMapSubformer - < - K, - Property< K >, - collection_tools::HashMap< K, Property< K > >, - CommandFormer< K, Context, End >, - impl the_module::FormingEnd< collection_tools::HashMap< K, Property< K > >, Self >, - > - { - let formed = self.storage.properties.take(); - let on_end = | formed : collection_tools::HashMap< K, Property< K > >, super_former : core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - super_former.storage.properties = Some( formed ); - super_former - }; - the_module::HashMapSubformer::begin( formed, Some( self ), on_end ) - } - -} - -// manual -impl< K, Context, End > -CommandFormer< K, Context, End > -where - K : core::hash::Hash + std::cmp::Eq, - End : the_module::FormingEnd< Command< K >, Context >, -{ - - /// Inserts a key-value pair into the map. Make a new container if it was not made so far. - #[ inline( always ) ] - pub fn property< Name, Description, Code > - ( mut self, name : Name, description : Description, code : Code ) -> Self - where - Name : core::convert::Into< K > + Clone, - Description : core::convert::Into< String >, - Code : core::convert::Into< isize >, - { - if self.storage.properties.is_none() - { - self.storage.properties = core::option::Option::Some( Default::default() ); - } - if let core::option::Option::Some( ref mut properties ) = self.storage.properties - { - let property = Property - { - name : name.clone().into(), - description : description.into(), - code : code.into(), - }; - properties.insert( name.into(), property ); - } - self - } - -} - -// == aggregator - -#[ derive( Debug, PartialEq ) ] -pub struct Aggregator< K > -where - K : core::hash::Hash + std::cmp::Eq, -{ - pub parameter1 : String, - pub commands : collection_tools::HashMap< String, Command< K > >, -} - -// generated by former -impl< K > Aggregator< K > -where - K : core::hash::Hash + std::cmp::Eq, -{ - - #[ inline( always ) ] - pub fn former() -> AggregatorFormer< K > - { - AggregatorFormer::< K >::new() - } - -} - -// generated by former -// #[ derive( Debug, Default ) ] -pub struct AggregatorFormer< K, Context = Aggregator< K >, End = the_module::ReturnFormed > -where - K : core::hash::Hash + std::cmp::Eq, - End : the_module::FormingEnd< Aggregator< K >, Context >, -{ - parameter1 : core::option::Option< String >, - commands : core::option::Option< collection_tools::HashMap< String, Command< K > > >, - context : core::option::Option< Context >, - on_end : core::option::Option< End >, -} - -// generated by former -impl< K, Context, End > -AggregatorFormer< K, Context, End > -where - K : core::hash::Hash + std::cmp::Eq, - End : the_module::FormingEnd< Aggregator< K >, Context >, -{ - - #[ inline( always ) ] - fn form( mut self ) -> Aggregator< K > - { - - let parameter1 = if self.parameter1.is_some() - { - self.parameter1.take().unwrap() - } - else - { - let val = Default::default(); - val - }; - - let commands = if self.commands.is_some() - { - self.commands.take().unwrap() - } - else - { - let val = Default::default(); - val - }; - - Aggregator - { - parameter1, - commands, - } - } - - #[ inline( always ) ] - pub fn perform( self ) -> Aggregator< K > - { - self.form() - } - - #[ inline( always ) ] - pub fn new() -> AggregatorFormer< K > - { - AggregatorFormer::< K >::begin - ( - None, - the_module::ReturnFormed, - ) - } - - #[ inline( always ) ] - pub fn begin - ( - context : core::option::Option< Context >, - on_end : End, - ) -> Self - { - Self - { - parameter1 : None, - commands : None, - context : context, - on_end : Some( on_end ), - } - } - - /// Return former of your struct moving container there. Should be called after configuring the container. - #[ inline( always ) ] - pub fn end( mut self ) -> Context - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let formed = self.form(); - on_end.call( formed, context ) - } - - #[ inline( always ) ] - pub fn parameter1< Src >( mut self, src : Src ) -> Self - where Src : core::convert::Into< String >, - { - debug_assert!( self.parameter1.is_none() ); - self.parameter1 = Some( src.into() ); - self - } - - #[ inline( always ) ] - pub fn commands( mut self ) -> the_module::HashMapSubformer - < - String, - Command< K >, - collection_tools::HashMap< String, Command< K > >, - AggregatorFormer< K, Context, End >, - impl the_module::FormingEnd< collection_tools::HashMap< String, Command< K > >, Self >, - > - { - let formed = self.commands.take(); - let on_end = | formed : collection_tools::HashMap< String, Command< K > >, super_former : core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - super_former.commands = Some( formed ); - super_former - }; - the_module::HashMapSubformer::begin( formed, Some( self ), on_end ) - } - -} - -// manual -impl< K, Context, End > -AggregatorFormer< K, Context, End > -where - K : core::hash::Hash + std::cmp::Eq, - End : the_module::FormingEnd< Aggregator< K >, Context >, -{ - - #[ inline( always ) ] - pub fn command< IntoName >( self, name : IntoName ) - -> CommandFormer< K, Self, impl the_module::FormingEnd< Command< K >, Self > > - where - K : core::hash::Hash + std::cmp::Eq, - IntoName : core::convert::Into< String >, - { - let on_end = | command : Command< K >, super_former : core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - if let Some( ref mut commands ) = super_former.commands - { - commands.insert( command.name.clone(), command ); - } - else - { - let mut commands : collection_tools::HashMap< String, Command< K > > = Default::default(); - commands.insert( command.name.clone(), command ); - super_former.commands = Some( commands ); - } - super_former - }; - let former = CommandFormer::begin( None, Some( self ), on_end ); - former.name( name ) - } - -} - -// == - -include!( "../only_test/subformer_basic.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_container.rs b/module/core/former/tests/inc/former_tests/subformer_container.rs new file mode 100644 index 0000000000..c168f83e8b --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_container.rs @@ -0,0 +1,27 @@ +#![ allow( dead_code ) ] + +use super::*; + +/// Parameter description. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent required for the template. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ derive( Debug, Default, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + #[ container( definition = former::VectorDefinition ) ] + children : Vec< Child >, +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/subformer_container.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_container_custom.rs b/module/core/former/tests/inc/former_tests/subformer_container_custom.rs new file mode 100644 index 0000000000..58f3a72356 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_container_custom.rs @@ -0,0 +1,264 @@ +#![ deny( missing_docs ) ] +#![ allow( dead_code ) ] + +use super::*; +use collection_tools::HashSet; + +// == define custom containers + +// Custom container that logs additions +#[ derive( Debug, PartialEq ) ] +pub struct LoggingSet< K > +where + K : core::cmp::Eq + core::hash::Hash, +{ + set : HashSet< K >, +} + +impl< K > Default for LoggingSet< K > +where + K : core::cmp::Eq + core::hash::Hash, +{ + + #[ inline( always ) ] + fn default() -> Self + { + Self + { + set : Default::default() + } + } + +} + +impl< K > IntoIterator for LoggingSet< K > +where + K : std::cmp::Eq + std::hash::Hash, +{ + type Item = K; + type IntoIter = std::collections::hash_set::IntoIter< K >; + + fn into_iter( self ) -> Self::IntoIter + { + self.set.into_iter() + } +} + +impl<'a, K> IntoIterator for &'a LoggingSet< K > +where + K : std::cmp::Eq + std::hash::Hash, +{ + type Item = &'a K; + type IntoIter = std::collections::hash_set::Iter< 'a, K >; + + fn into_iter( self ) -> Self::IntoIter + { + self.set.iter() + } +} + +impl< K > former::Container for LoggingSet< K > +where + K : core::cmp::Eq + core::hash::Hash, +{ + type Entry = K; + type Val = K; + + #[ inline( always ) ] + fn entry_to_val( e : Self::Entry ) -> Self::Val + { + e + } + +} + +impl< K > former::ContainerAdd for LoggingSet< K > +where + K : core::cmp::Eq + core::hash::Hash, +{ + + #[ inline( always ) ] + fn add( &mut self, e : Self::Entry ) -> bool + { + self.set.insert( e ) + } + +} + +impl< K > former::ContainerAssign for LoggingSet< K > +where + K : core::cmp::Eq + core::hash::Hash, +{ + fn assign< Elements >( &mut self, elements : Elements ) -> usize + where + Elements : IntoIterator< Item = Self::Entry > + { + let initial_len = self.set.len(); + self.set.extend( elements ); + self.set.len() - initial_len + } +} + +impl< K > former::ContainerValToEntry< K > for LoggingSet< K > +where + K : core::cmp::Eq + core::hash::Hash, +{ + type Entry = K; + #[ inline( always ) ] + fn val_to_entry( val : K ) -> Self::Entry + { + val + } +} + +// = storage + +impl< K > former::Storage +for LoggingSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Preformed = LoggingSet< K >; +} + +impl< K > former::StoragePreform +for LoggingSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + fn preform( self ) -> Self::Preformed + { + self + } +} + +// = definition types + +#[ derive( Debug, Default ) ] +pub struct LoggingSetDefinitionTypes< K, Context = (), Formed = LoggingSet< K > > +{ + _phantom : core::marker::PhantomData< ( K, Context, Formed ) >, +} + +impl< K, Context, Formed > former::FormerDefinitionTypes +for LoggingSetDefinitionTypes< K, Context, Formed > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Storage = LoggingSet< K >; + type Formed = Formed; + type Context = Context; +} + +// = definition + +#[ derive( Debug, Default ) ] +pub struct LoggingSetDefinition< K, Context = (), Formed = LoggingSet< K >, End = former::ReturnStorage > +{ + _phantom : core::marker::PhantomData< ( K, Context, Formed, End ) >, +} + +impl< K, Context, Formed, End > former::FormerDefinition +for LoggingSetDefinition< K, Context, Formed, End > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + End : former::FormingEnd< LoggingSetDefinitionTypes< K, Context, Formed > >, +{ + type Storage = LoggingSet< K >; + type Formed = Formed; + type Context = Context; + + type Types = LoggingSetDefinitionTypes< K, Context, Formed >; + type End = End; +} + +// = mutator + +impl< K, Context, Formed > former::FormerMutator +for LoggingSetDefinitionTypes< K, Context, Formed > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ +} + +// = Entity To + +impl< K, Definition > former::EntityToFormer< Definition > for LoggingSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + Definition : former::FormerDefinition + < + Storage = LoggingSet< K >, + Types = LoggingSetDefinitionTypes + < + K, + < Definition as former::FormerDefinition >::Context, + < Definition as former::FormerDefinition >::Formed, + >, + >, + Definition::End : former::FormingEnd< Definition::Types >, +{ + type Former = LoggingSetAsSubformer< K, Definition::Context, Definition::Formed, Definition::End >; +} + +impl< K > former::EntityToStorage +for LoggingSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Storage = LoggingSet< K >; +} + +impl< K, Context, Formed, End > former::EntityToDefinition< Context, Formed, End > +for LoggingSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, + End : former::FormingEnd< LoggingSetDefinitionTypes< K, Context, Formed > >, +{ + type Definition = LoggingSetDefinition< K, Context, Formed, End >; + type Types = LoggingSetDefinitionTypes< K, Context, Formed >; +} + +impl< K, Context, Formed > former::EntityToDefinitionTypes< Context, Formed > +for LoggingSet< K > +where + K : ::core::cmp::Eq + ::core::hash::Hash, +{ + type Types = LoggingSetDefinitionTypes< K, Context, Formed >; +} + +// = subformer + +pub type LoggingSetAsSubformer< K, Context, Formed, End > = +former::ContainerFormer::< K, LoggingSetDefinition< K, Context, Formed, End > >; + +// == use custom container + +/// Parent required for the template. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Parent +{ + #[ container ] + children : LoggingSet< i32 >, +} + +// == begin of generated + +// == end of generated + +#[ test ] +fn basic() +{ + + // Using the builder pattern provided by Former to manipulate Parent + let parent = Parent::former() + .children() + .add(10) + .add(20) + .add(10) + .end() + .form(); + + println!("Got: {:?}", parent); + +} diff --git a/module/core/former/tests/inc/former_tests/subformer_container_implicit.rs b/module/core/former/tests/inc/former_tests/subformer_container_implicit.rs new file mode 100644 index 0000000000..55b91e69de --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_container_implicit.rs @@ -0,0 +1,29 @@ +#![ deny( missing_docs ) ] +#![ allow( dead_code ) ] + +use super::*; + +/// Parameter description. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent required for the template. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ derive( Debug, Default, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + // #[ container( definition = former::VectorDefinition ) ] + #[ container ] + children : Vec< Child >, +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/subformer_container.rs" ); 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 new file mode 100644 index 0000000000..82811c38eb --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_container_manual.rs @@ -0,0 +1,109 @@ +#![ allow( dead_code ) ] + +use super::*; + +/// Parameter description. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent required for the template. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ derive( Debug, Default, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + // #[ container( definition = former::VectorDefinition ) ] + #[ scalar( setter = false ) ] + children : Vec< Child >, +} + +// == begin of generated for Parent in context of attribute container( former::VectorDefinition ) ] + +#[ automatically_derived ] +impl< Definition, > ParentFormer< Definition, > +where + Definition : former::FormerDefinition< Storage = ParentFormerStorage< > >, +{ + + #[ inline( always ) ] + pub fn _children_container_former< Former2 >( self ) -> Former2 + where + Former2 : former::FormerBegin< former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > >, + { + Former2::former_begin( None, Some( self ), ParentFormerAssignChildrenEnd::< Definition >::default() ) + } + + #[ inline( always ) ] + pub fn children( self ) -> former::ContainerFormer:: + < + Child, + former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > + > + { + self._children_container_former::< former::ContainerFormer::< Child, former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > > >() + } + +} + +// + +#[ doc = r"Callback to return original former after forming of container for `vec_1` is done. Callback replace content of container assigning new content from subformer's storage." ] +pub struct ParentFormerAssignChildrenEnd< Definition > +{ + _phantom : core::marker::PhantomData< ( Definition, ) >, +} + +impl< Definition > Default for ParentFormerAssignChildrenEnd< Definition > +{ + + #[ inline( always ) ] + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } + +} + +#[ automatically_derived ] +impl< Definition, > former::FormingEnd +< + < + Vec< Child > as former::EntityToDefinitionTypes< ParentFormer< Definition, >, ParentFormer< Definition, > > + >::Types +> +for ParentFormerAssignChildrenEnd< Definition > +where + Definition : former::FormerDefinition< Storage = ParentFormerStorage< > >, +{ + #[ inline( always ) ] + fn call + ( + &self, + storage : Vec< Child >, + super_former : Option< ParentFormer< Definition, > >, + ) + -> ParentFormer< Definition, > + { + let mut super_former = super_former.unwrap(); + if let Some( ref mut field ) = super_former.storage.children + { + former::ContainerAssign::assign( field, storage ); + } + else + { + super_former.storage.children = Some( storage ); + } + super_former + } +} + +// == end of generated for Parent in context of attribute container( former::VectorDefinition ) ] + +include!( "./only_test/subformer_container.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_container_named.rs b/module/core/former/tests/inc/former_tests/subformer_container_named.rs new file mode 100644 index 0000000000..75ce8845b6 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_container_named.rs @@ -0,0 +1,43 @@ +#![ allow( dead_code ) ] + +use super::*; + +/// Parameter description. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent required for the template. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ derive( Debug, Default, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + #[ container( name = children2 ) ] + children : Vec< Child >, +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn children( self ) -> &'static str + { + r#" + Scalar setter `children` should not be generated by default if container is used. + It can only be generated if req + "# + } + +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/subformer_container_children2.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_container_setter_off.rs b/module/core/former/tests/inc/former_tests/subformer_container_setter_off.rs new file mode 100644 index 0000000000..5f3ca56708 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_container_setter_off.rs @@ -0,0 +1,51 @@ +#![ allow( dead_code ) ] + +use super::*; + +/// Child +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent + +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + #[ container( setter = false ) ] + // #[ scalar( setter = false ) ] + children : Vec< Child >, +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn children( self ) -> &'static str + { + r#" + Scalar setter `children` should not be generated by default if container is used. + It can only be generated if req + "# + } + + #[ inline( always ) ] + pub fn children2( self ) -> former::ContainerFormer:: + < + Child, + former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > + > + { + self._children_container_former::< _ >() + } + +} + +include!( "./only_test/subformer_container_children2.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_container_setter_on.rs b/module/core/former/tests/inc/former_tests/subformer_container_setter_on.rs new file mode 100644 index 0000000000..83233366cf --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_container_setter_on.rs @@ -0,0 +1,45 @@ +#![ allow( dead_code ) ] + +use super::*; + +/// Child +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent + +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + // Such parameters switch off generation of front-end container setter and switch on scalar setter. + // Without explicit scalar_setter( true ) scalar setter is not generated. + #[ subform( setter = false ) ] + #[ scalar( setter = true ) ] + children : Vec< Child >, +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn children2( self ) -> former::ContainerFormer:: + < + Child, + former::VectorDefinition< Child, Self, Self, ParentFormerAssignChildrenEnd< Definition >, > + > + { + self._children_container_former::< _ >() + } + +} + +include!( "./only_test/subformer_scalar_children.rs" ); +include!( "./only_test/subformer_container_children2.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_hashmap.rs b/module/core/former/tests/inc/former_tests/subformer_hashmap.rs deleted file mode 100644 index cfe3574aed..0000000000 --- a/module/core/former/tests/inc/former_tests/subformer_hashmap.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![ allow( dead_code ) ] - -#[ allow( unused_imports ) ] -use super::*; -#[ allow( unused_imports ) ] -use collection_tools::HashMap; - -// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -#[ cfg( not( feature = "use_alloc" ) ) ] -#[ test ] -fn push() -{ - - // - - let got : HashMap< String, String > = the_module::HashMapSubformer::new() - .insert( "a", "x" ) - .insert( "b", "y" ) - .form(); - let exp = hmap! - [ - "a".to_string() => "x".to_string(), - "b".to_string() => "y".to_string(), - ]; - a_id!( got, exp ); - -} - -// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -#[ cfg( not( feature = "use_alloc" ) ) ] -#[ test ] -fn replace() -{ - - let got : HashMap< String, String > = the_module::HashMapSubformer::new() - .insert( "x", "x" ) - .replace( hmap![ "a".to_string() => "x".to_string(), "b".to_string() => "y".to_string() ] ) - .form(); - let exp = hmap! - [ - "a".to_string() => "x".to_string(), - "b".to_string() => "y".to_string(), - ]; - a_id!( got, exp ); - -} - diff --git a/module/core/former/tests/inc/former_tests/subformer_hashset.rs b/module/core/former/tests/inc/former_tests/subformer_hashset.rs deleted file mode 100644 index eeca2ca81c..0000000000 --- a/module/core/former/tests/inc/former_tests/subformer_hashset.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![ allow( dead_code ) ] - -#[ allow( unused_imports ) ] -use super::*; -#[ allow( unused_imports ) ] -use collection_tools::HashSet; - - -// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -#[ cfg( not( feature = "use_alloc" ) ) ] -#[ test ] -fn push() -{ - - let got : HashSet< String > = the_module::HashSetSubformer::new() - .insert( "a" ) - .insert( "b" ) - .form(); - let exp = hset! - [ - "a".to_string(), - "b".to_string(), - ]; - a_id!( got, exp ); - -} - -// qqq : zzz : remove #[ cfg( not( feature = "use_alloc" ) ) ] -#[ cfg( not( feature = "use_alloc" ) ) ] -#[ test ] -fn replace() -{ - - let got : HashSet< String > = the_module::HashSetSubformer::new() - .insert( "x" ) - .replace( hset![ "a".to_string(), "b".to_string() ] ) - .form(); - let exp = hset! - [ - "a".to_string(), - "b".to_string(), - ]; - a_id!( got, exp ); - -} diff --git a/module/core/former/tests/inc/former_tests/subformer_shortcut.rs b/module/core/former/tests/inc/former_tests/subformer_shortcut.rs deleted file mode 100644 index bf8c825690..0000000000 --- a/module/core/former/tests/inc/former_tests/subformer_shortcut.rs +++ /dev/null @@ -1,114 +0,0 @@ -#![ allow( dead_code ) ] - -use super::*; - -/// Parameter description. -#[ derive( Debug, Default, PartialEq, the_module::Former ) ] -pub struct TemplateParameterDescriptor -{ - descriptor : String, - is_mandatory : bool, -} - -/// Parameters required for the template. -#[ derive( Debug, Default, PartialEq, the_module::Former ) ] -pub struct TemplateParameters -{ - // #[ debug = the_module::VectorSubformer, descriptor, descriptor( name ) ] - #[ subformer( the_module::VectorSubformer ) ] - descriptors : Vec< TemplateParameterDescriptor >, - - // #[ subformer_setter = the_module::VectorSubformer ] - // pub fn descriptor( self, name : &str ) - // { - // descriptor( name ) - // } - -} - -impl< Context, End > former::FormerBegin< TemplateParameterDescriptorFormerStorage, TemplateParameterDescriptor, Context > -for TemplateParameterDescriptorFormer< Context, End > -where - End : the_module::FormingEnd< TemplateParameterDescriptor, Context >, -{ - type End = End; - - #[ inline( always ) ] - fn _begin - ( - storage : core::option::Option< TemplateParameterDescriptorFormerStorage >, /* xxx2 : that should be storage */ - context : core::option::Option< Context >, - on_end : End, - ) -> Self - { - debug_assert!( storage.is_none() ); - Self::begin( None, context, on_end ) - } - -} - -impl< Context, End > TemplateParametersFormer< Context, End > -where - End : former::FormingEnd< TemplateParameters, Context >, -{ - - #[ inline( always ) ] - pub fn descriptor3< Former2 >( self ) -> - Former2 - where - Former2 : former::FormerBegin - < - TemplateParameterDescriptorFormerStorage, - TemplateParameterDescriptor, - Self, - End = former::FormingEndWrapper< TemplateParameterDescriptor, Self >, - >, - // FieldContainer : ContainerAdd, - { - let on_end = | descriptor : TemplateParameterDescriptor, super_former : core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - if super_former.storage.descriptors.is_none() - { - super_former.storage.descriptors = Some( Default::default() ); - } - if let Some( ref mut descriptors ) = super_former.storage.descriptors - { - former::ContainerAdd::add( descriptors, descriptor ); - } - super_former - }; - Former2::_begin( None, Some( self ), former::FormingEndWrapper::new( on_end ) ) - } - - // xxx2 : move to a trait and make easier to use subformer, trait with generic interface of a container should help - - #[ inline( always ) ] - pub fn descriptor( self, name : &str ) -> - TemplateParameterDescriptorFormer< Self, impl former::FormingEnd< TemplateParameterDescriptor, Self > > - { - self.descriptor3::< TemplateParameterDescriptorFormer< _, _ > >().descriptor( name ) - } - -} - -#[ test ] -fn basic() -{ - - let got = TemplateParameters::former() - .descriptors() - .push( TemplateParameterDescriptor::former().descriptor( "a" ).form() ) - .push( TemplateParameterDescriptor::former().descriptor( "b" ).form() ) - .end() - .form(); - - let descriptors = vec! - [ - TemplateParameterDescriptor { descriptor : "a".to_string(), is_mandatory : false }, - TemplateParameterDescriptor { descriptor : "b".to_string(), is_mandatory : false }, - ]; - let exp = TemplateParameters { descriptors }; - a_id!( got, exp ); - -} diff --git a/module/core/former/tests/inc/former_tests/subformer_subform.rs b/module/core/former/tests/inc/former_tests/subformer_subform.rs new file mode 100644 index 0000000000..e112d38ecd --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_subform.rs @@ -0,0 +1,49 @@ +#![ allow( dead_code ) ] + +use super::*; + +/// Child +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent + +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + #[ subform( setter = false ) ] + children : Vec< Child >, +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn child( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._children_add::< ChildFormer< _ >, _, >() + .name( name ) + } + + #[ inline( always ) ] + pub fn _child( self ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._children_add + ::< < Child as former::EntityToFormer< _ > >::Former, _, >() + } + +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/subformer_subform_child.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_and_container.rs b/module/core/former/tests/inc/former_tests/subformer_subform_and_container.rs new file mode 100644 index 0000000000..6a3546113e --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_subform_and_container.rs @@ -0,0 +1,56 @@ +#![ allow( dead_code ) ] + +use super::*; + +/// Parameter description. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent required for the template. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ derive( Debug, Default, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + #[ subform( name = _child ) ] + #[ container( name = children2 ) ] + #[ scalar( name = children3 ) ] + children : Vec< Child >, +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn child( self, name : &str ) -> + ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._children_add + ::< ChildFormer< _ >, _, >() + .name( name ) + } + + #[ inline( always ) ] + pub fn children( self ) -> &'static str + { + r#" + Scalar setter `children` should not be generated by default if subform is used. + It can only be generated if req + "# + } + +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/subformer_subform_child.rs" ); +include!( "./only_test/subformer_container_children2.rs" ); +include!( "./only_test/subformer_scalar_children3.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_and_container_parametrized.rs b/module/core/former/tests/inc/former_tests/subformer_subform_and_container_parametrized.rs new file mode 100644 index 0000000000..43347fc9ce --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_subform_and_container_parametrized.rs @@ -0,0 +1,134 @@ +#![ allow( dead_code ) ] +#[ allow( unused_imports ) ] +use super::*; + +/// Parameter description. +#[ allow( explicit_outlives_requirements ) ] +#[ derive( Debug, PartialEq, the_module::Former ) ] +// #[ derive( Debug, PartialEq ) ] +pub struct Child< 'child, T > +where + T : 'child + ?Sized, +{ + name : String, + data : &'child T, +} + +/// Parent required for the template. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ derive( Debug, Default, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent< 'child > +{ + #[ subform( name = _child ) ] + #[ container( name = children2 ) ] + #[ scalar( name = children3 ) ] + children : Vec< Child< 'child, str > >, +} + +impl< 'child, Definition > ParentFormer< 'child, Definition > +where + Definition : former::FormerDefinition< Storage = < Parent< 'child > as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn child( self, name : &str ) -> + ChildAsSubformer< 'child, str, Self, impl ChildAsSubformerEnd< 'child, str, Self > > + { + self._children_add + ::< ChildFormer< '_, _, _ >, _, >() + .name( name ) + } + +} + +// == begin of generated + +// == end of generated + +#[ test ] +fn subform_child() +{ + + let got = Parent::former() + .child( "a" ).data( "aa" ).end() + .child( "b" ).data( "bb" ).end() + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), data : "aa" }, + Child { name : "b".to_string(), data : "bb" }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} + +#[ test ] +fn subform_child_generated() +{ + + let got = Parent::former() + ._child().name( "a" ).data( "aa" ).end() + ._child().name( "b" ).data( "bb" ).end() + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), data : "aa" }, + Child { name : "b".to_string(), data : "bb" }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} + +#[ test ] +fn container() +{ + + let got = Parent::former() + .children2() + .add( Child::former().name( "a" ).data( "aa" ).form() ) + .add( Child::former().name( "b" ).data( "bb" ).form() ) + .end() + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), data : "aa" }, + Child { name : "b".to_string(), data : "bb" }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} + + +#[ test ] +fn scalar() +{ + + let children = vec! + [ + Child { name : "a".to_string(), data : "aa" }, + Child { name : "b".to_string(), data : "bb" }, + ]; + let got = Parent::former() + .children3( children ) + .form(); + + let children = vec! + [ + Child { name : "a".to_string(), data : "aa" }, + Child { name : "b".to_string(), data : "bb" }, + ]; + let exp = Parent { children }; + a_id!( got, exp ); + +} + +// include!( "./only_test/subformer_subform_child.rs" ); +// include!( "./only_test/subformer_container_children2.rs" ); +// include!( "./only_test/subformer_scalar_children3.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_hashmap.rs b/module/core/former/tests/inc/former_tests/subformer_subform_hashmap.rs new file mode 100644 index 0000000000..2c0ad7e8d7 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_subform_hashmap.rs @@ -0,0 +1,57 @@ +#![ allow( dead_code ) ] + +#[ allow( unused_imports ) ] +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::HashMap; + +// Child struct with Former derived for builder pattern support +#[ derive( Debug, PartialEq, former::Former ) ] +pub struct Child +{ + name : String, + description : String, +} + +// Parent struct to hold commands +#[ derive( Debug, PartialEq, former::Former ) ] +// #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Parent +{ + #[ subform ] + command : HashMap< String, Child >, +} + +impl former::ValToEntry< HashMap< String, Child > > for Child +{ + type Entry = ( String, Child ); + #[ inline( always ) ] + fn val_to_entry( self ) -> Self::Entry + { + ( self.name.clone(), self ) + } +} + +// == begin of generated + +// == end of generated + +#[ test ] +fn basic() +{ + + let got = Parent::former() + .command() + .name( "echo" ) + .description( "prints all subjects and properties" ) // sets additional properties using custom subformer + .end() + .command() + .name( "exit" ) + .description( "just exit" ) // Sets additional properties using using custom subformer + .end() + .form(); + + a_id!( got.command.len(), 2 ); + +} diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_hashmap_custom.rs b/module/core/former/tests/inc/former_tests/subformer_subform_hashmap_custom.rs new file mode 100644 index 0000000000..175197fab8 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_subform_hashmap_custom.rs @@ -0,0 +1,178 @@ +#![ allow( dead_code ) ] + +#[ allow( unused_imports ) ] +use super::*; +#[ allow( unused_imports ) ] +use collection_tools::HashMap; + +// Child struct with Former derived for builder pattern support +#[ derive( Clone, Debug, PartialEq, former::Former ) ] +pub struct Child +{ + name : String, + description : String, +} + +// Parent struct to hold commands +#[ derive( Debug, PartialEq, former::Former ) ] +// #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Parent +{ + #[ subform( setter = false ) ] + command : HashMap< String, Child >, +} + +// Use ChildFormer as custom subformer for ParentFormer to add commands by name. +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + // more generic version + #[ inline( always ) ] + pub fn _children_add_with_closure< Former2, Definition2, Types2 >( self ) -> + Former2 + where + Types2 : former::FormerDefinitionTypes + < + Storage = ChildFormerStorage, + Formed = Self, + Context = Self, + >, + Definition2 : former::FormerDefinition + < + Types = Types2, + End = former::FormingEndClosure< Types2 >, + Storage = ChildFormerStorage, + Formed = Self, + Context = Self, + >, + Definition2::End : former::FormingEnd< Definition2::Types >, + Former2 : former::FormerBegin + < + Definition2, + >, + { + let on_end = | substorage : ChildFormerStorage, super_former : core::option::Option< Self > | -> Self + { + let mut super_former = super_former.unwrap(); + if super_former.storage.command.is_none() + { + super_former.storage.command = Some( Default::default() ); + } + if let Some( ref mut children ) = super_former.storage.command + { + former::ContainerAdd::add + ( + children, + < < HashMap< String, Child > as former::Container >::Val as former::ValToEntry< HashMap< String, Child > > > + ::val_to_entry( former::StoragePreform::preform( substorage ) ) + ); + } + super_former + }; + Former2::former_begin( None, Some( self ), former::FormingEndClosure::new( on_end ) ) + } + + // reuse _command_add + #[ inline( always ) ] + pub fn command( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._command_add::< ChildFormer< _ >, _, >() + .name( name ) + } + + // that's how you should do custom subformer setters if you can't reuse _command_add + #[ inline( always ) ] + pub fn command2( self, name : &str ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + let on_end = | substorage : ChildFormerStorage, super_former : core::option::Option< Self > | -> Self + { + let mut super_former = super_former.unwrap(); + let preformed = former::StoragePreform::preform( substorage ); + + if super_former.storage.command.is_none() + { + super_former.storage.command = Some( Default::default() ); + } + + // add instance to the container + super_former.storage.command.as_mut().unwrap() + .entry( preformed.name.clone() ) + .or_insert( preformed.clone() ); + + // custom logic to add two instances to the container + super_former.storage.command.as_mut().unwrap() + .entry( format!( "{}_2", preformed.name ) ) + .or_insert( preformed.clone() ); + + super_former + }; + let subformer = ChildAsSubformer::< Self, _ >::begin( None, Some( self ), former::FormingEndClosure::new( on_end ) ); + subformer.name( name ) + } + +} + +impl former::ValToEntry< HashMap< String, Child > > for Child +{ + type Entry = ( String, Child ); + #[ inline( always ) ] + fn val_to_entry( self ) -> Self::Entry + { + ( self.name.clone(), self ) + } +} + +// == begin of generated + +// == end of generated + +#[ test ] +fn custom1() +{ + + let got = Parent::former() + .command( "echo" ) + .description( "prints all subjects and properties" ) // sets additional properties using custom subformer + .end() + .command( "exit" ) + .description( "just exit" ) // Sets additional properties using using custom subformer + .end() + .form(); + + let got = got.command.iter().map( | e | e.0 ).cloned().collect::< collection_tools::HashSet< String > >(); + let exp = collection_tools::hset! + [ + "echo".into(), + "exit".into(), + ]; + a_id!( got, exp ); + +} + +#[ test ] +fn custom2() +{ + + let got = Parent::former() + .command2( "echo" ) + .description( "prints all subjects and properties" ) // sets additional properties using custom subformer + .end() + .command2( "exit" ) + .description( "just exit" ) // Sets additional properties using using custom subformer + .end() + .form(); + + let got = got.command.iter().map( | e | e.0 ).cloned().collect::< collection_tools::HashSet< String > >(); + let exp = collection_tools::hset! + [ + "echo".into(), + "echo_2".into(), + "exit".into(), + "exit_2".into(), + ]; + a_id!( got, exp ); + +} diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_manual.rs b/module/core/former/tests/inc/former_tests/subformer_subform_manual.rs new file mode 100644 index 0000000000..dd75b254c0 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_subform_manual.rs @@ -0,0 +1,204 @@ +#![ allow( dead_code ) ] + +use super::*; + +/// Parameter description. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent required for the template. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + // #[ container( definition = former::VectorDefinition ) ] + // #[ subform ] + #[ scalar( setter = false ) ] + children : Vec< Child >, +} + +// = custom + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, + // Definition::Types : former::FormerDefinitionTypes< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn _children_add_with_closure< Former2, Definition2, Types2 >( self ) -> + Former2 + where + Types2 : former::FormerDefinitionTypes + < + Storage = ChildFormerStorage, + Formed = Self, + Context = Self, + >, + Definition2 : former::FormerDefinition + < + Types = Types2, + End = former::FormingEndClosure< Types2 >, + Storage = ChildFormerStorage, + Formed = Self, + Context = Self, + >, + Definition2::End : former::FormingEnd< Definition2::Types >, + Former2 : former::FormerBegin + < + Definition2, + >, + { + let on_end = | substorage : ChildFormerStorage, super_former : core::option::Option< Self > | -> Self + { + let mut super_former = super_former.unwrap(); + if super_former.storage.children.is_none() + { + super_former.storage.children = Some( Default::default() ); + } + if let Some( ref mut children ) = super_former.storage.children + { + former::ContainerAdd::add + ( + children, + < < Vec< Child > as former::Container >::Val as former::ValToEntry< Vec< Child > > > + ::val_to_entry( former::StoragePreform::preform( substorage ) ) + ); + } + super_former + }; + Former2::former_begin( None, Some( self ), former::FormingEndClosure::new( on_end ) ) + } + + // < < #field_ty as former::Container >::Val as former::ValToEntry< #field_ty > > + + // less generic, but more concise way to define custom subform setter + #[ inline( always ) ] + pub fn child( self, name : &str ) -> + ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._children_add + ::< ChildFormer< _ >, _, >() + .name( name ) + } + + // #[ inline( always ) ] + // pub fn _child( self ) -> + // ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + // { + // self._children_add + // ::< < Child as former::EntityToFormer< _ > >::Former, _, >() + // } + + // it is generated + #[ inline( always ) ] + pub fn _child( self ) -> + < < Vec< Child > as former::Container >::Entry as former::EntityToFormer + < + // ChildFormerDefinition< Self, Self, ParentFormerAddChildrenEnd< Definition > >, + < + < Vec< Child > as former::Container >::Entry as former::EntityToDefinition< Self, Self, ParentFormerAddChildrenEnd< Definition > > + >::Definition, + > + >::Former + { + self._children_add + ::< < < Vec< Child > as former::Container >::Entry as former::EntityToFormer< _ > >::Former, _, >() + } + +} + +// == begin of generated for Parent in context of attribute subform + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, + // Definition::Types : former::FormerDefinitionTypes< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn _children_add< Former2, Definition2 >( self ) -> + Former2 + where + Definition2 : former::FormerDefinition + < + End = ParentFormerAddChildrenEnd< Definition >, + Storage = < Child as former::EntityToStorage >::Storage, + Formed = Self, + Context = Self, + >, + Definition2::Types : former::FormerDefinitionTypes + < + Storage = < Child as former::EntityToStorage >::Storage, + Formed = Self, + Context = Self, + >, + Former2 : former::FormerBegin< Definition2 >, + { + Former2::former_begin( None, Some( self ), ParentFormerAddChildrenEnd::default() ) + } + +} + +/// Handles the completion of and element of subformer's container. +pub struct ParentFormerAddChildrenEnd< Definition > +{ + _phantom : core::marker::PhantomData< fn( Definition ) >, +} + +impl< Definition > Default +for ParentFormerAddChildrenEnd< Definition > +{ + #[ inline( always ) ] + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } +} + +impl< Types2, Definition > former::FormingEnd< Types2, > +for ParentFormerAddChildrenEnd< Definition > +where + Definition : former::FormerDefinition + < + Storage = < Parent as former::EntityToStorage >::Storage, + >, + Types2 : former::FormerDefinitionTypes + < + Storage = < < Vec< Child > as former::Container >::Entry as former::EntityToStorage >::Storage, + Formed = ParentFormer< Definition >, + Context = ParentFormer< Definition >, + >, +{ + #[ inline( always ) ] + fn call + ( + &self, + substorage : Types2::Storage, + super_former : core::option::Option< Types2::Context >, + ) + -> Types2::Formed + { + let mut super_former = super_former.unwrap(); + if super_former.storage.children.is_none() + { + super_former.storage.children = Some( Default::default() ); + } + if let Some( ref mut fields ) = super_former.storage.children + { + former::ContainerAdd::add( fields, former::StoragePreform::preform( substorage ) ); + } + super_former + } +} + +// == end of generated for Parent in context of attribute subform + +include!( "./only_test/subformer_subform_child.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_named.rs b/module/core/former/tests/inc/former_tests/subformer_subform_named.rs new file mode 100644 index 0000000000..e834e8b30d --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_subform_named.rs @@ -0,0 +1,62 @@ +#![ deny( missing_docs ) ] +#![ allow( dead_code ) ] + +use super::*; + +/// Parameter description. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent required for the template. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ derive( Debug, Default, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + #[ subform( name = _child ) ] + children : Vec< Child >, +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, + // Definition::Types : former::FormerDefinitionTypes< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn children( self ) -> &'static str + { + r#" + Scalar setter `children` should not be generated by default if subform is used. + It can only be generated if req + "# + } + + #[ inline( always ) ] + pub fn child( self, name : &str ) -> + ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._children_add + ::< ChildFormer< _ >, _, >() + .name( name ) + } + + // #[ inline( always ) ] + // pub fn _child( self ) -> + // ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + // { + // self._children_add + // ::< < Child as former::EntityToFormer< _ > >::Former, _, >() + // } + +} + +// == begin of generated + +// == end of generated + +include!( "./only_test/subformer_subform_child.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_named_manual.rs b/module/core/former/tests/inc/former_tests/subformer_subform_named_manual.rs new file mode 100644 index 0000000000..c169a74d79 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_subform_named_manual.rs @@ -0,0 +1,72 @@ +#![ deny( missing_docs ) ] +#![ allow( dead_code ) ] + +use super::*; + +/// Parameter description. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent required for the template. +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ derive( Debug, Default, PartialEq, the_module::Former ) ] #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + #[ subform ] + // #[ scalar( setter = false ) ] + children : Vec< Child >, +} + +// == begin of custom + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn child( self, name : &str ) -> + ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._children_add + ::< ChildFormer< _ >, _, >() + .name( name ) + } + + // #[ inline( always ) ] + // pub fn _child( self ) -> + // ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + // { + // self._children_add + // ::< < Child as former::EntityToFormer< _ > >::Former, _, >() + // } + + #[ inline( always ) ] + pub fn _child( self ) -> + < < Vec< Child > as former::Container >::Entry as former::EntityToFormer + < + // ChildFormerDefinition< Self, Self, ParentFormerAddChildrenEnd< Definition > >, + < + < Vec< Child > as former::Container >::Entry as former::EntityToDefinition< Self, Self, ParentFormerAddChildrenEnd< Definition > > + >::Definition, + > + >::Former + { + self._children_add + ::< < < Vec< Child > as former::Container >::Entry as former::EntityToFormer< _ > >::Former, _, >() + } + +} + +// == end of custom + +// == begin of generated for Parent in context of attribute subform + +// == end of generated for Parent in context of attribute subform + +include!( "./only_test/subformer_subform_child.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_setter_off.rs b/module/core/former/tests/inc/former_tests/subformer_subform_setter_off.rs new file mode 100644 index 0000000000..e5a2f9eb61 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_subform_setter_off.rs @@ -0,0 +1,49 @@ +#![ allow( dead_code ) ] + +use super::*; + +/// Child +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent + +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + #[ subform( setter = false ) ] + children : Vec< Child >, +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn children( self ) -> &'static str + { + r#" + Scalar setter `children` should not be generated by default if subform is used. + It can only be generated if req + "# + } + + #[ inline( always ) ] + pub fn children2( self, name : &str ) -> + ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._children_add + ::< ChildFormer< _ >, _, >() + .name( name ) + } + +} + +include!( "./only_test/subformer_subform_children2.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_subform_setter_on.rs b/module/core/former/tests/inc/former_tests/subformer_subform_setter_on.rs new file mode 100644 index 0000000000..29378ff208 --- /dev/null +++ b/module/core/former/tests/inc/former_tests/subformer_subform_setter_on.rs @@ -0,0 +1,44 @@ +#![ allow( dead_code ) ] + +use super::*; + +/// Child +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +pub struct Child +{ + name : String, + data : bool, +} + +/// Parent + +#[ derive( Debug, Default, PartialEq, the_module::Former ) ] +// #[ debug ] +// #[ derive( Debug, Default, PartialEq ) ] +pub struct Parent +{ + // Such parameters switch off generation of front-end subform setter and switch on scalar setter. + // Without explicit scalar_setter( true ) scalar setter is not generated. + #[ subform( setter = false ) ] + #[ scalar( setter = true ) ] + children : Vec< Child >, +} + +impl< Definition > ParentFormer< Definition > +where + Definition : former::FormerDefinition< Storage = < Parent as former::EntityToStorage >::Storage >, +{ + + #[ inline( always ) ] + pub fn children2( self, name : &str ) -> + ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + { + self._children_add + ::< ChildFormer< _ >, _, >() + .name( name ) + } + +} + +include!( "./only_test/subformer_scalar_children.rs" ); +include!( "./only_test/subformer_subform_children2.rs" ); diff --git a/module/core/former/tests/inc/former_tests/subformer_vec.rs b/module/core/former/tests/inc/former_tests/subformer_vec.rs deleted file mode 100644 index 7a7244fece..0000000000 --- a/module/core/former/tests/inc/former_tests/subformer_vec.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![ allow( dead_code ) ] - -use super::*; -#[ allow( unused_imports ) ] -use collection_tools::Vec; - -#[ test ] -fn push() -{ - - // - - let got : Vec< String > = the_module::VectorSubformer::new() - .push( "a" ) - .push( "b" ) - .form(); - let exp = vec! - [ - "a".to_string(), - "b".to_string(), - ]; - a_id!( got, exp ); - -} - -#[ test ] -fn replace() -{ - - let got : Vec< String > = the_module::VectorSubformer::new() - .push( "x" ) - .replace( vec![ "a".to_string(), "b".to_string() ] ) - .form(); - let exp = vec! - [ - "a".to_string(), - "b".to_string(), - ]; - a_id!( got, exp ); - -} diff --git a/module/core/former/tests/inc/former_tests/unsigned_primitive_types.rs b/module/core/former/tests/inc/former_tests/unsigned_primitive_types.rs index 32bb942485..abfbe7d183 100644 --- a/module/core/former/tests/inc/former_tests/unsigned_primitive_types.rs +++ b/module/core/former/tests/inc/former_tests/unsigned_primitive_types.rs @@ -47,91 +47,90 @@ tests_impls! // -// zzz : make it working fn with_u16() { -// #[ derive( Debug, PartialEq, the_module::Former ) ] -// pub struct Counter -// { -// count : u16, -// } -// -// let counter = Counter::former() -// .count( 0 ) -// .form(); -// -// let expected = Counter -// { -// count : 0, -// }; -// -// a_id!( counter, expected ); + #[ derive( Debug, PartialEq, the_module::Former ) ] + pub struct Counter + { + count : u16, + } + + let counter = Counter::former() + .count( 0u16 ) + .form(); + + let expected = Counter + { + count : 0, + }; + + a_id!( counter, expected ); } // fn with_u32() { - // #[ derive( Debug, PartialEq, Former ) ] - // pub struct Counter - // { - // count : u32, - // } - // - // let counter = Counter::former() - // .count( 0 ) - // .form(); - // - // let expected = Counter - // { - // count : 0, - // }; - // - // a_id!( counter, expected ); + #[ derive( Debug, PartialEq, the_module::Former ) ] + pub struct Counter + { + count : u32, + } + + let counter = Counter::former() + .count( 0u32 ) + .form(); + + let expected = Counter + { + count : 0, + }; + + a_id!( counter, expected ); } // fn with_u64() { - // #[ derive( Debug, PartialEq, Former ) ] - // pub struct Counter - // { - // count : u64, - // } - // - // let counter = Counter::former() - // .count( 0 ) - // .form(); - // - // let expected = Counter - // { - // count : 0, - // }; - // - // a_id!( counter, expected ); + #[ derive( Debug, PartialEq, the_module::Former ) ] + pub struct Counter + { + count : u64, + } + + let counter = Counter::former() + .count( 0u64 ) + .form(); + + let expected = Counter + { + count : 0, + }; + + a_id!( counter, expected ); } // fn with_usize() { - // #[ derive( Debug, PartialEq, Former ) ] - // pub struct Counter - // { - // count : usize, - // } - // - // let counter = Counter::former() - // .count( 0 ) - // .form(); - // - // let expected = Counter - // { - // count : 0, - // }; - // - // a_id!( counter, expected ); + #[ derive( Debug, PartialEq, the_module::Former ) ] + pub struct Counter + { + count : usize, + } + + let counter = Counter::former() + .count( 0usize ) + .form(); + + let expected = Counter + { + count : 0, + }; + + a_id!( counter, expected ); } } diff --git a/module/core/former/tests/inc/former_tests/visibility.rs b/module/core/former/tests/inc/former_tests/visibility.rs new file mode 100644 index 0000000000..7df53933ac --- /dev/null +++ b/module/core/former/tests/inc/former_tests/visibility.rs @@ -0,0 +1,25 @@ +//! Structure must be public. +//! Otherwise public trait can't have it as type. + +#[ allow( unused_imports ) ] +use super::*; + +#[ derive( Debug, PartialEq, former::Former ) ] +// #[ debug ] +// #[ derive( Debug, PartialEq ) ] +pub struct Foo +{ + bar : i32, +} + +// == begin of generated + +// == end of generated + +#[ test ] +fn basic() +{ + let got = Foo::former().bar( 13 ).form(); + let exp = Foo { bar : 13 }; + a_id!( got, exp ); +} \ No newline at end of file diff --git a/module/core/former/tests/inc/mod.rs b/module/core/former/tests/inc/mod.rs index 8eff590826..2cb4dab571 100644 --- a/module/core/former/tests/inc/mod.rs +++ b/module/core/former/tests/inc/mod.rs @@ -1,3 +1,5 @@ +// #![ deny( missing_docs ) ] + #[ allow( unused_imports ) ] use super::*; @@ -7,19 +9,35 @@ 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_containers_without_runtime_manual; - mod a_containers_without_runtime; + mod a_primitives; + + mod a_containers_scalar; #[ cfg( not( feature = "no_std" ) ) ] - mod a_containers_with_runtime_manual; + mod a_containers_manual; #[ cfg( not( feature = "no_std" ) ) ] - mod a_containers_with_runtime ; + mod a_containers; mod attribute_default_container; mod attribute_default_primitive; + mod attribute_default_conflict; + mod attribute_storage_with_end; + mod attribute_storage_with_mutator; mod attribute_perform; mod attribute_setter; mod attribute_alias; + mod attribute_feature; mod string_slice_manual; mod string_slice; @@ -27,6 +45,7 @@ mod former_tests 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; @@ -41,21 +60,47 @@ mod former_tests 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_manual; #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] mod subformer_basic; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod subformer_vec; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod subformer_hashset; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - mod subformer_hashmap; + #[ 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_container_setter_off; + #[ cfg( any( not( feature = "no_std" ) ) ) ] + mod subformer_container_named; + #[ cfg( any( not( feature = "no_std" ) ) ) ] + mod subformer_container_custom; #[ cfg( any( not( feature = "no_std" ) ) ) ] - mod subformer_shortcut; + 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_setter_off; + #[ cfg( any( not( feature = "no_std" ) ) ) ] + mod subformer_subform_setter_on; + + #[ cfg( any( not( feature = "no_std" ) ) ) ] + mod subformer_subform_hashmap; + #[ cfg( any( not( feature = "no_std" ) ) ) ] + mod subformer_subform_hashmap_custom; + + #[ cfg( any( not( feature = "no_std" ) ) ) ] + mod subformer_subform_and_container; + #[ cfg( any( not( feature = "no_std" ) ) ) ] + mod subformer_subform_and_container_parametrized; } @@ -96,6 +141,8 @@ only_for_terminal_module! // stable have different information about error // that's why these tests are active only for nightly + + #[ cfg( feature = "derive_former" ) ] #[ test_tools::nightly ] #[ test ] fn former_trybuild() @@ -104,11 +151,25 @@ only_for_terminal_module! println!( "current_dir : {:?}", std::env::current_dir().unwrap() ); let t = test_tools::compiletime::TestCases::new(); - 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" ); + t.compile_fail( "tests/inc/former_tests/compiletime/field_attr_bad.rs" ); + t.compile_fail( "tests/inc/former_tests/compiletime/struct_attr_bad.rs" ); + t.pass( "tests/inc/former_tests/compiletime/hashmap_without_parameter.rs" ); + t.pass( "tests/inc/former_tests/compiletime/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(); - //t.compile_fail( "tests/inc/compiletime/components_component_from_debug.rs" ); + // zzz : make it working test + //t.run( "tests/inc/components_tests/compiletime/components_component_from_debug.rs" ); } diff --git a/module/core/former/tests/inc/only_test/containers_with_runtime.rs b/module/core/former/tests/inc/only_test/containers_with_runtime.rs deleted file mode 100644 index ceeb84abaa..0000000000 --- a/module/core/former/tests/inc/only_test/containers_with_runtime.rs +++ /dev/null @@ -1,264 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; - -// - -tests_impls_optional! -{ - - // - - fn internals() - { - - // test.case( "vector : construction" ); - - let former = Struct1::former(); - a_id!( former.storage.vec_1, None ); - a_id!( former.storage.hashmap_strings_1, None ); - a_id!( former.storage.hashset_strings_1, None ); - a_id!( former.context, None ); - a_id!( print!( "{:?}", former.on_end ), print!( "{:?}", Some( the_module::ReturnFormed ) ) ); - let former2 = Struct1Former::< Struct1, the_module::ReturnFormed >::new(); - a_id!( std::mem::size_of_val( &former ), std::mem::size_of_val( &former2 ) ); - - let command = Struct1::former().form(); - a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_strings_1, hmap!{} ); - a_id!( command.hashset_strings_1, hset![] ); - - let command = Struct1::former().perform(); - a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_strings_1, hmap!{} ); - a_id!( command.hashset_strings_1, hset![] ); - - let command = Struct1::former().end(); - a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_strings_1, hmap!{} ); - a_id!( command.hashset_strings_1, hset![] ); - - } - - // - - fn test_vector() - { - - // test.case( "vector : implicit construction" ); - - let command = Struct1::former() - .vec_1().push( "ghi" ).push( "klm" ).end() - .form() - ; - // dbg!( &command ); - - let expected = Struct1 - { - vec_1 : vec![ "ghi".to_string(), "klm".to_string() ], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{}, - }; - a_id!( command, expected ); - - // test.case( "vector : replace" ); - - let command = Struct1::former() - .vec_1().replace( vec![ "a".to_string(), "bc".to_string(), "def".to_string() ] ).end() - .form(); - let expected = Struct1 - { - vec_1 : vec![ "a".to_string(), "bc".to_string(), "def".to_string() ], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{}, - }; - a_id!( command, expected ); - - let command = Struct1::former() - .vec_1().push( "x" ).replace( vec![ "a".to_string(), "bc".to_string(), "def".to_string() ] ).end() - .form(); - let expected = Struct1 - { - vec_1 : vec![ "a".to_string(), "bc".to_string(), "def".to_string() ], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{}, - }; - a_id!( command, expected ); - - // test.case( "vector : replace and push" ); - - let command = Struct1::former() - .vec_1().replace( vec![ "a".to_string(), "bc".to_string(), "def".to_string() ] ).push( "gh" ).end() - .form(); - // dbg!( &command ); - - let expected = Struct1 - { - vec_1 : vec![ "a".to_string(), "bc".to_string(), "def".to_string(), "gh".to_string() ], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{}, - }; - a_id!( command, expected ); - } - - // - - fn test_hashmap() - { - - // test.case( "implicit construction" ); - - let command = Struct1::former() - .hashmap_strings_1().insert( "k1", "v1" ).insert( "k2", "v2" ).end() - .form() - ; - // dbg!( &command ); - - let expected = Struct1 - { - vec_1 : vec![], - hashmap_strings_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, - hashset_strings_1 : hset!{}, - }; - a_id!( command, expected ); - - // test.case( "replace" ); - - let command = Struct1::former() - .hashmap_strings_1().replace( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ).end() - .form() - ; - let expected = Struct1 - { - vec_1 : vec![], - hashmap_strings_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, - hashset_strings_1 : hset!{}, - }; - a_id!( command, expected ); - - let command = Struct1::former() - .hashmap_strings_1().insert( "x", "v1" ).replace( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ).end() - .form() - ; - let expected = Struct1 - { - vec_1 : vec![], - hashmap_strings_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, - hashset_strings_1 : hset!{}, - }; - a_id!( command, expected ); - - // test.case( "replace and insert" ); - - let command = Struct1::former() - .hashmap_strings_1().replace( hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ).insert( "k3", "v3" ).end() - .form() - ; - // dbg!( &command ); - - let expected = Struct1 - { - vec_1 : vec![], - hashmap_strings_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string(), "k3".to_string() => "v3".to_string() }, - hashset_strings_1 : hset!{}, - }; - a_id!( command, expected ); - } - - // - - fn test_hashset() - { - - // test.case( "implicit construction" ); - - let command = Struct1::former() - .hashset_strings_1().insert( "v1" ).insert( "v2" ).end() - .form() - ; - // dbg!( &command ); - - let expected = Struct1 - { - vec_1 : vec![], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{ "v1".to_string(), "v2".to_string() }, - }; - a_id!( command, expected ); - - // test.case( "replace" ); - - let command = Struct1::former() - .hashset_strings_1().replace( hset!{ "v1".to_string(), "v2".to_string() } ).end() - .form() - ; - let expected = Struct1 - { - vec_1 : vec![], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{ "v1".to_string(), "v2".to_string() }, - }; - a_id!( command, expected ); - - let command = Struct1::former() - .hashset_strings_1().insert( "x" ).replace( hset!{ "v1".to_string(), "v2".to_string() } ).end() - .form() - ; - let expected = Struct1 - { - vec_1 : vec![], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{ "v1".to_string(), "v2".to_string() }, - }; - a_id!( command, expected ); - - // test.case( "replace and insert" ); - - let command = Struct1::former() - .hashset_strings_1().replace( hset!{ "v1".to_string(), "v2".to_string() } ).insert( "v3" ).end() - .form() - ; - // dbg!( &command ); - - let expected = Struct1 - { - vec_1 : vec![], - hashmap_strings_1 : hmap!{}, - hashset_strings_1 : hset!{ "v1".to_string(), "v2".to_string(), "v3".to_string() }, - }; - a_id!( command, expected ); - } - - // - - fn test_complex() - { - - let command = Struct1::former() - .vec_1().push( "ghi" ).push( "klm" ).end() - .hashmap_strings_1().insert( "k1", "v1" ).insert( "k2", "v2" ).end() - .hashset_strings_1().insert( "k1" ).end() - .form(); - // dbg!( &command ); - - let expected = Struct1 - { - vec_1 : vec![ "ghi".to_string(), "klm".to_string() ], - hashmap_strings_1 : hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, - hashset_strings_1 : hset!{ "k1".to_string() }, - }; - a_id!( command, expected ); - - } - -} - -// - -tests_index! -{ - internals, - test_vector, - test_hashmap, - test_hashset, - test_complex, -} diff --git a/module/core/former/tests/inc/only_test/string_slice.rs b/module/core/former/tests/inc/only_test/string_slice.rs deleted file mode 100644 index cd07841dd3..0000000000 --- a/module/core/former/tests/inc/only_test/string_slice.rs +++ /dev/null @@ -1,51 +0,0 @@ -#[ allow( unused_imports ) ] -use super::*; -#[ allow( unused_imports ) ] -use test_tools::exposed::*; - -// - -tests_impls! -{ - fn test_complex() - { - // test.case( "default" ); - - let command = Struct1::former().form(); - let expected = Struct1 - { - string_slice_1 : "", - }; - a_id!( command, expected ); - - // test.case( "from slice" ); - - let command = Struct1::former() - .string_slice_1( "abc" ) - .form(); - let expected = Struct1 - { - string_slice_1 : "abc", - }; - a_id!( command, expected ); - -// // test.case( "from string" ); -// -// let command = Struct1::former() -// .string_slice_1( "abc".to_string() ) -// .form(); -// let expected = Struct1 -// { -// string_slice_1 : "abc", -// }; -// a_id!( command, expected ); - - } -} - -// - -tests_index! -{ - test_complex, -} diff --git a/module/core/former/tests/inc/only_test/with_field_under_feature.rs b/module/core/former/tests/inc/only_test/with_field_under_feature.rs deleted file mode 100644 index 2623f7579b..0000000000 --- a/module/core/former/tests/inc/only_test/with_field_under_feature.rs +++ /dev/null @@ -1,9 +0,0 @@ -// xxx : need to fix -// #[ derive( Former ) ] -// struct Foo -// { -// #[ cfg( feature = "baz" ) ] -// bar : i32, -// } - -// error => Unknown attribute #[cfg(feature = "baz")] \ No newline at end of file diff --git a/module/core/former_meta/Cargo.toml b/module/core/former_meta/Cargo.toml index d5b80364a1..4bb60c3283 100644 --- a/module/core/former_meta/Cargo.toml +++ b/module/core/former_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former_meta" -version = "0.14.0" +version = "1.0.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -46,7 +46,7 @@ full = [ ] enabled = [ "macro_tools/enabled", "iter_tools/enabled" ] -derive_former = [] +derive_former = [ "convert_case" ] derive_components = [] derive_component_assign = [] derive_components_assign = [ "derive_components", "derive_component_assign", "convert_case" ] diff --git a/module/core/former_meta/src/derive/component_assign.rs b/module/core/former_meta/src/component/component_assign.rs similarity index 89% rename from module/core/former_meta/src/derive/component_assign.rs rename to module/core/former_meta/src/component/component_assign.rs index 1120c9da64..4677f27b0f 100644 --- a/module/core/former_meta/src/derive/component_assign.rs +++ b/module/core/former_meta/src/component/component_assign.rs @@ -23,9 +23,15 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> Result< proc_macro if has_debug { - diag::debug_report_print( "derive : ComponentAssign", original_input, &result ); + let about = format!( "derive : ComponentAssign\nstructure : {0}", &parsed.item_name ); + diag::report_print( about, &original_input, &result ); } + // if has_debug + // { + // diag::report_print( "derive : ComponentAssign", original_input, &result ); + // } + Ok( result ) } diff --git a/module/core/former_meta/src/derive/component_from.rs b/module/core/former_meta/src/component/component_from.rs similarity index 87% rename from module/core/former_meta/src/derive/component_from.rs rename to module/core/former_meta/src/component/component_from.rs index dda6740aa5..994206b996 100644 --- a/module/core/former_meta/src/derive/component_from.rs +++ b/module/core/former_meta/src/component/component_from.rs @@ -22,9 +22,15 @@ pub fn component_from( input : proc_macro::TokenStream ) -> Result< proc_macro2: if has_debug { - diag::debug_report_print( "derive : ComponentFrom", original_input, &result ); + let about = format!( "derive : ComponentFrom\nstructure : {0}", &parsed.item_name ); + diag::report_print( about, &original_input, &result ); } + // if has_debug + // { + // diag::report_print( "derive : ComponentFrom", original_input, &result ); + // } + Ok( result ) } diff --git a/module/core/former_meta/src/derive/components_assign.rs b/module/core/former_meta/src/component/components_assign.rs similarity index 92% rename from module/core/former_meta/src/derive/components_assign.rs rename to module/core/former_meta/src/component/components_assign.rs index ac4ecac166..bcf5cc0bc9 100644 --- a/module/core/former_meta/src/derive/components_assign.rs +++ b/module/core/former_meta/src/component/components_assign.rs @@ -16,7 +16,7 @@ pub fn components_assign( input : proc_macro::TokenStream ) -> Result< proc_macr let has_debug = attr::has_debug( parsed.item.attrs.iter() )?; // name - let item_name = parsed.item_name; + let item_name = &parsed.item_name; let trait_name = format!( "{}ComponentsAssign", item_name ); let trait_ident = syn::Ident::new( &trait_name, item_name.span() ); let method_name = format!( "{}_assign", item_name.to_string().to_case( Case::Snake ) ); @@ -68,8 +68,15 @@ pub fn components_assign( input : proc_macro::TokenStream ) -> Result< proc_macr if has_debug { - diag::debug_report_print( "derive : ComponentsAssign", original_input, &result ); + let about = format!( "derive : ComponentsAssign\nstructure : {0}", item_name ); + diag::report_print( about, &original_input, &result ); } + + // if has_debug + // { + // diag::report_print( "derive : ComponentsAssign", original_input, &result ); + // } + Ok( result ) } diff --git a/module/core/former_meta/src/derive/from_components.rs b/module/core/former_meta/src/component/from_components.rs similarity index 93% rename from module/core/former_meta/src/derive/from_components.rs rename to module/core/former_meta/src/component/from_components.rs index b7d1d9f58f..62ae0615a9 100644 --- a/module/core/former_meta/src/derive/from_components.rs +++ b/module/core/former_meta/src/component/from_components.rs @@ -69,8 +69,15 @@ pub fn from_components( input : proc_macro::TokenStream ) -> Result< proc_macro2 if has_debug { - diag::debug_report_print( "derive : FromComponents", original_input, &result ); + let about = format!( "derive : FromComponents\nstructure : {0}", &parsed.item_name ); + diag::report_print( about, &original_input, &result ); } + + // if has_debug + // { + // diag::report_print( "derive : FromComponents", original_input, &result ); + // } + Ok( result.into() ) } diff --git a/module/core/former_meta/src/derive/former.rs b/module/core/former_meta/src/derive/former.rs deleted file mode 100644 index 5ca9feb66c..0000000000 --- a/module/core/former_meta/src/derive/former.rs +++ /dev/null @@ -1,1095 +0,0 @@ - -use super::*; -use iter_tools::{ Itertools, process_results }; -use macro_tools::{ attr, diag, generics, container_kind, typ, Result }; -use proc_macro2::TokenStream; - -/// -/// Descriptor of a field. -/// - -#[ allow( dead_code ) ] -struct FormerField< 'a > -{ - pub attrs : Attributes, - pub vis : &'a syn::Visibility, - pub ident : &'a syn::Ident, - pub colon_token : &'a Option< syn::token::Colon >, - pub ty : &'a syn::Type, - pub non_optional_ty : &'a syn::Type, - pub is_optional : bool, - pub of_type : container_kind::ContainerKind, -} - -/// -/// Attributes of the field. -/// - -struct Attributes -{ - default : Option< AttributeDefault >, - setter : Option< AttributeSetter >, - // #[ allow( dead_code ) ] - subformer : Option< AttributeFormer >, - alias : Option< AttributeAlias >, -} - -impl Attributes -{ - fn parse( attributes : & Vec< syn::Attribute > ) -> Result< Self > - { - let mut default = None; - let mut setter = None; - let mut subformer = None; - let mut alias = None; - for attr in attributes - { - 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_str = format!( "{}", key_ident ); - match key_str.as_ref() - { - "default" => - { - match attr.meta - { - syn::Meta::List( ref meta_list ) => - { - default.replace( syn::parse2::< AttributeDefault >( meta_list.tokens.clone() )? ); - }, - _ => return_syn_err!( attr, "Expects an attribute of format #[ attribute( val ) ], but got:\n {}", qt!{ #attr } ), - } - } - "setter" => - { - match attr.meta - { - syn::Meta::List( ref meta_list ) => - { - setter.replace( syn::parse2::< AttributeSetter >( meta_list.tokens.clone() )? ); - }, - _ => return_syn_err!( attr, "Expects an attribute of format #[ attribute( val ) ], but got:\n {}", qt!{ #attr } ), - } - // let attr_setter = syn::parse2::< AttributeSetter >( attr.tokens.clone() )?; - // setter.replace( attr_setter ); - } - "subformer" => - { - match attr.meta - { - syn::Meta::List( ref meta_list ) => - { - subformer.replace( syn::parse2::< AttributeFormer >( meta_list.tokens.clone() )? ); - }, - _ => return_syn_err!( attr, "Expects an attribute of format #[ attribute( val ) ], but got:\n {}", qt!{ #attr } ), - } - // let attr_former = syn::parse2::< AttributeFormer >( attr.tokens.clone() )?; - // subformer.replace( attr_former ); - } - "alias" => - { - match attr.meta - { - syn::Meta::List( ref meta_list ) => - { - alias.replace( syn::parse2::< AttributeAlias >( meta_list.tokens.clone() )? ); - }, - _ => return_syn_err!( attr, "Expects an attribute of format #[ attribute( val ) ], but got:\n {}", qt!{ #attr } ), - } - // let attr_alias = syn::parse2::< AttributeAlias >( attr.tokens.clone() )?; - // alias.replace( attr_alias ); - } - "doc" => - { - } - _ => - { - return Err( syn_err!( attr, "Unknown attribute {}", qt!{ #attr } ) ); - } - } - } - - Ok( Attributes { default, setter, subformer, alias } ) - } -} - -/// -/// Attribute to hold information about method to call after form. -/// -/// `#[ perform( fn after1< 'a >() -> Option< &'a str > ) ]` -/// - -#[ allow( dead_code ) ] -struct AttributeFormAfter -{ - // paren_token : syn::token::Paren, - signature : syn::Signature, -} - -impl syn::parse::Parse for AttributeFormAfter -{ - 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()?, - }) - } -} - -/// -/// Attribute to hold information about default value. -/// -/// `#[ default( 13 ) ]` -/// - -#[ allow( dead_code ) ] -struct AttributeDefault -{ - // eq_token : syn::Token!{ = }, - // paren_token : syn::token::Paren, - expr : syn::Expr, -} - -impl syn::parse::Parse for AttributeDefault -{ - fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > - { - // let input2; - Ok( Self - { - // paren_token : syn::parenthesized!( input2 in input ), - // eq_token : input.parse()?, - // expr : input2.parse()?, - expr : input.parse()?, - }) - } -} - -/// -/// Attribute to enable/disable setter generation. -/// -/// `#[ setter( false ) ]` -/// - -#[ allow( dead_code ) ] -struct AttributeSetter -{ - // paren_token : syn::token::Paren, - condition : syn::LitBool, -} - -impl syn::parse::Parse for AttributeSetter -{ - fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > - { - // let input2; - Ok( Self - { - // paren_token : syn::parenthesized!( input2 in input ), - // condition : input2.parse()?, - condition : input.parse()?, - }) - } -} - -/// -/// Attribute to enable/disable former 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` -/// -/// `#[ subformer( former::VectorSubformer ) ]` -/// - -#[ allow( dead_code ) ] -struct AttributeFormer -{ - // paren_token : syn::token::Paren, - expr : syn::Type, -} - -impl syn::parse::Parse for AttributeFormer -{ - fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > - { - // let input2; - Ok( Self - { - // paren_token : syn::parenthesized!( input2 in input ), - // expr : input2.parse()?, - expr : input.parse()?, - }) - } -} - -/// -/// Attribute to create alias. -/// -/// `#[ alias( name ) ]` -/// - -#[ allow( dead_code ) ] -struct AttributeAlias -{ - // paren_token : syn::token::Paren, - alias : syn::Ident, -} - -impl syn::parse::Parse for AttributeAlias -{ - fn parse( input : syn::parse::ParseStream< '_ > ) -> Result< Self > - { - // let input2; - Ok( Self - { - // paren_token : syn::parenthesized!( input2 in input ), - // alias : input2.parse()?, - alias : input.parse()?, - }) - } -} - -/// -/// Is type under Option. -/// - -fn is_optional( ty : &syn::Type ) -> bool -{ - typ::type_rightmost( ty ) == Some( "Option".to_string() ) -} - -/// -/// Extract the first parameter of the type if such exist. -/// - -fn parameter_internal_first( ty : &syn::Type ) -> Result< &syn::Type > -{ - typ::type_parameters( ty, 0 ..= 0 ) - .first() - .copied() - .ok_or_else( || syn_err!( ty, "Expects at least one parameter here:\n {}", qt!{ #ty } ) ) -} - -/// -/// Generate fields for initializer of a struct setting each field to `None`. -/// -/// Used for initializing a Container, where on initialization all fields are None. User can alter them through builder pattern -/// -/// ### Basic use-case. of output -/// -/// ```ignore -/// int_1 : core::option::Option::None, -/// string_1 : core::option::Option::None, -/// int_optional_1 : core::option::Option::None, -/// ``` -/// - -#[ inline( always ) ] -fn field_none_map( field : &FormerField< '_ > ) -> TokenStream -{ - let ident = Some( field.ident.clone() ); - let tokens = qt! { ::core::option::Option::None }; - let ty2 : syn::Type = syn::parse2( tokens ).unwrap(); - - qt! - { - #ident : #ty2 - } -} - -/// -/// Generate field of the former for a field of the structure -/// -/// Used to generate a Container -/// -/// ### Basic use-case. of output -/// -/// ```ignore -/// pub int_1 : core::option::Option< i32 >, -/// pub string_1 : core::option::Option< String >, -/// pub int_optional_1 : core::option::Option< i32 >, -/// pub string_optional_1 : core::option::Option< String >, -/// ``` -/// - -#[ inline( always ) ] -fn field_optional_map( field : &FormerField< '_ > ) -> TokenStream -{ - let ident = Some( field.ident.clone() ); - let ty = field.ty.clone(); - - // let ty2 = if is_optional( &ty ) - let ty2 = if field.is_optional - { - qt! { #ty } - } - else - { - qt! { ::core::option::Option< #ty > } - }; - - qt! - { - pub #ident : #ty2 - } - -} - -/// -/// Generate code converting a field of the former to the field of the structure. -/// -/// In simple terms, used on `form()` call to unwrap contained values from the former's storage. -/// Will try to use default values if no values supplied by the former and the type implements `Default` trait. -/// -/// ### Generated code will look similar to this : -/// -/// ```ignore -/// let int_1 : i32 = if self.storage.int_1.is_some() -/// { -/// // if int_1 is optional -/// Some( self.storage.int_1.take().unwrap() ) -/// -/// // if int_1 isn't optional -/// self.storage.int_1.take().unwrap() -/// } -/// else -/// { -/// // if int_1 is optional and has default -/// Some( i32::default().into() ) -/// -/// // if int_1 is optional and doesn't have default -/// None -/// -/// // if int_1 isn't optional and has default -/// i32::default().into() -/// -/// // if int_1 isn't optional and hasn't default -/// panic!( "Field 'int_1' isn't initialized" ) -/// }; -/// ``` -/// - -#[ inline( always ) ] -fn field_form_map( field : &FormerField< '_ > ) -> Result< TokenStream > -{ - let ident = field.ident; - let ty = field.ty; - let default = field.attrs.default.as_ref() - .map( | attr_default | &attr_default.expr ); - - let tokens = if field.is_optional - { - - let _else = match default - { - None => - { - qt! - { - ::core::option::Option::None - } - } - - Some( default_val ) => - { - qt! - { - ::core::option::Option::Some( ( #default_val ).into() ) - } - } - }; - - qt! - { - let #ident = if self.storage.#ident.is_some() - { - ::core::option::Option::Some( self.storage.#ident.take().unwrap() ) - } - else - { - #_else - }; - } - - } - else - { - - let _else = match default - { - None => - { - let panic_msg = format!( "Field '{}' isn't initialized", ident ); - qt! - { - { - // By hardly utilizing deref coercion, we achieve conditional trait implementation - trait MaybeDefault< T > - { - fn maybe_default( self : &Self ) -> T { panic!( #panic_msg ) } - } - - // Panic on non-`Default` types - impl< T > MaybeDefault< T > - for &::core::marker::PhantomData< T > - {} - - // Return default value on `Default`` types - impl< T > MaybeDefault< T > - for ::core::marker::PhantomData< T > - where T : ::core::default::Default, - { - fn maybe_default( self : &Self ) -> T - { - T::default() - } - } - - // default if `impl Default`, otherwise - panic - ( &::core::marker::PhantomData::< #ty > ).maybe_default() - } - } - } - Some( default_val ) => - { - qt! - { - ( #default_val ).into() - } - } - }; - - qt! - { - let #ident = if self.storage.#ident.is_some() - { - self.storage.#ident.take().unwrap() - } - else - { - #_else - }; - } - - }; - - Ok( tokens ) -} - -/// -/// Extract name of a field out. -/// - -#[ inline( always ) ] -fn field_name_map( field : &FormerField< '_ > ) -> syn::Ident -{ - field.ident.clone() -} - -/// -/// Generate a former setter for the field. -/// -/// If aliases provided, also generate aliases -/// -/// # Example of output -/// ```ignore -/// #[ doc = "Setter for the 'int_1' field." ] -/// #[ inline ] -/// pub fn int_1< Src >( mut self, src : Src ) -> Self -/// where -/// Src : ::core::convert::Into< i32 >, -/// { -/// debug_assert!( self.int_1.is_none() ); -/// self.storage.int_1 = ::core::option::Option::Some( src.into() ); -/// self -/// } -/// -/// /// #[ doc = "Setter for the 'int_1' field." ] -/// #[ inline ] -/// pub fn int_1_alias< Src >( mut self, src : Src ) -> Self -/// where -/// Src : ::core::convert::Into< i32 >, -/// { -/// debug_assert!( self.int_1.is_none() ); -/// self.storage.int_1 = ::core::option::Option::Some( src.into() ); -/// self -/// } -/// ``` - -#[ inline ] -fn field_setter_map( field : &FormerField< '_ > ) -> Result< TokenStream > -{ - let ident = &field.ident; - - if let Some( setter_attr ) = &field.attrs.setter - { - if !setter_attr.condition.value() - { - return Ok( qt!{ } ); - } - } - - let non_optional_ty = &field.non_optional_ty; - // Either subformer or ordinary setter. - let setter_tokens = if let Some( subformer_ty ) = &field.attrs.subformer - { - subformer_field_setter( ident, ident, non_optional_ty, &subformer_ty.expr ) - // field_setter( ident, ident, non_optional_ty ) - } - else - { - field_setter( ident, ident, non_optional_ty ) - }; - - let r = if let Some( alias_attr ) = &field.attrs.alias - { - let alias_tokens = field_setter( ident, &alias_attr.alias, non_optional_ty ); - let token = qt! - { - #setter_tokens - #alias_tokens - }; - Ok( token ) - } - else - { - Ok( setter_tokens ) - }; - - // tree_print!( r.as_ref().unwrap() ); - r -} - -/// -/// Generate a single setter for the 'field_ident' with the 'setter_name' name. -/// -/// Used as a helper function for field_setter_map(), which generates all alias setters -/// -/// # Example of output -/// ```ignore -/// #[ doc = "Setter for the 'int_1' field." ] -/// #[ inline ] -/// pub fn int_1< Src >( mut self, src : Src ) -> Self -/// where -/// Src : ::core::convert::Into< i32 >, -/// { -/// debug_assert!( self.int_1.is_none() ); -/// self.storage.int_1 = ::core::option::Option::Some( src.into() ); -/// self -/// } -/// ``` - -#[ inline ] -fn field_setter -( - field_ident : &syn::Ident, - setter_name : &syn::Ident, - non_optional_type : &syn::Type, -) --> TokenStream -{ - let doc = format! - ( - "Setter for the '{}' field.", - field_ident, - ); - - qt! - { - #[ doc = #doc ] - #[ inline ] - pub fn #setter_name< Src >( mut self, src : Src ) -> Self - where Src : ::core::convert::Into< #non_optional_type >, - { - debug_assert!( self.storage.#field_ident.is_none() ); - self.storage.#field_ident = ::core::option::Option::Some( src.into() ); - self - } - } -} - -/// -/// Generate a sub-former setter for the 'field_ident' with the 'setter_name' name. -/// -/// # Example of generated code -/// -/// ```ignore -/// pub fn hashmap_strings_1( mut self ) -> former::HashMapSubformer -/// < -/// String, -/// String, -/// std::collections::HashMap< String, String >, -/// Struct1Former, -/// impl Fn( std::collections::HashMap< String, String >, core::option::Option< Self > ) -> Self -/// > -/// { -/// let formed = self.hashmap_strings_1.take(); -/// let on_end = | formed : std::collections::HashMap< String, String >, mut former : core::option::Option< Self > | -> Self -/// { -/// former.hashmap_strings_1 = Some( formed ); -/// former -/// }; -/// former::HashMapSubformer::begin( formed, self, on_end ) -/// } -/// ``` - -#[ inline ] -fn subformer_field_setter -( - field_ident : &syn::Ident, - setter_name : &syn::Ident, - non_optional_type : &syn::Type, - subformer_type : &syn::Type, -) --> TokenStream -{ - let doc = format! - ( - "Subformer setter for the '{}' field.", - field_ident - ); - - // tree_print!( non_optional_type ); - // code_print!( non_optional_type ); - let params = typ::type_parameters( &non_optional_type, .. ); - // params.iter().for_each( | e | println!( "{}", qt!( #e ) ) ); - - qt! - { - #[ doc = #doc ] - #[ inline ] - pub fn #setter_name( mut self ) -> #subformer_type - < - #( #params, )* - #non_optional_type, - Self, - impl Fn( #non_optional_type, core::option::Option< Self > ) -> Self, - > - { - let formed = self.storage.#setter_name.take(); - let on_end = | formed : #non_optional_type, former : core::option::Option< Self > | -> Self - { - let mut former = former.unwrap(); - former.storage.#setter_name = Some( formed ); - former - }; - #subformer_type::begin( formed, Some( self ), on_end ) - } - } - -} - -/// -/// Generate documentation for the former. -/// - -fn doc_generate( name_ident : &syn::Ident ) -> ( String, String ) -{ - - let doc_former_mod = format! - ( -r#" Implementation of former for [{}]. -"#, - name_ident - ); - - let doc_example1 = -r#" -use former::Former; -#[ derive( Former ) ] -pub struct Struct1 -{ - #[default( 31 ) ] - field1 : i32, -} -"#; - - let doc_former_struct = format! - ( -r#" Object to form [{}]. If field's values is not set then default value of the field is set. - -For specifying custom default value use attribute `default`. For example: -``` -{} -``` -"#, - name_ident, doc_example1 - ); - - ( doc_former_mod, doc_former_struct ) -} - -// - -/// -/// Generate parts, used for generating `perform()`` method. -/// -/// Similar to `form()`, but will also invoke function from `perform` attribute, if specified. -/// -/// # Example of returned tokens : -/// -/// ## perform : -/// return result; -/// -/// ## perform_output : -/// < T : ::core::default::Default > -/// -/// ## perform_generics : -/// Vec< T > - -pub fn performer< 'a > -( - name_ident : &syn::Ident, - generics_ty : &syn::TypeGenerics< '_ >, - attrs : impl Iterator< Item = &'a syn::Attribute >, -) --> Result< ( TokenStream, TokenStream, TokenStream ) > -{ - - let mut perform = qt! - { - return result; - }; - let mut perform_output = qt!{ #name_ident #generics_ty }; - let mut perform_generics = qt!{}; - for attr in attrs - { - if let Some( ident ) = attr.path().get_ident() - { - let ident_string = format!( "{}", ident ); - if ident_string == "perform" - { - match attr.meta - { - syn::Meta::List( ref meta_list ) => - { - // default.replace( syn::parse2::< AttributeDefault >( meta_list.tokens.clone() )? ); - // let attr_perform = syn::parse2::< AttributeFormAfter >( attr.tokens.clone() )?; - let attr_perform = syn::parse2::< AttributeFormAfter >( meta_list.tokens.clone() )?; - let signature = &attr_perform.signature; - let generics = &signature.generics; - perform_generics = qt!{ #generics }; - let perform_ident = &signature.ident; - let output = &signature.output; - if let syn::ReturnType::Type( _, boxed_type ) = output - { - perform_output = qt!{ #boxed_type }; - } - perform = qt! - { - return result.#perform_ident(); - }; - }, - _ => return_syn_err!( attr, "Expects an attribute of format #[ attribute( val ) ], but got:\n {}", qt!{ #attr } ), - } - } - } - else - { - return_syn_err!( "Unknown structure attribute:\n{}", qt!{ attr } ); - } - } - - Ok( ( perform, perform_output, perform_generics ) ) -} - -// - -/// -/// Generate the whole Former ecosystem -/// -/// Output examples can be found in [docs to former crate](https://docs.rs/former/latest/former/) -/// - -pub fn former( input : proc_macro::TokenStream ) -> Result< TokenStream > -{ - - let original_input = input.clone(); - let ast = match syn::parse::< syn::DeriveInput >( input ) - { - Ok( syntax_tree ) => syntax_tree, - Err( err ) => return Err( err ), - }; - let has_debug = attr::has_debug( ast.attrs.iter() )?; - let example_of_custom_setter = false; - - - /* names */ - - let name_ident = &ast.ident; - let former_name = format!( "{}Former", name_ident ); - let former_name_ident = syn::Ident::new( &former_name, name_ident.span() ); - let former_storage_name = format!( "{}FormerStorage", name_ident ); - let former_storage_name_ident = syn::Ident::new( &former_storage_name, name_ident.span() ); - - /* generic parameters */ - - let generics = &ast.generics; - let ( generics_impl, generics_ty, generics_where ) = generics.split_for_impl(); - let _generics_params = generics::params_names( generics ).params; - let generics_params = if _generics_params.len() == 0 - { - qt!{} - } - else - { - qt!{ #_generics_params, } - }; - - // add embedded generic parameters - let mut extra_generics : syn::Generics = parse_quote! - { - < __FormerContext = #name_ident #generics_ty, __FormerEnd = former::ReturnFormed > - }; - extra_generics.where_clause = parse_quote! - { - where __FormerEnd : former::FormingEnd< #name_ident #generics_ty, __FormerContext >, - }; - // xxx : write helper to fix bug with where - let generics_of_former = generics::merge( &generics, &extra_generics ); - let ( generics_of_former_impl, generics_of_former_ty, generics_of_former_where ) = generics_of_former.split_for_impl(); - let generics_of_former_with_defaults = generics_of_former.params.clone(); - // macro_tools::code_print!( generics_of_former_with_defaults ); - // macro_tools::code_print!( extra_generics ); - - /* structure attribute */ - - let ( perform, perform_output, perform_generics ) = performer - ( - &name_ident, - &generics_ty, - ast.attrs.iter(), - )?; - - /* */ - - 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_err!( ast, "Unknown format of data, expected syn::Fields::Named( ref fields_named )\n {}", qt!{ #ast } ) ), - }, - _ => return Err( syn_err!( ast, "Unknown format of data, expected syn::Data::Struct( ref data_struct )\n {}", qt!{ #ast } ) ), - }; - - let former_fields : Vec< Result< FormerField< '_ > > > = fields.iter().map( | field | - { - let attrs = Attributes::parse( &field.attrs )?; - let vis = &field.vis; - let ident = field.ident.as_ref() - .ok_or_else( || syn_err!( field, "Expected that each field has key, but some does not:\n {}", qt!{ #field } ) )?; - let colon_token = &field.colon_token; - let ty = &field.ty; - let is_optional = is_optional( ty ); - let of_type = container_kind::of_optional( ty ).0; - let non_optional_ty : &syn::Type = if is_optional { parameter_internal_first( ty )? } else { ty }; - let former_field = FormerField { attrs, vis, ident, colon_token, ty, non_optional_ty, is_optional, of_type }; - Ok( former_field ) - }).collect(); - - let former_fields : Vec< _ > = process_results( former_fields, | iter | iter.collect() )?; - - let ( fields_none, fields_optional, fields_form, fields_names, fields_setter ) - : ( Vec< _ >, Vec< _ >, Vec< _ >, Vec< _ >, Vec< _ > ) - = former_fields.iter().map( | former_field | - {( - field_none_map( former_field ), - field_optional_map( former_field ), - field_form_map( former_field ), - field_name_map( former_field ), - field_setter_map( former_field ), - )}).multiunzip(); - - let ( _doc_former_mod, doc_former_struct ) = doc_generate( name_ident ); - let fields_setter : Vec< _ > = process_results( fields_setter, | iter | iter.collect() )?; - let fields_form : Vec< _ > = process_results( fields_form, | iter | iter.collect() )?; - - let result = qt! - { - - #[ automatically_derived ] - impl #generics_impl #name_ident #generics_ty - #generics_where - { - /// - /// Make former, variation of builder pattern to form structure defining values of fields step by step. - /// - #[ inline( always ) ] - pub fn former() -> #former_name_ident < #generics_params #name_ident #generics_ty, former::ReturnFormed > - { - #former_name_ident :: < #generics_params #name_ident #generics_ty, former::ReturnFormed > :: new() - } - } - - // xxx : rename to storage - #[ doc = "Container of a corresponding former." ] - pub struct #former_storage_name_ident #generics_ty - #generics_where - { - #( - /// A field - #fields_optional, - )* - } - - impl #generics_impl ::core::default::Default for #former_storage_name_ident #generics_ty - #generics_where - { - - #[ inline( always ) ] - fn default() -> Self - { - Self - { - #( #fields_none, )* - } - } - - } - - #[ doc = #doc_former_struct ] - #[ automatically_derived ] - pub struct #former_name_ident < #generics_of_former_with_defaults > - #generics_of_former_where - { - storage : #former_storage_name_ident #generics_ty, - context : core::option::Option< __FormerContext >, - on_end : core::option::Option< __FormerEnd >, - } - - #[ automatically_derived ] - impl #generics_of_former_impl #former_name_ident #generics_of_former_ty - #generics_of_former_where - { - - /// - /// Finish setting options and return formed entity. - /// - /// `perform` has no effect on method `form`, but change behavior and returned type of method `perform`. - /// - #[ inline( always ) ] - pub fn form( mut self ) -> #name_ident #generics_ty - { - #( #fields_form )* - let result = #name_ident - { - #( #fields_names, )* - }; - return result; - } - - /// - /// Finish setting options and call perform on formed entity. - /// - /// If `perform` defined then associated method is called and its result returned instead of entity. - /// For example `perform()` of structure with : `#[ perform( fn after1() -> &str > )` returns `&str`. - /// - #[ inline( always ) ] - pub fn perform #perform_generics ( self ) -> #perform_output - { - let result = self.form(); - #perform - } - - /// - /// Begin the process of forming. Expects context of forming to return it after forming. - /// - #[ inline( always ) ] - pub fn begin - ( - mut storage : core::option::Option< #former_storage_name_ident #generics_ty >, - context : core::option::Option< __FormerContext >, - on_end : __FormerEnd, - ) -> Self - { - if storage.is_none() - { - storage = Some( ::core::default::Default::default() ); - } - Self - { - storage : storage.unwrap(), - context : context, - on_end : ::core::option::Option::Some( on_end ), - } - } - - /// - /// End the process of forming returning original context of forming. - /// - #[ inline( always ) ] - pub fn end( mut self ) -> __FormerContext - { - let on_end = self.on_end.take().unwrap(); - let context = self.context.take(); - let storage = self.form(); - on_end.call( storage, context ) - } - - #( - #fields_setter - )* - - } - - #[ automatically_derived ] - impl #generics_impl #former_name_ident < #generics_params #name_ident #generics_ty, former::ReturnFormed > - #generics_where - { - - /// - /// Construct new instance of former with default parameters. - /// - #[ inline( always ) ] - pub fn new() -> Self - { - // #former_name_ident :: < #generics_params #name_ident #generics_ty, former::ReturnFormed > :: begin - Self :: begin - ( - None, - None, - former::ReturnFormed, - ) - } - - } - - }; - - if has_debug - { - diag::debug_report_print( "derive : Former", original_input, &result ); - } - - // xxx : implement - if example_of_custom_setter - { - let _example = -r#" -impl< Context, End > UserProfileFormer< Context, End > -where - End : former::FormingEnd< UserProfile, Context >, -{ - pub fn age< Src >( mut self, src : Src ) -> Self - where - Src : Into< i32 >, - { - debug_assert!( self.age.is_none() ); - self.storage.age = ::core::option::Option::Some( src.into() ); - self - } -} -"#; - } - - Ok( result ) -} - -// xxx : explain concept of Storage diff --git a/module/core/former_meta/src/derive_former.rs b/module/core/former_meta/src/derive_former.rs new file mode 100644 index 0000000000..92beeefef0 --- /dev/null +++ b/module/core/former_meta/src/derive_former.rs @@ -0,0 +1,776 @@ + +use super::*; +use iter_tools::{ Itertools, process_results }; +use macro_tools::{ attr, diag, generic_params, generic_args, typ, derive, Result }; +use proc_macro2::TokenStream; + +// qqq : implement interfaces for other containers + +mod field; +use field::*; +mod field_attrs; +use field_attrs::*; +mod struct_attrs; +use struct_attrs::*; + +/// Generates the code for implementing the `FormerMutator` trait for a specified former definition type. +/// +/// This function generate code that implements the `FormerMutator` trait based on the given +/// former definition types and their associated generics. The `FormerMutator` trait provides the +/// functionality to mutate the storage and context of an entity just before its formation process +/// completes. This is particularly useful for performing final adjustments or validations on the data +/// before the entity is fully constructed. +/// +/// # Example +/// +/// Below is an example of how the generated code might look: +/// +/// ```rust, ignore +/// impl< Context, Formed > former::FormerMutator +/// for Struct1FormerDefinitionTypes< Context, Formed > +/// { +/// /// Mutates the context and storage of the entity just before the formation process completes. +/// #[ inline ] +/// fn form_mutation( storage : &mut Self::Storage, _context : &mut ::core::option::Option< Self::Context > ) +/// { +/// storage.a.get_or_insert_with( Default::default ); +/// storage.b.get_or_insert_with( Default::default ); +/// storage.c = Some( format!( "{:?} - {}", storage.a.unwrap(), storage.b.as_ref().unwrap() ) ); +/// } +/// } +/// ``` +/// + +pub fn mutator +( + mutator : &AttributeMutator, + former_definition_types : &syn::Ident, + former_definition_types_generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + former_definition_types_generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + former_definition_types_generics_where : &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, +) +-> Result< TokenStream > +{ + let former_mutator_code = if mutator.custom + { + qt!{} + } + else + { + qt! + { + impl< #former_definition_types_generics_impl > former::FormerMutator + for #former_definition_types < #former_definition_types_generics_ty > + where + #former_definition_types_generics_where + { + } + } + }; + + if mutator.hint + { + let hint = format! + ( + r#" += Example of custom mutator + +impl< {} > former::FormerMutator +for {} < {} > +where + {} +{{ + /// Mutates the context and storage of the entity just before the formation process completes. + #[ inline ] + fn form_mutation( storage : &mut Self::Storage, context : &mut Option< Self::Context > ) + {{ + }} +}} + "#, + format!( "{}", qt!{ #former_definition_types_generics_impl } ), + former_definition_types, + format!( "{}", qt!{ #former_definition_types_generics_ty } ), + format!( "{}", qt!{ #former_definition_types_generics_where } ), + ); + println!( "{hint}" ); + }; + + Ok( former_mutator_code ) +} + +/// +/// Generate documentation for the former. +/// + +fn doc_generate( stru : &syn::Ident ) -> ( String, String ) +{ + + let doc_former_mod = format! + ( +r#" Implementation of former for [{}]. +"#, + stru + ); + + let doc_former_struct = format! + ( +r#" +Structure to form [{}]. Represents a forming entity designed to construct objects through a builder pattern. + +This structure holds temporary storage and context during the formation process and +utilizes a defined end strategy to finalize the object creation. +"#, + stru + ); + + ( doc_former_mod, doc_former_struct ) +} + +/// +/// Generate the whole Former ecosystem +/// +/// Output examples can be found in [docs to former crate](https://docs.rs/former/latest/former/) +/// + +pub fn former( input : proc_macro::TokenStream ) -> Result< TokenStream > +{ + use macro_tools::IntoGenericArgs; + + let original_input = input.clone(); + let ast = match syn::parse::< syn::DeriveInput >( input ) + { + Ok( syntax_tree ) => syntax_tree, + Err( err ) => return Err( err ), + }; + let has_debug = attr::has_debug( ast.attrs.iter() )?; + let struct_attrs = StructAttributes::from_attrs( ast.attrs.iter() )?; + + /* names */ + + let stru = &ast.ident; + let former_name = format!( "{}Former", stru ); + let former = syn::Ident::new( &former_name, stru.span() ); + let former_storage_name = format!( "{}FormerStorage", stru ); + let former_storage = syn::Ident::new( &former_storage_name, stru.span() ); + let former_definition_name = format!( "{}FormerDefinition", stru ); + let former_definition = syn::Ident::new( &former_definition_name, stru.span() ); + let former_definition_types_name = format!( "{}FormerDefinitionTypes", stru ); + let former_definition_types = syn::Ident::new( &former_definition_types_name, stru.span() ); + let as_subformer_name = format!( "{}AsSubformer", stru ); + let as_subformer = syn::Ident::new( &as_subformer_name, stru.span() ); + let as_subformer_end_name = format!( "{}AsSubformerEnd", stru ); + let as_subformer_end = syn::Ident::new( &as_subformer_end_name, stru.span() ); + + let as_subformer_end_doc = format! + ( + r#" +Represents an end condition for former of [`${stru}`], tying the lifecycle of forming processes to a broader context. + +This trait is intended for use with subformer alias, ensuring that end conditions are met according to the +specific needs of the broader forming context. It mandates the implementation of `former::FormingEnd`. + "# + ); + + /* parameters for structure */ + + let generics = &ast.generics; + let ( struct_generics_with_defaults, struct_generics_impl, struct_generics_ty, struct_generics_where ) + = generic_params::decompose( generics ); + + /* parameters for definition */ + + let extra : macro_tools::syn::AngleBracketedGenericArguments = parse_quote! + { + < (), #stru < #struct_generics_ty >, former::ReturnPreformed > + }; + let former_definition_args = generic_args::merge( &generics.into_generic_args(), &extra.into() ).args; + + /* parameters for former */ + + let extra : macro_tools::GenericsWithWhere = parse_quote! + { + < Definition = #former_definition < #former_definition_args > > + where + Definition : former::FormerDefinition< Storage = #former_storage < #struct_generics_ty > >, + Definition::Types : former::FormerDefinitionTypes< Storage = #former_storage < #struct_generics_ty > >, + }; + let extra = generic_params::merge( &generics, &extra.into() ); + + let ( former_generics_with_defaults, former_generics_impl, former_generics_ty, former_generics_where ) + = generic_params::decompose( &extra ); + + /* parameters for former perform */ + + let extra : macro_tools::GenericsWithWhere = parse_quote! + { + < Definition = #former_definition < #former_definition_args > > + where + Definition : former::FormerDefinition + < + Storage = #former_storage < #struct_generics_ty >, + Formed = #stru < #struct_generics_ty >, + >, + Definition::Types : former::FormerDefinitionTypes + < + Storage = #former_storage < #struct_generics_ty >, + Formed = #stru < #struct_generics_ty >, + >, + }; + let extra = generic_params::merge( &generics, &extra.into() ); + + let ( _former_perform_generics_with_defaults, former_perform_generics_impl, former_perform_generics_ty, former_perform_generics_where ) + = generic_params::decompose( &extra ); + + /* parameters for definition types */ + + let extra : macro_tools::GenericsWithWhere = parse_quote! + { + < __Context = (), __Formed = #stru < #struct_generics_ty > > + }; + let former_definition_types_generics = generic_params::merge( &generics, &extra.into() ); + let ( former_definition_types_generics_with_defaults, former_definition_types_generics_impl, former_definition_types_generics_ty, former_definition_types_generics_where ) + = generic_params::decompose( &former_definition_types_generics ); + + let former_definition_types_phantom = macro_tools::phantom::tuple( &former_definition_types_generics_impl ); + + /* parameters for definition */ + + let extra : macro_tools::GenericsWithWhere = parse_quote! + { + < __Context = (), __Formed = #stru < #struct_generics_ty >, __End = former::ReturnPreformed > + }; + let generics_of_definition = generic_params::merge( &generics, &extra.into() ); + let ( former_definition_generics_with_defaults, former_definition_generics_impl, former_definition_generics_ty, former_definition_generics_where ) + = generic_params::decompose( &generics_of_definition ); + + let former_definition_phantom = macro_tools::phantom::tuple( &former_definition_generics_impl ); + + /* struct attributes */ + + let ( _doc_former_mod, doc_former_struct ) = doc_generate( stru ); + let ( perform, perform_output, perform_generics ) = struct_attrs.performer()?; + + /* fields */ + + let fields = derive::named_fields( &ast )?; + + let formed_fields : Vec< Result< FormerField< '_ > > > = fields + .into_iter() + .map( | field | + { + FormerField::from_syn( field, true, true ) + }) + .collect(); + let formed_fields : Vec< _ > = process_results( formed_fields, | iter | iter.collect() )?; + + let storage_fields : Vec< Result< FormerField< '_ > > > = struct_attrs + .storage_fields() + .iter() + .map( | field | + { + FormerField::from_syn( &field, true, false ) + }) + .collect(); + let storage_fields : Vec< _ > = process_results( storage_fields, | iter | iter.collect() )?; + + let + ( + storage_field_none, + storage_field_optional, + storage_field_name, + storage_field_preform, + former_field_setter, + ) + : + ( Vec< _ >, Vec< _ >, Vec< _ >, Vec< _ >, Vec< _ > ) + = formed_fields + .iter() + .chain( storage_fields.iter() ) + .map( | field | + {( + field.storage_fields_none(), + field.storage_field_optional(), + field.storage_field_name(), + field.storage_field_preform(), + field.former_field_setter + ( + &stru, + &struct_generics_impl, + &struct_generics_ty, + &struct_generics_where, + &former, + &former_generics_impl, + &former_generics_ty, + &former_generics_where, + &former_storage, + &original_input, + ), + )}).multiunzip(); + + let results : Result< Vec< _ > > = former_field_setter.into_iter().collect(); + let ( former_field_setter, namespace_code ) : ( Vec< _ >, Vec< _ > ) = results?.into_iter().unzip(); + + let storage_field_preform : Vec< _ > = process_results( storage_field_preform, | iter | iter.collect() )?; + + let former_mutator_code = mutator + ( + &struct_attrs.mutator, + &former_definition_types, + &former_definition_types_generics_impl, + &former_definition_types_generics_ty, + &former_definition_types_generics_where, + )?; + + let result = qt! + { + + // = formed + + #[ automatically_derived ] + impl < #struct_generics_impl > #stru < #struct_generics_ty > + where + #struct_generics_where + { + + /// + /// Provides a mechanism to initiate the formation process with a default completion behavior. + /// + + #[ inline( always ) ] + pub fn former() -> #former < #struct_generics_ty #former_definition< #former_definition_args > > + { + #former :: < #struct_generics_ty #former_definition< #former_definition_args > > :: new_coercing( former::ReturnPreformed ) + } + + } + + // = entity to former + + impl< #struct_generics_impl Definition > former::EntityToFormer< Definition > + for #stru < #struct_generics_ty > + where + Definition : former::FormerDefinition< Storage = #former_storage < #struct_generics_ty > >, + #struct_generics_where + { + type Former = #former < #struct_generics_ty Definition > ; + } + + impl< #struct_generics_impl > former::EntityToStorage + for #stru < #struct_generics_ty > + where + #struct_generics_where + { + type Storage = #former_storage < #struct_generics_ty >; + } + + impl< #struct_generics_impl __Context, __Formed, __End > former::EntityToDefinition< __Context, __Formed, __End > + for #stru < #struct_generics_ty > + where + __End : former::FormingEnd< #former_definition_types < #struct_generics_ty __Context, __Formed > >, + #struct_generics_where + { + type Definition = #former_definition < #struct_generics_ty __Context, __Formed, __End >; + type Types = #former_definition_types < #struct_generics_ty __Context, __Formed >; + } + + impl< #struct_generics_impl __Context, __Formed > former::EntityToDefinitionTypes< __Context, __Formed > + for #stru < #struct_generics_ty > + where + #struct_generics_where + { + type Types = #former_definition_types < #struct_generics_ty __Context, __Formed >; + } + + // = definition types + + /// Defines the generic parameters for formation behavior including context, form, and end conditions. + #[ derive( Debug ) ] + pub struct #former_definition_types < #former_definition_types_generics_with_defaults > + where + #former_definition_types_generics_where + { + // _phantom : core::marker::PhantomData< ( __Context, __Formed ) >, + _phantom : #former_definition_types_phantom, + } + + impl < #former_definition_types_generics_impl > ::core::default::Default + for #former_definition_types < #former_definition_types_generics_ty > + where + #former_definition_types_generics_where + { + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } + } + + impl < #former_definition_types_generics_impl > former::FormerDefinitionTypes + for #former_definition_types < #former_definition_types_generics_ty > + where + #former_definition_types_generics_where + { + type Storage = #former_storage < #struct_generics_ty >; + type Formed = __Formed; + type Context = __Context; + } + + // = definition + + /// Holds the definition types used during the formation process. + #[ derive( Debug ) ] + pub struct #former_definition < #former_definition_generics_with_defaults > + where + #former_definition_generics_where + { + // _phantom : core::marker::PhantomData< ( __Context, __Formed, __End ) >, + _phantom : #former_definition_phantom, + } + + impl < #former_definition_generics_impl > ::core::default::Default + for #former_definition < #former_definition_generics_ty > + where + #former_definition_generics_where + { + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } + } + + impl < #former_definition_generics_impl > former::FormerDefinition + for #former_definition < #former_definition_generics_ty > + where + __End : former::FormingEnd< #former_definition_types < #former_definition_types_generics_ty > >, + #former_definition_generics_where + { + type Types = #former_definition_types < #former_definition_types_generics_ty >; + type End = __End; + type Storage = #former_storage < #struct_generics_ty >; + type Formed = __Formed; + type Context = __Context; + } + + // = former mutator + + #former_mutator_code + + // = storage + + #[ doc = "Stores potential values for fields during the formation process." ] + #[ allow( explicit_outlives_requirements ) ] + pub struct #former_storage < #struct_generics_with_defaults > + where + #struct_generics_where + { + #( + /// A field + #storage_field_optional, + )* + } + + impl < #struct_generics_impl > ::core::default::Default + for #former_storage < #struct_generics_ty > + where + #struct_generics_where + { + + #[ inline( always ) ] + fn default() -> Self + { + Self + { + #( #storage_field_none, )* + } + } + + } + + impl < #struct_generics_impl > former::Storage + for #former_storage < #struct_generics_ty > + where + #struct_generics_where + { + type Preformed = #stru < #struct_generics_ty >; + } + + impl < #struct_generics_impl > former::StoragePreform + for #former_storage < #struct_generics_ty > + where + #struct_generics_where + { + // type Preformed = #stru < #struct_generics_ty >; + + fn preform( mut self ) -> Self::Preformed + { + #( #storage_field_preform )* + // Rust does not support that, yet + // let result = < Definition::Types as former::FormerDefinitionTypes >::Formed + let result = #stru :: < #struct_generics_ty > + { + #( #storage_field_name )* + // #( #storage_field_name, )* + }; + return result; + } + + } + + // = former + + #[ doc = #doc_former_struct ] + pub struct #former < #former_generics_with_defaults > + where + #former_generics_where + { + /// Temporary storage for all fields during the formation process. It contains + /// partial data that progressively builds up to the final object. + pub storage : Definition::Storage, + /// An optional context providing additional data or state necessary for custom + /// formation logic or to facilitate this former's role as a subformer within another former. + pub context : core::option::Option< Definition::Context >, + /// An optional closure or handler that is invoked to transform the accumulated + /// temporary storage into the final object structure once formation is complete. + pub on_end : core::option::Option< Definition::End >, + } + + #[ automatically_derived ] + impl < #former_generics_impl > #former < #former_generics_ty > + where + #former_generics_where + { + + /// + /// Initializes a former with an end condition and default storage. + /// + #[ inline( always ) ] + pub fn new( on_end : Definition::End ) -> Self + { + Self::begin_coercing( None, None, on_end ) + } + + /// + /// Initializes a former with a coercible end condition. + /// + #[ inline( always ) ] + pub fn new_coercing< IntoEnd >( end : IntoEnd ) -> Self + where + IntoEnd : Into< Definition::End >, + { + Self::begin_coercing + ( + None, + None, + end, + ) + } + + /// + /// Begins the formation process with specified context and termination logic. + /// + #[ inline( always ) ] + pub fn begin + ( + mut storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : < Definition as former::FormerDefinition >::End, + ) + -> Self + { + if storage.is_none() + { + storage = Some( ::core::default::Default::default() ); + } + Self + { + storage : storage.unwrap(), + context : context, + on_end : ::core::option::Option::Some( on_end ), + } + } + + /// + /// Starts the formation process with coercible end condition and optional initial values. + /// + #[ inline( always ) ] + pub fn begin_coercing< IntoEnd > + ( + mut storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : IntoEnd, + ) -> Self + where + IntoEnd : ::core::convert::Into< < Definition as former::FormerDefinition >::End >, + { + if storage.is_none() + { + storage = Some( ::core::default::Default::default() ); + } + Self + { + storage : storage.unwrap(), + context : context, + on_end : ::core::option::Option::Some( ::core::convert::Into::into( on_end ) ), + } + } + + /// + /// Wrapper for `end` to align with common builder pattern terminologies. + /// + #[ inline( always ) ] + pub fn form( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + self.end() + } + + /// + /// Completes the formation and returns the formed object. + /// + #[ inline( always ) ] + pub fn end( mut self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + let on_end = self.on_end.take().unwrap(); + let mut context = self.context.take(); + < Definition::Types as former::FormerMutator >::form_mutation( &mut self.storage, &mut context ); + former::FormingEnd::< Definition::Types >::call( &on_end, self.storage, context ) + } + + #( + #former_field_setter + )* + + } + + // = former :: preform + + impl< #former_generics_impl > #former< #former_generics_ty > + where + Definition : former::FormerDefinition< Storage = #former_storage < #struct_generics_ty >, Formed = #stru < #struct_generics_ty > >, + Definition::Types : former::FormerDefinitionTypes< Storage = #former_storage < #struct_generics_ty >, Formed = #stru < #struct_generics_ty > >, + #former_generics_where + { + + /// Executes the transformation from the former's storage state to the preformed object as specified by the definition. + pub fn preform( self ) -> < Definition::Types as former::FormerDefinitionTypes >::Formed + { + former::StoragePreform::preform( self.storage ) + } + + } + + // = former :: perform + + #[ automatically_derived ] + impl < #former_perform_generics_impl > #former < #former_perform_generics_ty > + where + #former_perform_generics_where + { + + /// + /// Finish setting options and call perform on formed entity. + /// + /// If `perform` defined then associated method is called and its result returned instead of entity. + /// For example `perform()` of structure with : `#[ perform( fn after1() -> &str > )` returns `&str`. + /// + #[ inline( always ) ] + pub fn perform #perform_generics ( self ) -> #perform_output + { + let result = self.form(); + #perform + } + + } + + // = former begin + + impl< #struct_generics_impl Definition > former::FormerBegin< Definition > + // for ChildFormer< Definition > + for #former + < + #struct_generics_ty + Definition, + > + where + Definition : former::FormerDefinition< Storage = #former_storage < #struct_generics_ty > >, + #struct_generics_where + { + + #[ inline( always ) ] + fn former_begin + ( + storage : core::option::Option< Definition::Storage >, + context : core::option::Option< Definition::Context >, + on_end : Definition::End, + ) + -> Self + { + debug_assert!( storage.is_none() ); + Self::begin( None, context, on_end ) + } + + } + + // = subformer + + /// Provides a specialized former for structure using predefined settings for superformer and end conditions. + /// + /// This type alias configures former of the structure with a specific definition to streamline its usage in broader contexts, + /// especially where structure needs to be integrated into larger structures with a clear termination condition. + pub type #as_subformer < #struct_generics_ty __Superformer, __End > = #former + < + #struct_generics_ty + #former_definition + < + #struct_generics_ty + __Superformer, + __Superformer, + __End, + // impl former::FormingEnd< CommandFormerDefinitionTypes< K, __Superformer, __Superformer > >, + >, + >; + + // = as subformer end + + #[ doc = #as_subformer_end_doc ] + pub trait #as_subformer_end < #struct_generics_impl SuperFormer > + where + #struct_generics_where + Self : former::FormingEnd + < + #former_definition_types < #struct_generics_ty SuperFormer, SuperFormer >, + >, + { + } + + impl< #struct_generics_impl SuperFormer, __T > #as_subformer_end < #struct_generics_ty SuperFormer > + for __T + where + #struct_generics_where + Self : former::FormingEnd + < + #former_definition_types < #struct_generics_ty SuperFormer, SuperFormer >, + >, + { + } + + // = etc + + #( + #namespace_code + )* + + }; + + if has_debug + { + let about = format!( "derive : Former\nstructure : {stru}" ); + diag::report_print( about, &original_input, &result ); + } + + Ok( result ) +} diff --git a/module/core/former_meta/src/derive_former/field.rs b/module/core/former_meta/src/derive_former/field.rs new file mode 100644 index 0000000000..a38cdead95 --- /dev/null +++ b/module/core/former_meta/src/derive_former/field.rs @@ -0,0 +1,1186 @@ + +use super::*; +use macro_tools::{ container_kind }; + +/// +/// Definition of a field. +/// + +#[ allow( dead_code ) ] +pub struct FormerField< 'a > +{ + pub attrs : FieldAttributes, + pub vis : &'a syn::Visibility, + pub ident : &'a syn::Ident, + pub colon_token : &'a Option< syn::token::Colon >, + pub ty : &'a syn::Type, + pub non_optional_ty : &'a syn::Type, + pub is_optional : bool, + pub of_type : container_kind::ContainerKind, + pub for_storage : bool, + pub for_formed : bool, +} + +impl< 'a > FormerField< 'a > +{ + +/** methods + +from_syn + +storage_fields_none +storage_field_optional +storage_field_preform +storage_field_name +former_field_setter +subform_setter +container_setter +scalar_setter + +scalar_setter_name +container_setter_name +subform_setter_name +scalar_setter_required + +*/ + + /// Construct former field from [`syn::Field`] + pub fn from_syn( field : &'a syn::Field, for_storage : bool, for_formed : bool ) -> Result< Self > + { + let attrs = FieldAttributes::from_attrs( field.attrs.iter() )?; + let vis = &field.vis; + let ident = field.ident.as_ref() + .ok_or_else( || syn_err!( field, "Expected that each field has key, but some does not:\n {}", qt!{ #field } ) )?; + let colon_token = &field.colon_token; + let ty = &field.ty; + let is_optional = typ::is_optional( ty ); + let of_type = container_kind::of_optional( ty ).0; + let non_optional_ty : &syn::Type = if is_optional { typ::parameter_first( ty )? } else { ty }; + let field2 = Self + { + attrs, + vis, + ident, + colon_token, + ty, + non_optional_ty, + is_optional, + of_type, + for_storage, + for_formed, + }; + Ok( field2 ) + } + + /// + /// Generate fields for initializer of a struct setting each field to `None`. + /// + /// Used for initializing a Container, where on initialization all fields are None. User can alter them through builder pattern + /// + /// ### Basic use-case. of output + /// + /// ```ignore + /// int_1 : core::option::Option::None, + /// string_1 : core::option::Option::None, + /// int_optional_1 : core::option::Option::None, + /// ``` + /// + + #[ inline( always ) ] + pub fn storage_fields_none( &self ) -> TokenStream + { + let ident = Some( self.ident.clone() ); + let tokens = qt! { ::core::option::Option::None }; + let ty2 : syn::Type = syn::parse2( tokens ).unwrap(); + + qt! + { + #ident : #ty2 + } + } + + /// + /// Generate field of the former for a field of the structure + /// + /// Used to generate a Container + /// + /// ### Basic use-case. of output + /// + /// ```ignore + /// pub int_1 : core::option::Option< i32 >, + /// pub string_1 : core::option::Option< String >, + /// pub int_optional_1 : core::option::Option< i32 >, + /// pub string_optional_1 : core::option::Option< String >, + /// ``` + /// + + #[ inline( always ) ] + pub fn storage_field_optional( &self ) -> TokenStream + { + let ident = Some( self.ident.clone() ); + let ty = self.ty.clone(); + + // let ty2 = if is_optional( &ty ) + let ty2 = if self.is_optional + { + qt! { #ty } + } + else + { + qt! { ::core::option::Option< #ty > } + }; + + qt! + { + pub #ident : #ty2 + } + + } + + /// + /// Generate code converting a field of the former to the field of the structure. + /// + /// In simple terms, used on `form()` call to unwrap contained values from the former's storage. + /// Will try to use default values if no values supplied by the former and the type implements `Default` trait. + /// + /// ### Generated code will look similar to this : + /// + /// ```ignore + /// let int_1 : i32 = if self.storage.int_1.is_some() + /// { + /// // if int_1 is optional + /// Some( self.storage.int_1.take().unwrap() ) + /// + /// // if int_1 isn't optional + /// self.storage.int_1.take().unwrap() + /// } + /// else + /// { + /// // if int_1 is optional and has default + /// Some( i32::default().into() ) + /// + /// // if int_1 is optional and doesn't have default + /// None + /// + /// // if int_1 isn't optional and has default + /// i32::default().into() + /// + /// // if int_1 isn't optional and hasn't default + /// panic!( "Field 'int_1' isn't initialized" ) + /// }; + /// ``` + /// + + #[ inline( always ) ] + pub fn storage_field_preform( &self ) -> Result< TokenStream > + { + + if !self.for_formed + { + return Ok( qt!{} ) + } + + 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() ); + + let tokens = if self.is_optional + { + + let _else = match default + { + None => + { + qt! + { + ::core::option::Option::None + } + } + + Some( default_val ) => + { + qt! + { + ::core::option::Option::Some( ::core::convert::Into::into( #default_val ) ) + } + } + }; + + qt! + { + let #ident = if self.#ident.is_some() + { + ::core::option::Option::Some( self.#ident.take().unwrap() ) + } + else + { + #_else + }; + } + + } + else + { + + let _else = match default + { + None => + { + let panic_msg = format!( "Field '{}' isn't initialized", ident ); + qt! + { + { + // By hardly utilizing deref coercion, we achieve conditional trait implementation + trait MaybeDefault< T > + { + fn maybe_default( self : &Self ) -> T { panic!( #panic_msg ) } + } + + // Panic on non-`Default` types + impl< T > MaybeDefault< T > + for &::core::marker::PhantomData< T > + {} + + // Return default value on `Default`` types + impl< T > MaybeDefault< T > + for ::core::marker::PhantomData< T > + where T : ::core::default::Default, + { + fn maybe_default( self : &Self ) -> T + { + T::default() + } + } + + // default if `impl Default`, otherwise - panic + ( &::core::marker::PhantomData::< #ty > ).maybe_default() + } + } + } + Some( default_val ) => + { + qt! + { + ::core::convert::Into::into( #default_val ) + } + } + }; + + qt! + { + let #ident = if self.#ident.is_some() + { + self.#ident.take().unwrap() + } + else + { + #_else + }; + } + + }; + + Ok( tokens ) + } + + /// + /// Extract name of a field out. + /// + + #[ inline( always ) ] + pub fn storage_field_name( &self ) -> TokenStream + { + + if !self.for_formed + { + return qt!{} + } + + let ident = self.ident; + qt!{ #ident, } + + } + + /// Generates former setters for the specified field within a struct or enum. + /// + /// This function is responsible for dynamically creating code that allows for the building + /// or modifying of fields within a `Former`-enabled struct or enum. It supports different + /// types of setters based on the field attributes, such as scalar setters, container setters, + /// and subform setters. + /// + /// # Returns + /// + /// Returns a pair of `TokenStream` instances: + /// - The first `TokenStream` contains the generated setter functions for the field. + /// - The second `TokenStream` includes additional namespace or supporting code that might + /// be required for the setters to function correctly, such as definitions for end conditions + /// or callbacks used in the formation process. + /// + /// The generation of setters is dependent on the attributes of the field: + /// - **Scalar Setters**: Created for basic data types and simple fields. + /// - **Container Setters**: Generated when the field is annotated to behave as a container, + /// supporting operations like adding or replacing elements. + /// - **Subform Setters**: Generated for fields annotated as subforms, allowing for nested + /// forming processes where a field itself can be formed using a dedicated former. + /// + + #[ inline ] + pub fn former_field_setter + ( + &self, + stru : &syn::Ident, + struct_generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + struct_generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + struct_generics_where : &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + former : &syn::Ident, + former_generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + former_generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + former_generics_where : &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + former_storage : &syn::Ident, + original_input : &proc_macro::TokenStream, + ) + -> Result< ( TokenStream, TokenStream ) > + { + let namespace_code = qt! {}; + let setters_code = self.scalar_setter + ( + former, + former_storage, + ); + + // container setter + let ( setters_code, namespace_code ) = if let Some( _ ) = &self.attrs.container + { + let ( setters_code2, namespace_code2 ) = self.container_setter + ( + stru, + former, + former_storage, + former_generics_impl, + former_generics_ty, + former_generics_where, + original_input, + )?; + ( qt! { #setters_code #setters_code2 }, qt! { #namespace_code #namespace_code2 } ) + } + else + { + ( setters_code, namespace_code ) + }; + + // subform setter + let ( setters_code, namespace_code ) = if self.attrs.subform.is_some() + { + let ( setters_code2, namespace_code2 ) = self.subform_setter + ( + stru, + former, + former_storage, + former_generics_ty, + struct_generics_impl, + struct_generics_ty, + struct_generics_where, + )?; + ( qt! { #setters_code #setters_code2 }, qt! { #namespace_code #namespace_code2 } ) + } + else + { + ( setters_code, namespace_code ) + }; + + // tree_print!( setters_code.as_ref().unwrap() ); + Ok( ( setters_code, namespace_code ) ) + } + + /// Generates setter functions for subforms within a container structure in a builder pattern. + /// + /// This function is a key component of the `former` crate's capability to dynamically create setters for manipulating + /// data within a nested container structure like a `HashMap` or a `Vec`. The setters facilitate the addition or + /// modification of entries within the container, directly from the parent former's context. + /// + /// See `examples/subformer_subform_manual.rs` for example of generated code. + /// + + #[ inline ] + pub fn subform_setter + ( + &self, + stru : &syn::Ident, + former : &syn::Ident, + former_storage : &syn::Ident, + former_generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + struct_generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + struct_generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + struct_generics_where : &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + ) + -> Result< ( TokenStream, TokenStream ) > + { + + // if self.attrs.subform.is_none() + // { + // return Ok( qt!{ } ); + // } + + use convert_case::{ Case, Casing }; + let field_ident = self.ident; + let field_typ = self.non_optional_ty; + let attr = self.attrs.subform.as_ref().unwrap(); + // let params = typ::type_parameters( &self.non_optional_ty, .. ); + + // example : `child` + let setter_name = self.subform_setter_name(); + + // example : `ParentFormerAddChildrenEnd`` + let former_add_end_name = format!( "{}FormerAdd{}End", stru, field_ident.to_string().to_case( Case::Pascal ) ); + let former_add_end = syn::Ident::new( &former_add_end_name, field_ident.span() ); + + // example : `_children_former` + let field_add_name = format!( "_{}_add", field_ident ); + let field_add = syn::Ident::new( &field_add_name, field_ident.span() ); + + let doc = format! + ( + r#" + +Initiates the addition of {field_ident} to the `{stru}` entity using a dedicated subformer. + +This method configures and returns a subformer specialized for the `{0}` entities' formation process, +which is part of the `{stru}` entity's construction. The subformer is set up with a specific end condition +handled by `{former_add_end}`, ensuring that the {field_ident} are properly integrated into the +parent's structure once formed. + +# Returns + +Returns an instance of `Former2`, a subformer ready to begin the formation process for `{0}` entities, +allowing for dynamic and flexible construction of the `{stru}` entity's {field_ident}. + + "#, + format!( "{}", qt!{ #field_typ } ), + ); + + let setters_code = qt! + { + + #[ doc = #doc ] + #[ inline( always ) ] + pub fn #field_add< Former2, Definition2 >( self ) -> Former2 + where + Definition2 : former::FormerDefinition + < + End = #former_add_end< Definition >, + Storage = < < #field_typ as former::Container >::Val as former::EntityToStorage >::Storage, + Formed = Self, + Context = Self, + >, + Definition2::Types : former::FormerDefinitionTypes + < + Storage = < < #field_typ as former::Container >::Val as former::EntityToStorage >::Storage, + Formed = Self, + Context = Self, + >, + Former2 : former::FormerBegin< Definition2 >, + { + Former2::former_begin( None, Some( self ), #former_add_end::default() ) + } + + }; + + let setters_code = if attr.setter() + { + + let doc = format! + ( + r#" +Provides a user-friendly interface to add an instancce of {field_ident} to the {stru}. + +# Returns + +Returns an instance of `Former2`, a subformer ready to begin the formation process for `{0}` entities, +allowing for dynamic and flexible construction of the `{stru}` entity's {field_ident}. + + "#, + format!( "{}", qt!{ #field_typ } ), + ); + + qt! + { + #setters_code + + #[ doc = #doc ] + #[ inline( always ) ] + pub fn #setter_name( self ) -> + < < #field_typ as former::Container >::Val as former::EntityToFormer + < + < + < #field_typ as former::Container >::Val as former::EntityToDefinition< Self, Self, #former_add_end < Definition > > + >::Definition, + > + >::Former + // #as_subformer< Self, impl #as_subformer_end< Self > > + { + self.#field_add + ::< < < #field_typ as former::Container >::Val as former::EntityToFormer< _ > >::Former, _, >() + // ::< #former< _ >, _, >() + } + } + + // #[ inline( always ) ] + // pub fn child( self ) -> + // ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + // { + // self._children_add + // ::< < Child as former::EntityToFormer< _ > >::Former, _, >() + // } + + } + else + { + setters_code + }; + + if attr.hint + { + let hint = format! + ( + r#" + +/// Initializes and configures a subformer for adding named child entities. This method leverages an internal function +/// to create and return a configured subformer instance. It allows for the dynamic addition of children with specific names, +/// integrating them into the formation process of the parent entity. + +impl< Definition > {}< Definition > +where + Definition : former::FormerDefinition< Storage = {} >, +{{ + + #[ inline( always ) ] + pub fn {}( self ) -> ChildAsSubformer< Self, impl ChildAsSubformerEnd< Self > > + {{ + self.{}::< ChildFormer< _ >, _, >() + }} + // Replace Child with name of type of element value. + +}} + "#, + former, + former_storage, + field_ident, + field_add_name, + ); + println!( "{hint}" ); + } + + let doc = format! + ( + r#" + +Implements the `FormingEnd` trait for `{former_add_end}` to handle the final +stage of the forming process for a `{stru}` container that contains `{0}` elements. + +This implementation is tailored to manage the transition of {field_ident} elements from a substorage +temporary state into their final state within the `{stru}`'s storage. The function ensures +that the `{stru}`'s {field_ident} storage is initialized if not already set, and then adds the +preformed elements to this storage. + +# Type Parameters + +- `Types2`: Represents the specific types associated with the `Former` trait being applied, + which include storage, formed type, and context. +- `Definition`: Defines the `FormerDefinition` that outlines the storage structure and + the end conditions for the formation process. + +# Parameters + +- `substorage`: The storage from which {field_ident} elements are preformed and retrieved. +- `super_former`: An optional context which, upon invocation, contains the `{former}` + instance being formed. + +# Returns + +Returns the updated `{former}` instance with newly added {field_ident}, completing the +formation process of the `{stru}`. + + "#, + format!( "{}", qt!{ #field_typ } ), + ); + + + let namespace_code = qt! + { + + #[ doc = #doc ] + pub struct #former_add_end< Definition > + { + _phantom : core::marker::PhantomData< fn( Definition ) >, + } + + impl< Definition > Default + for #former_add_end< Definition > + { + #[ inline( always ) ] + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } + } + + impl< #struct_generics_impl Types2, Definition > former::FormingEnd< Types2, > + for #former_add_end< Definition > + where + Definition : former::FormerDefinition + < + Storage = < #stru < #struct_generics_ty > as former::EntityToStorage >::Storage, + >, + Types2 : former::FormerDefinitionTypes + < + Storage = < < #field_typ as former::Container >::Val as former::EntityToStorage >::Storage, + Formed = #former< #former_generics_ty >, + Context = #former< #former_generics_ty >, + >, + #struct_generics_where + { + #[ inline( always ) ] + fn call + ( + &self, + substorage : Types2::Storage, + super_former : core::option::Option< Types2::Context >, + ) + -> Types2::Formed + { + let mut super_former = super_former.unwrap(); + if super_former.storage.#field_ident.is_none() + { + super_former.storage.#field_ident = Some( Default::default() ); + } + if let Some( ref mut field ) = super_former.storage.#field_ident + { + former::ContainerAdd::add + ( + field, + < < #field_typ as former::Container >::Val as former::ValToEntry< #field_typ > > + ::val_to_entry( former::StoragePreform::preform( substorage ) ), + ); + } + super_former + } + } + + }; + + // tree_print!( setters_code.as_ref().unwrap() ); + Ok( ( setters_code, namespace_code ) ) + } + + /// + /// Generate a container setter for the 'field_ident' with the 'setter_name' name. + /// + /// See `examples/subformer_container_manual.rs` for example of generated code. + + #[ inline ] + pub fn container_setter + ( + &self, + stru : &syn::Ident, + former : &syn::Ident, + former_storage : &syn::Ident, + former_generics_impl : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + former_generics_ty : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + former_generics_where : &syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + original_input : &proc_macro::TokenStream, + ) + -> Result< ( TokenStream, TokenStream ) > + { + let attr = self.attrs.container.as_ref().unwrap(); + let field_ident = &self.ident; + let field_typ = &self.non_optional_ty; + let params = typ::type_parameters( &field_typ, .. ); + + use convert_case::{ Case, Casing }; + let former_assign_end_name = format!( "{}FormerAssign{}End", stru, field_ident.to_string().to_case( Case::Pascal ) ); + let former_assign_end = syn::Ident::new( &former_assign_end_name, field_ident.span() ); + let field_assign_name = format!( "_{}_container_former", field_ident ); + let field_assign = syn::Ident::new( &field_assign_name, field_ident.span() ); + + // example : `former::VectorDefinition` + let subformer_definition = &attr.definition; + let subformer_definition = if subformer_definition.is_some() + { + qt! + { + #subformer_definition + < + #( #params, )* + Self, + Self, + #former_assign_end< Definition >, + > + } + // former::VectorDefinition< String, Self, Self, Struct1FormerAssignVec1End, > + } + else + { + qt! + { + < + #field_typ as former::EntityToDefinition< Self, Self, #former_assign_end< Definition > > + >::Definition + } + // < Vec< String > as former::EntityToDefinition< Self, Self, Struct1FormerAssignVec1End > >::Definition + }; + + let doc = format! + ( + "Container setter for the '{}' field. Method {} unlike method {} accept custom container subformer.", + field_ident, + field_assign_name, + field_ident, + ); + + let setter1 = + qt! + { + + #[ doc = #doc ] + #[ inline( always ) ] + pub fn #field_assign< Former2 >( self ) -> Former2 + where + Former2 : former::FormerBegin + < + #subformer_definition, + >, + #subformer_definition : former::FormerDefinition + < + // Storage : former::ContainerAdd< Entry = < #field_typ as former::Container >::Entry >, + Storage = #field_typ, + Context = #former< #former_generics_ty >, + End = #former_assign_end< Definition >, + >, + { + Former2::former_begin( None, Some( self ), #former_assign_end::< Definition >::default() ) + } + + // #[ inline( always ) ] + // pub fn _hashset_1_assign< Former2 >( self ) -> Former2 + // where + // Former2 : former::FormerBegin + // < + // former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, + // >, + // former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > > : former::FormerDefinition + // < + // Storage : former::ContainerAdd< Entry = < collection_tools::HashSet< String > as former::Container >::Entry >, + // Context = Struct1Former< Definition >, + // End = Struct1FormerAssignHashset1End< Definition >, + // >, + // { + // Former2::former_begin( None, Some( self ), Struct1FormerAssignHashset1End::< Definition >::default() ) + // } + + }; + + let setter_name = self.container_setter_name(); + let setter2 = if let Some( setter_name ) = setter_name + { + qt! + { + + #[ doc = #doc ] + #[ inline( always ) ] + pub fn #setter_name( self ) -> former::ContainerFormer:: + < + // ( #( #params, )* ), + < #field_typ as former::Container >::Entry, + #subformer_definition, + > + where + #subformer_definition : former::FormerDefinition + < + // Storage : former::ContainerAdd< Entry = < #field_typ as former::Container >::Entry >, + Storage = #field_typ, + Context = #former< #former_generics_ty >, + End = #former_assign_end < Definition >, + >, + { + self.#field_assign::< former::ContainerFormer:: + < + _, + _, + // ( #( #params, )* ), + // #subformer_definition, + > > () + } + + // #[ inline( always ) ] + // pub fn hashset_1( self ) -> former::ContainerFormer:: + // < + // String, + // former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, + // > + // where + // former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > > : former::FormerDefinition + // < + // Storage : former::ContainerAdd< Entry = < collection_tools::HashSet< String > as former::Container >::Entry >, + // Context = Struct1Former< Definition >, + // End = Struct1FormerAssignHashset1End< Definition >, + // >, + // { + // self._hashset_1_assign::< former::ContainerFormer:: + // < + // String, + // former::HashSetDefinition< String, Self, Self, Struct1FormerAssignHashset1End< Definition > >, + // > > () + // } + + } + } + else + { + qt!{} + }; + + if attr.hint + { + let hint = format! + ( + r#" + +/// The containr setter provides a container setter that returns a ContainerFormer tailored for managing a collection of child entities. It employs a generic container definition to facilitate operations on the entire collection, such as adding or updating elements. + +impl< Definition, > {}< Definition, > +where + Definition : former::FormerDefinition< Storage = {} >, +{{ + + #[ inline( always ) ] + pub fn {}( self ) -> former::ContainerFormer:: + < + ( {} ), + former::HashMapDefinition< {} Self, Self, {}< Definition >, > + // Replace `HashMapDefinition` with definition for your container + > + {{ + self.{}() + }} + +}} + + "#, + former, + former_storage, + field_ident, + format!( "{}", qt!{ #( #params, )* } ), + format!( "{}", qt!{ #( #params, )* } ), + former_assign_end, + field_assign, + ); + let about = format! + ( +r#"derive : Former +structure : {stru} +field : {field_ident}"#, + ); + diag::report_print( about, original_input, hint ); + } + + let setters_code = qt! + { + #setter1 + #setter2 + }; + + // example : `former::VectorDefinition`` + let subformer_definition = &self.attrs.container.as_ref().unwrap().definition; + + let former_assign_end_doc = format! + ( + r#" +A callback structure to manage the final stage of forming a `{0}` for the `{stru}` container. + +This callback is used to integrate the contents of a temporary `{0}` back into the original `{stru}` former +after the subforming process is completed. It replaces the existing content of the `{field_ident}` field in `{stru}` +with the new content generated during the subforming process. + "#, + format!( "{}", qt!{ #field_typ } ), + ); + + let subformer_definition_types = if let Some( ref _subformer_definition ) = subformer_definition + { + let subformer_definition_types_string = format!( "{}Types", qt!{ #subformer_definition } ); + let subformer_definition_types : syn::Type = syn::parse_str( &subformer_definition_types_string )?; + qt! + { + #subformer_definition_types + < + #( #params, )* + #former< #former_generics_ty >, + #former< #former_generics_ty >, + > + } + } + else + { + qt! + { + < + #field_typ as former::EntityToDefinitionTypes + < + #former< #former_generics_ty >, + #former< #former_generics_ty >, + > + >::Types + } + }; + + let r = qt! + { + + #[ doc = #former_assign_end_doc ] + pub struct #former_assign_end< Definition > + { + _phantom : core::marker::PhantomData< ( Definition, ) >, + } + + impl< Definition > Default + for #former_assign_end< Definition > + { + + #[ inline( always ) ] + fn default() -> Self + { + Self + { + _phantom : core::marker::PhantomData, + } + } + + } + + #[ automatically_derived ] + impl< #former_generics_impl > former::FormingEnd + < + // VectorDefinitionTypes + #subformer_definition_types, + > + for #former_assign_end< Definition > + where + #former_generics_where + { + #[ inline( always ) ] + fn call + ( + &self, + storage : #field_typ, + super_former : Option< #former< #former_generics_ty > >, + ) + -> #former< #former_generics_ty > + { + let mut super_former = super_former.unwrap(); + if let Some( ref mut field ) = super_former.storage.#field_ident + { + former::ContainerAssign::assign( field, storage ); + } + else + { + super_former.storage.#field_ident = Some( storage ); + } + super_former + } + } + + }; + + // tree_print!( r.as_ref().unwrap() ); + let namespace_code = r; + + Ok( ( setters_code, namespace_code ) ) + } + + /// + /// Generate a single scalar setter for the 'field_ident' with the 'setter_name' name. + /// + /// Used as a helper function for former_field_setter(), which generates alias setters + /// + /// # Example of generated code + /// + /// ```ignore + /// #[ doc = "Setter for the 'int_1' field." ] + /// #[ inline ] + /// pub fn int_1< Src >( mut self, src : Src ) -> Self + /// where + /// Src : ::core::convert::Into< i32 >, + /// { + /// debug_assert!( self.int_1.is_none() ); + /// self.storage.int_1 = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); + /// self + /// } + /// ``` + + #[ inline ] + pub fn scalar_setter + ( + &self, + former : &syn::Ident, + former_storage : &syn::Ident, + ) + -> TokenStream + { + let field_ident = self.ident; + let typ = self.non_optional_ty; + let setter_name = self.scalar_setter_name(); + let attr = self.attrs.scalar.as_ref(); + + if attr.is_some() && attr.unwrap().hint + { + let hint = format! + ( + r#" + +impl< Definition > {}< Definition > +where + Definition : former::FormerDefinition< Storage = {} >, +{{ + #[ inline ] + pub fn {}< Src >( mut self, src : Src ) -> Self + where + Src : ::core::convert::Into< {} >, + {{ + debug_assert!( self.storage.{}.is_none() ); + self.storage.{} = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); + self + }} +}} + + "#, + former, + former_storage, + field_ident, + format!( "{}", qt!{ #typ } ), + field_ident, + field_ident, + ); + println!( "{hint}" ); + } + + if !self.scalar_setter_required() + { + return qt! {}; + } + + let doc = format! + ( + "Scalar setter for the '{}' field.", + field_ident, + ); + + qt! + { + #[ doc = #doc ] + #[ inline ] + pub fn #setter_name< Src >( mut self, src : Src ) -> Self + where + Src : ::core::convert::Into< #typ >, + { + debug_assert!( self.storage.#field_ident.is_none() ); + self.storage.#field_ident = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); + self + } + } + } + + /// Get name of scalar setter. + pub fn scalar_setter_name( &self ) -> &syn::Ident + { + if let Some( ref attr ) = self.attrs.scalar + { + if let Some( ref name ) = attr.name + { + return name + } + } + return &self.ident; + } + + /// 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() + { + 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 > + { + + if let Some( ref attr ) = self.attrs.subform + { + if attr.setter() + { + if let Some( ref name ) = attr.name + { + return Some( &name ) + } + else + { + return Some( &self.ident ) + } + } + } + + return None; + } + + /// Is scalar setter required. Does not if container of subformer setter requested. + pub fn scalar_setter_required( &self ) -> bool + { + + let mut explicit = false; + if let Some( ref attr ) = self.attrs.scalar + { + if let Some( setter ) = attr.setter + { + if setter == false + { + return false + } + explicit = true; + } + if let Some( ref _name ) = attr.name + { + explicit = true; + } + } + + if self.attrs.container.is_some() && !explicit + { + return false; + } + + if self.attrs.subform.is_some() && !explicit + { + return false; + } + + return 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 new file mode 100644 index 0000000000..5aea94b80d --- /dev/null +++ b/module/core/former_meta/src/derive_former/field_attrs.rs @@ -0,0 +1,442 @@ + +use super::*; +use macro_tools::{ attr, Result }; + +/// +/// Attributes of a field. +/// + +pub struct FieldAttributes +{ + pub config : Option< AttributeConfig >, + pub scalar : Option< AttributeScalarSetter >, + pub container : Option< AttributeContainerSetter >, + pub subform : Option< AttributeSubformSetter >, +} + +impl FieldAttributes +{ + // fn from_attrs( attributes : & Vec< syn::Attribute > ) -> Result< Self > + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> Result< Self > + { + let mut config = None; + let mut scalar = None; + let mut container = None; + let mut subform = None; + 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_str = format!( "{}", key_ident ); + + if attr::is_standard( &key_str ) + { + continue; + } + + match key_str.as_ref() + { + "former" => + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + config.replace( syn::parse2::< AttributeConfig >( meta_list.tokens.clone() )? ); + }, + syn::Meta::Path( ref _path ) => + { + config.replace( syn::parse2::< AttributeConfig >( Default::default() )? ); + }, + _ => return_syn_err!( attr, "Expects an attribute of format #[ former( default = 13 ) ].\nGot: {}", qt!{ #attr } ), + } + } + "scalar" => + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + scalar.replace( syn::parse2::< AttributeScalarSetter >( meta_list.tokens.clone() )? ); + }, + syn::Meta::Path( ref _path ) => + { + scalar.replace( syn::parse2::< AttributeScalarSetter >( Default::default() )? ); + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ scalar( setter = false ) ]` or `#[ scalar( setter = false, name = my_name ) ]`. \nGot: {}", qt!{ #attr } ), + } + } + "container" => + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + container.replace( syn::parse2::< AttributeContainerSetter >( meta_list.tokens.clone() )? ); + }, + syn::Meta::Path( ref _path ) => + { + container.replace( syn::parse2::< AttributeContainerSetter >( Default::default() )? ); + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ container ]` or `#[ container( definition = former::VectorDefinition ) ]` if you want to use default container defition. \nGot: {}", qt!{ #attr } ), + } + } + "subform" => + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + subform.replace( syn::parse2::< AttributeSubformSetter >( meta_list.tokens.clone() )? ); + }, + syn::Meta::Path( ref _path ) => + { + subform.replace( syn::parse2::< AttributeSubformSetter >( Default::default() )? ); + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ subform ]` or `#[ subform( name : child )` ], \nGot: {}", qt!{ #attr } ), + } + } + _ => + { + return Err( syn_err!( attr, "Unknown field attribute {}", qt!{ #attr } ) ); + } + } + } + + Ok( FieldAttributes { config, scalar, container, subform } ) + } +} + +/// +/// Attribute to hold configuration information about the field such as default value. +/// +/// `#[ default( 13 ) ]` +/// + +pub struct AttributeConfig +{ + + /// Default value to use for the field. + pub default : Option< syn::Expr >, + +} + +impl AttributeConfig +{ +} + +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; + + while !input.is_empty() + { + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Ident ) + { + let ident : syn::Ident = input.parse()?; + if ident == "default" + { + input.parse::< syn::Token![ = ] >()?; + default = Some( input.parse()? ); + } + else + { + return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'default'. For example: `former( default = 13 )`", ident ) ) ); + } + } + + else + { + return Err( syn::Error::new( input.span(), "Expected 'default'. For example: `former( default = 13 )`" ) ); + } + + // Optional comma handling + if input.peek( syn::Token![ , ] ) + { + input.parse::< syn::Token![ , ] >()?; + } + } + + Ok( Self { default } ) + } +} + +/// +/// 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 +/// ``` +/// + +pub struct AttributeScalarSetter +{ + /// Optional identifier for naming the setter. + pub name : Option< syn::Ident >, + /// Controls the generation of a setter method. If false, a setter method is not generated. + pub setter : Option< bool >, + /// 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, +} + +#[ allow( dead_code ) ] +impl AttributeScalarSetter +{ + + /// Should setter be generated or not? + pub fn setter( &self ) -> bool + { + self.setter.is_none() || self.setter.unwrap() + } + +} + +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; + + while !input.is_empty() + { + 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 == "hint" + { + input.parse::< syn::Token![ = ] >()?; + let value : syn::LitBool = input.parse()?; + hint = value.value; + } + else + { + return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'name', 'setter', or 'definition'. For example: `scalar( name = myName, setter = true )`", ident ) ) ); + } + } + else + { + return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `scalar( name = myName, setter = true )`" ) ); + } + + // Optional comma handling + if input.peek( syn::Token![,] ) + { + input.parse::< syn::Token![,] >()?; + } + } + + Ok( Self { name, setter, hint } ) + } +} + +/// Represents an attribute for configuring container 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. +/// It allows the customization of setter methods and the specification of the container's behavior through meta attributes. +/// +/// ## Example Input +/// +/// The following is an example of a token stream that this struct can parse: +/// ```ignore +/// name = "custom_setter", setter = true, definition = former::VectorDefinition +/// ``` +/// + +pub struct AttributeContainerSetter +{ + /// Optional identifier for naming the setter. + pub name : Option< syn::Ident >, + /// Controls the generation of a setter method. If false, a setter method is not generated. + pub setter : Option< bool >, + /// Definition of the container former to use, e.g., `former::VectorFormer`. + pub definition : Option< syn::Type >, + /// 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, +} + +impl AttributeContainerSetter +{ + + /// Should setter be generated or not? + pub fn setter( &self ) -> bool + { + self.setter.is_none() || self.setter.unwrap() + } + +} + +impl syn::parse::Parse for AttributeContainerSetter +{ + 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; + + while !input.is_empty() + { + 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 == "hint" + { + input.parse::< syn::Token![ = ] >()?; + let value : syn::LitBool = input.parse()?; + hint = value.value; + } + else if ident == "definition" + { + input.parse::< syn::Token![ = ] >()?; + definition = Some( input.parse()? ); + } + else + { + return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'name', 'setter', or 'definition'. For example: `container( name = myName, setter = true, definition = MyDefinition )`", ident ) ) ); + } + } + else + { + return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `container( name = myName, setter = true, definition = MyDefinition )`" ) ); + } + + // Optional comma handling + if input.peek( syn::Token![ , ] ) + { + input.parse::< syn::Token![ , ] >()?; + } + } + + Ok( Self { name, setter, hint, definition } ) + } +} + +/// Represents a subform attribute to control subform setter generation. +/// Used to specify extra options for using one former as subformer of another one. +/// For example name of setter could be customized. +/// +/// ## Example Input +/// +/// A typical input to parse might look like the following: +/// +/// ```ignore +/// name = field_name, setter = true +/// ``` +/// +/// or simply: +/// +/// ```ignore +/// mame = field_name +/// ``` + +pub struct AttributeSubformSetter +{ + /// An optional identifier that names the setter. It is parsed from inputs + /// like `name = my_field`. + pub 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. + pub setter : Option< bool >, + /// 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, +} + +impl AttributeSubformSetter +{ + + /// Should setter be generated or not? + pub fn setter( &self ) -> bool + { + self.setter.is_none() || self.setter.unwrap() + } + +} + +impl syn::parse::Parse for AttributeSubformSetter +{ + 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()?; + 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 == "hint" + { + input.parse::< syn::Token![ = ] >()?; + let value : syn::LitBool = input.parse()?; + hint = value.value; + } + else + { + return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'name', 'setter', or 'definition'. For example: `subform( name = myName, setter = true )`", ident ) ) ); + } + } + else + { + return Err( syn::Error::new( input.span(), "Expected 'name', 'setter', or 'definition' identifier. For example: `subform( name = myName, setter = true )`" ) ); + } + + // Optional comma handling + if input.peek( syn::Token![,] ) + { + input.parse::< syn::Token![,] >()?; + } + } + + Ok( Self { name, setter, hint } ) + } +} diff --git a/module/core/former_meta/src/derive_former/struct_attrs.rs b/module/core/former_meta/src/derive_former/struct_attrs.rs new file mode 100644 index 0000000000..c2a15bb9cf --- /dev/null +++ b/module/core/former_meta/src/derive_former/struct_attrs.rs @@ -0,0 +1,299 @@ + +use super::*; +use macro_tools::{ attr, Result }; + +/// +/// Definition of a field. +/// + +/// +/// Attributes of a struct. +/// + +pub struct StructAttributes +{ + pub perform : Option< AttributePerform >, + pub storage_fields : Option< AttributeStorageFields >, + pub mutator : AttributeMutator, +} + +impl StructAttributes +{ + + pub fn from_attrs< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> Result< Self > + { + let mut perform = None; + let mut storage_fields = None; + let mut mutator = Default::default(); + + 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_str = format!( "{}", key_ident ); + + if attr::is_standard( &key_str ) + { + continue; + } + + match key_str.as_ref() + { + "storage_fields" => + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + storage_fields.replace( syn::parse2::< AttributeStorageFields >( meta_list.tokens.clone() )? ); + }, + _ => return_syn_err!( attr, "Expects an attribute of format #[ storage_fields( a : i32, b : Option< String > ) ] +.\nGot: {}", qt!{ #attr } ), + } + } + "perform" => + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + perform.replace( syn::parse2::< AttributePerform >( meta_list.tokens.clone() )? ); + }, + _ => return_syn_err!( attr, "Expects an attribute of format #[ perform( fn parse( mut self ) -> Request ) ] +.\nGot: {}", qt!{ #attr } ), + } + } + "mutator" => + { + match attr.meta + { + syn::Meta::List( ref meta_list ) => + { + mutator = syn::parse2::< AttributeMutator >( meta_list.tokens.clone() )? + }, + syn::Meta::Path( ref _path ) => + { + }, + _ => return_syn_err!( attr, "Expects an attribute of format `#[ mutator( custom = true, hint = true ) ]`. \nGot: {}", qt!{ #attr } ), + } + } + "debug" => + { + } + _ => + { + return Err( syn_err!( attr, "Known structure attirbutes are : `storage_fields`, `perform`, `debug`.\nUnknown structure attribute : {}", qt!{ #attr } ) ); + } + } + } + + Ok( StructAttributes { perform, storage_fields, mutator } ) + } + + + /// + /// Generate parts, used for generating `perform()`` method. + /// + /// Similar to `form()`, but will also invoke function from `perform` attribute, if specified. + /// + /// # Example of returned tokens : + /// + /// ## perform : + /// return result; + /// + /// ## perform_output : + /// < T : ::core::default::Default > + /// + /// ## perform_generics : + /// Vec< T > + /// + + pub fn performer( &self ) + -> Result< ( TokenStream, TokenStream, TokenStream ) > + { + + let mut perform = qt! + { + return result; + }; + let mut perform_output = qt!{ Definition::Formed }; + let mut perform_generics = qt!{}; + + if let Some( ref attr ) = self.perform + { + + // let attr_perform = syn::parse2::< AttributePerform >( meta_list.tokens.clone() )?; + let signature = &attr.signature; + let generics = &signature.generics; + perform_generics = qt!{ #generics }; + let perform_ident = &signature.ident; + let output = &signature.output; + if let syn::ReturnType::Type( _, boxed_type ) = output + { + perform_output = qt!{ #boxed_type }; + } + perform = qt! + { + return result.#perform_ident(); + }; + + } + + Ok( ( perform, perform_output, perform_generics ) ) + } + + /// Returns an iterator over the fields defined in the `storage_fields` attribute. + /// + /// This function provides an iterator that yields `syn::Field` objects. If `storage_fields` is set, + /// it clones and iterates over its fields. If `storage_fields` is `None`, it returns an empty iterator. + /// + + // pub fn storage_fields( &self ) -> impl Iterator< Item = syn::Field > + pub fn storage_fields( &self ) -> &syn::punctuated::Punctuated< syn::Field, syn::token::Comma > + { + + self.storage_fields.as_ref().map_or_else + ( + || &*Box::leak( Box::new( syn::punctuated::Punctuated::new() ) ), + | attr | &attr.fields + ) + // qqq : find better solutioin + + // self.storage_fields + // .as_ref() + // .map_or_else( + // || syn::punctuated::Punctuated::< syn::Field, syn::token::Comma >::new().into_iter(), + // | attr | attr.fields.clone().into_iter() + // // Clone and create an iterator when storage_fields is Some + // ) + } + +} + +/// +/// Attribute to hold information about method to call after form. +/// +/// `#[ perform( fn after1< 'a >() -> Option< &'a str > ) ]` +/// + +pub struct AttributePerform +{ + pub signature : syn::Signature, +} + +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()?, + }) + } +} + +/// +/// Attribute to hold storage-specific fields. +/// Useful if formed structure should not have such fields. +/// +/// `#[ storage_fields( a : i32, b : Option< String > ) ]` +/// + +pub struct AttributeStorageFields +{ + pub fields : syn::punctuated::Punctuated< syn::Field, syn::token::Comma >, +} + +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![,] )?; + + Ok( Self + { + fields, + // fields : syn::Fields::Named( syn::FieldsNamed + // { + // brace_token : Default::default(), + // named : fields, + // }), + }) + } +} + +/// Represents attributes for customizing the mutation process in a forming operation. +/// +/// `AttributeMutator` allows specifying whether a custom mutator should be used or a sketch should be provided +/// as a hint for developing a custom mutator. This is crucial for advanced scenarios where the entity's state +/// might require conditional modifications which are not handled by the standard `FormingEnd`. +/// +/// ## Example of code +/// ```ignore +/// custom = true, hint = true +/// ``` + +#[ derive( Debug, Default ) ] +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, + /// 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, +} + +impl syn::parse::Parse for AttributeMutator +{ + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let mut custom = false; + let mut hint = false; + + while !input.is_empty() + { + let lookahead = input.lookahead1(); + if lookahead.peek( syn::Ident ) + { + let ident : syn::Ident = input.parse()?; + input.parse::< syn::Token![=] >()?; + if ident == "custom" + { + let value : syn::LitBool = input.parse()?; + custom = value.value; + } + else if ident == "hint" + { + let value : syn::LitBool = input.parse()?; + hint = value.value; + } + else + { + return Err( syn::Error::new_spanned( &ident, format!( "Unexpected identifier '{}'. Expected 'custom' or 'hint'.", ident ) ) ); + } + } + else + { + return Err( lookahead.error() ); + } + + // Optional comma handling + if input.peek( syn::Token![,] ) + { + input.parse::< syn::Token![,] >()?; + } + } + + Ok( Self + { + custom, + hint, + }) + } +} diff --git a/module/core/former_meta/src/lib.rs b/module/core/former_meta/src/lib.rs index e02c582eaf..cf131eef6f 100644 --- a/module/core/former_meta/src/lib.rs +++ b/module/core/former_meta/src/lib.rs @@ -4,7 +4,8 @@ #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] #[ cfg( feature = "enabled" ) ] -mod derive +// #[ cfg( feature = "derive_component_from" ) ] +mod component { //! @@ -14,8 +15,6 @@ mod derive #[ allow( unused_imports ) ] use macro_tools::prelude::*; - #[ cfg( feature = "derive_former" ) ] - pub mod former; #[ cfg( feature = "derive_component_from" ) ] pub mod component_from; #[ cfg( feature = "derive_from_components" ) ] @@ -27,79 +26,46 @@ mod derive } -/// -/// Derive macro to generate former for a structure. Former is variation of Builder Pattern. -/// +#[ allow( unused_imports ) ] +use macro_tools::prelude::*; +#[ cfg( feature = "derive_former" ) ] +mod derive_former; -/// Derives a 'Former' for a struct, implementing a variation of the Builder Pattern. -/// -/// This macro simplifies the creation of builder patterns for structs by automatically -/// generating a 'former' (builder) struct and implementation. It supports customization -/// through attributes to control default values, setter generation, subformer inclusion, -/// and field aliases. +/// Derive macro for generating a `Former` struct, applying a Builder Pattern to the annotated struct. /// -/// # Attributes : -/// - `perform` : Specifies a method to call on the built object immediately after its construction. -/// - `default` : Sets a default value for a field. -/// - `setter` : Enables or disables the generation of a setter method for a field. -/// - `subformer` : Defines a sub-former for complex field types, allowing nested builders. -/// - `alias` : Creates an alias for a field setter. -/// - `doc` : Adds documentation to the generated setter methods. (deprecated) +/// This macro simplifies the construction of complex objects by automatically generating a builder (former) for +/// the specified struct. It supports extensive customization through attributes that control defaults, setter generation, +/// and field customization, allowing for flexible and fluent object construction. /// -/// # Input Example : -/// -/// ```rust -/// use former::Former; +/// # Struct Attributes /// -/// #[ derive( Debug, PartialEq, Former ) ] -/// #[ perform( fn greet_user() ) ] -/// pub struct UserProfile -/// { -/// #[default(1)] -/// age : i32, +/// - `debug`: Enables debug mode which can be used to print or log the internal state of the builder for debugging purposes. +/// - `perform`: Specifies a custom method to be invoked automatically at the end of the build process. +/// - `storage_fields`: Specifies fields that should be treated as part of the storage for the former. +/// - `mutator`: Defines a custom mutator class or function to manipulate the data just before the object is finalized. /// -/// username : String, +/// # Field Attributes /// -/// #[alias(bio)] -/// bio_optional : Option< String >, // Fields could be optional -/// } -/// -/// impl UserProfile -/// { -/// fn greet_user(self) -> Self -/// { -/// println!("Hello, {}", self.username); -/// self -/// } -/// } -/// -/// let profile = UserProfile::former() -/// .age( 30 ) -/// .username( "JohnDoe".to_string() ) -/// .bio_optional( "Software Developer".to_string() ) // Optionally provide a bio -/// .form(); -/// // .perform(); // same as `form()` but will execute method passed to perform attribute -/// -/// dbg!( &profile ); -/// // Expected output: -/// // &profile = UserProfile { -/// // age: 30, -/// // username: "JohnDoe", -/// // bio_optional: Some("Software Developer"), -/// // } -/// ``` +/// - `former`: General attribute to specify various options like defaults or inclusion in the former. +/// - `scalar`: Indicates that the field is a scalar value, enabling direct assignment without the need for a sub-former. +/// - `container`: Marks the field as a container that can use specific former methods to manage its contents. +/// - `subform`: Specifies that the field should utilize a nested former, facilitating the construction of complex nested structures. /// -/// # Generated Code Example : +/// # Usage Example /// -/// Assuming the struct above, the macro generates something like this : +/// Below is a typical usage example where the macro is applied to a struct: /// /// ```rust -/// # #[ cfg( feature = "enabled" ) ] -/// # #[ allow( dead_code ) ] +/// +/// # #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ] /// # fn main() /// # { +/// use former::Former; /// -/// #[ derive( Debug, PartialEq ) ] +/// // Use attribute debug to print expanded code. +/// #[ derive( Debug, PartialEq, Former ) ] +/// // Uncomment to see what derive expand into +/// // #[ debug ] /// pub struct UserProfile /// { /// age : i32, @@ -107,154 +73,10 @@ mod derive /// bio_optional : Option< String >, // Fields could be optional /// } /// -/// impl UserProfile -/// { -/// fn greet_user(self) -> Self -/// { -/// println!("Hello, {}", self.username); -/// self -/// } -/// } -/// -/// impl UserProfile -/// { -/// #[ inline( always ) ] -/// pub fn former() -> UserProfileFormer< UserProfile, former::ReturnFormed > -/// { -/// UserProfileFormer::< UserProfile, former::ReturnFormed >::new() -/// } -/// } -/// -/// #[ derive( Debug, Default ) ] -/// pub struct UserProfileFormerStorage -/// { -/// age : Option< i32 >, -/// username : Option< String >, -/// bio_optional : Option< String >, -/// } -/// -/// pub struct UserProfileFormer -/// < -/// Context = UserProfile, -/// End = former::ReturnFormed, -/// > -/// where -/// End : former::FormingEnd< UserProfile, Context >, -/// { -/// storage : UserProfileFormerStorage, -/// context : Option< Context >, -/// on_end : Option< End >, -/// } -/// -/// impl< Context, End > UserProfileFormer< Context, End > -/// where -/// End : former::FormingEnd< UserProfile, Context >, -/// { -/// #[ inline( always ) ] -/// pub fn form( mut self ) -> UserProfile -/// { -/// let age = if self.storage.age.is_some() -/// { -/// self.storage.age.take().unwrap() -/// } -/// else -/// { -/// (1).into() -/// }; -/// let username = if self.storage.username.is_some() -/// { -/// self.storage.username.take().unwrap() -/// } -/// else -/// { -/// String::default() -/// }; -/// let bio_optional = if self.storage.bio_optional.is_some() -/// { -/// Some( self.storage.bio_optional.take().unwrap() ) -/// } -/// else -/// { -/// None -/// }; -/// UserProfile { age, username, bio_optional } -/// } -/// -/// #[ inline( always ) ] -/// pub fn perform( self ) -> UserProfile -/// { -/// let result = self.form(); -/// return result.greet_user(); -/// } -/// -/// // qqq : xxx : outdated, update -/// #[ inline( always ) ] -/// pub fn new() -> UserProfileFormer< UserProfile, former::ReturnFormed > -/// { -/// UserProfileFormer::< UserProfile, former::ReturnFormed >::begin( None, former::ReturnFormed ) -/// } -/// -/// #[ inline( always ) ] -/// pub fn begin( context : Option< Context >, on_end : End ) -> Self -/// { -/// Self -/// { -/// storage : Default::default(), -/// context, -/// on_end : Some( on_end ), -/// } -/// } -/// -/// #[ inline( always ) ] -/// pub fn end( mut self ) -> Context -/// { -/// let on_end = self.on_end.take().unwrap(); -/// let context = self.context.take(); -/// let formed = self.form(); -/// on_end.call( formed, context ) -/// } -/// -/// #[ inline ] -/// pub fn age< Src >( mut self, src : Src ) -> Self -/// where -/// Src : Into< i32 >, -/// { -/// self.storage.age = Some( src.into() ); -/// self -/// } -/// -/// #[ inline ] -/// pub fn username< Src >( mut self, src : Src ) -> Self -/// where -/// Src : Into< String >, -/// { -/// self.storage.username = Some( src.into() ); -/// self -/// } -/// -/// #[ inline ] -/// pub fn bio_optional< Src >( mut self, src : Src ) -> Self -/// where -/// Src : Into< String >, -/// { -/// self.storage.bio_optional = Some( src.into() ); -/// self -/// } -/// -/// #[inline] -/// pub fn bio< Src >( mut self, src : Src ) -> Self -/// where -/// Src : Into< String >, -/// { -/// self.storage.bio_optional = Some( src.into() ); -/// self -/// } -/// } -/// /// let profile = UserProfile::former() /// .age( 30 ) /// .username( "JohnDoe".to_string() ) -/// .bio_optional( "Software Developer".to_string() ) +/// .bio_optional( "Software Developer".to_string() ) // Optionally provide a bio /// .form(); /// /// dbg!( &profile ); @@ -264,17 +86,29 @@ mod derive /// // username: "JohnDoe", /// // bio_optional: Some("Software Developer"), /// // } +/// /// # } +/// /// ``` /// -/// This generated code allows building an instance of `MyStruct` fluently, with optional customization for each field. +/// This pattern enables fluent and customizable construction of `UserProfile` instances, allowing for easy setting and modification of its fields. #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "derive_former" ) ] -#[ proc_macro_derive( Former, attributes( debug, perform, default, setter, subformer, alias, doc, embed ) ) ] +#[ + proc_macro_derive + ( + Former, + attributes + ( + debug, perform, storage_fields, mutator, // struct attributes + former, scalar, container, subform, // field attributes + ) + ) +] pub fn former( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { - let result = derive::former::former( input ); + let result = derive_former::former( input ); match result { Ok( stream ) => stream.into(), @@ -327,7 +161,7 @@ pub fn former( input : proc_macro::TokenStream ) -> proc_macro::TokenStream #[ proc_macro_derive( ComponentFrom, attributes( debug ) ) ] pub fn component_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { - let result = derive::component_from::component_from( input ); + let result = component::component_from::component_from( input ); match result { Ok( stream ) => stream.into(), @@ -418,7 +252,7 @@ pub fn component_from( input : proc_macro::TokenStream ) -> proc_macro::TokenStr #[ proc_macro_derive( ComponentAssign, attributes( debug ) ) ] pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { - let result = derive::component_assign::component_assign( input ); + let result = component::component_assign::component_assign( input ); match result { Ok( stream ) => stream.into(), @@ -665,12 +499,13 @@ pub fn component_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenS /// take_smaller_opts( &options2 ); /// ``` /// + #[ cfg( feature = "enabled" ) ] #[ cfg( all( feature = "derive_component_assign", feature = "derive_components_assign" ) ) ] #[ proc_macro_derive( ComponentsAssign, attributes( debug ) ) ] pub fn components_assign( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { - let result = derive::components_assign::components_assign( input ); + let result = component::components_assign::components_assign( input ); match result { Ok( stream ) => stream.into(), @@ -771,7 +606,7 @@ pub fn components_assign( input : proc_macro::TokenStream ) -> proc_macro::Token #[ proc_macro_derive( FromComponents, attributes( debug ) ) ] pub fn from_components( input : proc_macro::TokenStream ) -> proc_macro::TokenStream { - let result = derive::from_components::from_components( input ); + let result = component::from_components::from_components( input ); match result { Ok( stream ) => stream.into(), diff --git a/module/core/macro_tools/src/attr.rs b/module/core/macro_tools/src/attr.rs index 51a3fbe10d..c3a3cc2707 100644 --- a/module/core/macro_tools/src/attr.rs +++ b/module/core/macro_tools/src/attr.rs @@ -97,6 +97,108 @@ pub( crate ) mod private return Ok( false ) } + /// Checks if the given attribute name is a standard Rust attribute. + /// + /// Standard Rust attributes are those which are recognized and processed + /// directly by the Rust compiler. They influence various aspects of compilation, + /// including but not limited to conditional compilation, optimization hints, + /// code visibility, and procedural macro behavior. + /// + /// This function is useful when developing tools that need to interact with or + /// understand the significance of specific attributes in Rust source code, such + /// as linters, code analyzers, or procedural macros. + /// + /// This function does not cover all possible attributes but includes many of the + /// common ones that are relevant to most Rust projects. Developers are encouraged + /// to update this function as needed to suit more specialized needs, especially + /// when dealing with nightly-only compiler attributes or deprecated ones. + /// + /// # Parameters + /// - `attr_name`: A string slice that holds the name of the attribute to check. + /// + /// # Returns + /// Returns `true` if `attr_name` is a recognized standard Rust attribute. Otherwise, + /// returns `false`. + /// + /// # Examples + /// + /// Standard attributes: + /// + /// ``` + /// assert_eq!( macro_tools::attr::is_standard( "cfg" ), true ); + /// assert_eq!( macro_tools::attr::is_standard( "inline" ), true ); + /// assert_eq!( macro_tools::attr::is_standard( "derive" ), true ); + /// ``` + /// + /// Non-standard or custom attributes: + /// + /// ``` + /// assert_eq!( macro_tools::attr::is_standard( "custom_attr" ), false ); + /// assert_eq!( macro_tools::attr::is_standard( "my_attribute" ), false ); + /// ``` + /// + + pub fn is_standard<'a>( attr_name : &'a str ) -> bool + { + match attr_name + { + // Conditional compilation + "cfg" | "cfg_attr" => true, + + // Compiler instructions and optimizations + "inline" | "repr" | "derive" | "allow" | "warn" | "deny" | "forbid" => true, + + // Testing attributes + "test" | "bench" => true, + + // Documentation attributes + "doc" => true, + + // Visibility and accessibility + "pub" => true, // This would typically need context to be accurate + + // Safety and ABI + "unsafe" | "no_mangle" | "extern" => true, + + // Module and Crate configuration + "path" | "macro_use" | "crate_type" | "crate_name" => true, + + // Linking + "link" | "link_name" | "link_section" => true, + + // Usage warnings + "must_use" => true, + + // Other attributes + "cold" | "export_name" | "global_allocator" => true, + + // Module handling + "used" | "unused" => true, + + // Procedural macros and hygiene + "proc_macro" | "proc_macro_derive" | "proc_macro_attribute" => true, + + // Stability attributes + "stable" | "unstable" | "rustc_const_unstable" | "rustc_const_stable" | + "rustc_diagnostic_item" | "rustc_deprecated" | "rustc_legacy_const_generics" => true, + + // Special compiler attributes + "feature" | "non_exhaustive" => true, + + // Future compatibility + "rustc_paren_sugar" | "rustc_insignificant_dtor" => true, + + // Type system extensions + "opaque" => true, + + // Miscellaneous + "track_caller" => true, + + // Default case + _ => false, + } + } + /// /// Attribute which is inner. /// @@ -316,6 +418,7 @@ pub mod exposed { equation, has_debug, + is_standard, AttributesInner, AttributesOuter, AttributedIdent, @@ -326,4 +429,3 @@ pub mod exposed pub mod prelude { } - diff --git a/module/core/macro_tools/src/derive.rs b/module/core/macro_tools/src/derive.rs new file mode 100644 index 0000000000..9f29444e48 --- /dev/null +++ b/module/core/macro_tools/src/derive.rs @@ -0,0 +1,104 @@ +//! +//! Macro helpers around derive macro and structure [`syn::DeriveInput`]. +//! + +/// Internal namespace. +pub( crate ) mod private +{ + use super::super::*; + use syn::punctuated::Punctuated; + + /// + /// Extracts the named fields from a struct defined in a `syn::DeriveInput`. + /// + /// This function specifically handles `syn::DeriveInput` that represent structs + /// with named fields. It will return an error if the provided AST does not conform to these expectations. + /// + /// # Example + /// + /// ```rust, ignore + /// let ast = match syn::parse::< syn::DeriveInput >( input ) + /// { + /// Ok( syntax_tree ) => syntax_tree, + /// Err( err ) => return Err( err ), + /// }; + /// let fields = derive.named_fields( &ast ); + /// ``` + + pub fn named_fields< 'a >( ast : &'a syn::DeriveInput ) -> crate::Result< &'a Punctuated< syn::Field, syn::token::Comma > > + { + + 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_err!( ast, "Unknown format of data, expected syn::Fields::Named( ref fields_named )\n {}", qt!{ #ast } ) ), + }, + _ => return Err( syn_err!( ast, "Unknown format of data, expected syn::Data::Struct( ref data_struct )\n {}", qt!{ #ast } ) ), + }; + + Ok( fields ) + } + +} + +#[ 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:: + { + named_fields, + }; + +} + +/// Parented 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 derive; + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::prelude::*; + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + }; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +pub mod prelude +{ + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + }; + +} diff --git a/module/core/macro_tools/src/diag.rs b/module/core/macro_tools/src/diag.rs index f35ab49712..739d92f527 100644 --- a/module/core/macro_tools/src/diag.rs +++ b/module/core/macro_tools/src/diag.rs @@ -86,39 +86,43 @@ pub( crate ) mod private result } - /// Formats a debugging report for a pair of token streams, showing the original and generated code. + /// Formats a debugging report for code transformation processes, detailing both the original and generated code for easy comparison and review. /// - /// This function takes two inputs: the original code as an `IntoTokens` (which can be converted into a `proc_macro2::TokenStream`), - /// and the generated code as a `proc_macro2::TokenStream`. It formats both inputs with indentation for better readability, - /// labeling them as "original" and "generated" respectively. + /// This function creates a structured report comprising the initial input code, the resulting generated code, and an explanatory context. It is designed to facilitate debugging and documentation of code transformations, such as those performed in procedural macros or similar code generation tasks. The report categorizes the information into labeled sections to enhance readability and traceability. /// - /// Ensure the correct conversion of `proc_macro::TokenStream` to `proc_macro2::TokenStream` where necessary, - /// especially when interfacing with procedural macros' `input` parameter + /// This function helps visualize the changes from the original to the generated code, assisting developers in verifying and understanding the transformations applied during code generation processes. /// /// # Parameters - /// - `input`: The original input code that can be converted into a `proc_macro2::TokenStream`. - /// - `output`: The generated code as a `proc_macro2::TokenStream`. /// - /// # Returns - /// A `String` containing the formatted debug report. + /// - `about` : A description or context explaining the purpose or nature of the transformation. This information is displayed at the beginning of the report to provide an overview of the code transformation context. + /// - `input` : The original code before transformation. This is typically the code that is subject to processing by macros or other code generation tools. + /// - `output` : The code generated as a result of the transformation. This reflects the changes or enhancements made to the original code. /// /// # Type Parameters - /// - `IntoTokens`: A type that can be converted into a `proc_macro2::TokenStream`. + /// + /// - `IntoAbout` : A type that can be converted into a string representation, providing a descriptive context for the report. + /// - `IntoInput` : A type representing the original code, which can be converted into a string format for display. + /// - `IntoOutput` : A type representing the generated code, which can be converted into a string format for display. + /// + /// # Returns + /// + /// A string containing the formatted debug report, organized into sections with appropriate labels and indentation to distinguish between the original and generated code segments. /// /// # Examples + /// /// ``` /// use macro_tools::exposed::*; /// - /// let original_input : proc_macro2::TokenStream = qt! + /// let original_input : proc_macro2::TokenStream = quote! /// { - /// #[ derive( Debug, PartialEq ) ] + /// #[derive(Debug, PartialEq)] /// pub struct MyStruct /// { /// pub field : i32, /// } /// }; /// - /// let generated_code : proc_macro2::TokenStream = qt! + /// let generated_code : proc_macro2::TokenStream = quote! /// { /// impl MyStruct /// { @@ -130,52 +134,55 @@ pub( crate ) mod private /// }; /// /// // Format the debug report for printing or logging - /// let formatted_report = debug_report_format( "derive :: MyDerive", original_input, &generated_code ); + /// let formatted_report = report_format( "Code Transformation for MyStruct", original_input, generated_code ); /// println!( "{}", formatted_report ); /// ``` /// - /// This will output a formatted report showing the original input code and the generated code side by side, - /// each line indented for clarity. - /// - pub fn debug_report_format< IntoAbout, IntoTokens > + + pub fn report_format< IntoAbout, IntoInput, IntoOutput > ( - about : IntoAbout, input : IntoTokens, output : &proc_macro2::TokenStream + about : IntoAbout, input : IntoInput, output : IntoOutput ) -> String where - IntoAbout : Into< String >, - // xxx : qqq : use AsRef<> - IntoTokens : Into< proc_macro2::TokenStream >, + IntoAbout : ToString, + IntoInput : ToString, + IntoOutput : ToString, { format!( "\n" ) + - &format!( " = context\n\n{}\n\n", indentation( " ", about.into(), "" ) ) + - &format!( " = original\n\n{}\n\n", indentation( " ", input.into().to_string(), "" ) ) + - &format!( " = generated\n\n{}\n", indentation( " ", qt!{ #output }.to_string(), "" ) ) + &format!( " = context\n\n{}\n\n", indentation( " ", about.to_string(), "" ) ) + + &format!( " = original\n\n{}\n\n", indentation( " ", input.to_string(), "" ) ) + + &format!( " = generated\n\n{}\n", indentation( " ", output.to_string(), "" ) ) } /// Prints a debugging report for a pair of token streams to the standard output. /// - /// This convenience function wraps `debug_report_format`, directly printing the formatted report to stdout. - /// It serves as a utility for debugging procedural macros, providing a clear comparison between original - /// and generated code. + /// This function acts as a utility for debugging transformations in procedural macros or other code generation scenarios. + /// It provides an immediate visual comparison of the original code versus the generated code by utilizing the `report_format` + /// function to format the output and then printing it directly to the standard output. This can be particularly helpful for + /// real-time debugging and quick assessments without requiring additional output management. /// /// # Parameters and Type Parameters - /// - Same as `debug_report_format`. + /// - `about` : A description of the code transformation context or operation. This is used to headline the generated report. + /// - `input` : The original code or token stream before transformation. This is what the code looked like prior to any procedural manipulations. + /// - `output` : The transformed or generated code or token stream as a result of the macro or code transformation process. + /// + /// The types for these parameters are expected to be convertible to strings, matching the `report_format` function's requirements. /// /// # Examples /// - /// ``` + /// ```rust /// use macro_tools::exposed::*; /// - /// let original_input : proc_macro2::TokenStream = qt! + /// let original_input : proc_macro2::TokenStream = quote! /// { - /// #[ derive( Debug, PartialEq ) ] + /// #[derive(Debug, PartialEq)] /// pub struct MyStruct /// { /// pub field : i32, /// } /// }; /// - /// let generated_code : proc_macro2::TokenStream = qt! + /// let generated_code : proc_macro2::TokenStream = quote! /// { /// impl MyStruct /// { @@ -187,21 +194,23 @@ pub( crate ) mod private /// }; /// /// // Directly print the debug report - /// debug_report_print( "derive :: MyDerive", original_input, &generated_code ); + /// report_print( "Code Transformation for MyStruct", original_input, generated_code ); /// ``` /// - /// This will output a formatted report showing the original input code and the generated code side by side, - /// each line indented for clarity. + /// The above example demonstrates how the `report_print` function can be used to visualize the changes from original input code to the generated code, + /// helping developers to verify and understand the modifications made during code generation processes. The output is formatted to show clear distinctions + /// between the 'original' and 'generated' sections, providing an easy-to-follow comparison. - pub fn debug_report_print< IntoAbout, IntoTokens > + pub fn report_print< IntoAbout, IntoInput, IntoOutput > ( - about : IntoAbout, input : IntoTokens, output : &proc_macro2::TokenStream + about : IntoAbout, input : IntoInput, output : IntoOutput ) where - IntoAbout : Into< String >, - IntoTokens : Into< proc_macro2::TokenStream >, + IntoAbout : ToString, + IntoInput : ToString, + IntoOutput : ToString, { - println!( "{}", debug_report_format( about, input, output ) ); + println!( "{}", report_format( about, input, output ) ); } /// @@ -408,8 +417,8 @@ pub mod exposed { Result, indentation, - debug_report_format, - debug_report_print, + report_format, + report_print, }; } diff --git a/module/core/macro_tools/src/generic_args.rs b/module/core/macro_tools/src/generic_args.rs new file mode 100644 index 0000000000..aeea032b5a --- /dev/null +++ b/module/core/macro_tools/src/generic_args.rs @@ -0,0 +1,191 @@ +//! +//! Manipulations on generic arguments. +//! +//! xxx : update documentation of file + +/// Internal namespace. +pub( crate ) mod private +{ + + /// A trait for converting a reference to an existing type into a `syn::AngleBracketedGenericArguments`. + /// + /// This trait provides a mechanism to transform various types that represent generic parameters, + /// such as `syn::Generics`, into a uniform `syn::AngleBracketedGenericArguments`. This is particularly + /// useful when working with Rust syntax trees in procedural macros, allowing for the manipulation + /// and merging of generic parameters from different syntactic elements. + pub trait IntoGenericArgs + { + /// Converts a reference of the implementing type into `syn::AngleBracketedGenericArguments`. + /// + /// This method should handle the conversion logic necessary to transform the implementing + /// type's generic parameter representations into the structured format required by + /// `syn::AngleBracketedGenericArguments`, which is commonly used to represent generic parameters + /// enclosed in angle brackets. + /// + /// # Returns + /// A new instance of `syn::AngleBracketedGenericArguments` representing the generic parameters + /// of the original type. + fn into_generic_args( &self ) -> syn::AngleBracketedGenericArguments; + } + + impl IntoGenericArgs for syn::Generics + { + fn into_generic_args( &self ) -> syn::AngleBracketedGenericArguments + { + let args = self.params.iter().map( | param | + { + match param + { + syn::GenericParam::Type( ty ) => syn::GenericArgument::Type( syn::Type::Path( syn::TypePath + { + qself: None, + path: ty.ident.clone().into(), + })), + syn::GenericParam::Lifetime( lifetime ) => syn::GenericArgument::Lifetime( lifetime.lifetime.clone() ), + syn::GenericParam::Const( const_param ) => syn::GenericArgument::Const( syn::Expr::Path( syn::ExprPath + { + attrs: vec![], + qself: None, + path: const_param.ident.clone().into(), + })), + } + }).collect(); + + syn::AngleBracketedGenericArguments + { + colon2_token: None, + lt_token: syn::token::Lt::default(), + args, + gt_token: syn::token::Gt::default(), + } + } + } + + /// Merges two `syn::AngleBracketedGenericArguments` instances into a new one, + /// prioritizing lifetime parameters before other types of generic arguments. + /// + /// This function takes two references to `syn::AngleBracketedGenericArguments` and + /// categorizes their arguments into lifetimes and other types. It then combines + /// them such that all lifetimes from both instances precede any other arguments in the + /// resulting `syn::AngleBracketedGenericArguments` instance. This is particularly useful + /// for ensuring that the merged generics conform to typical Rust syntax requirements where + /// lifetimes are declared before other generic parameters. + /// + /// # Arguments + /// + /// * `a` - A reference to the first `syn::AngleBracketedGenericArguments` instance, containing one or more generic arguments. + /// * `b` - A reference to the second `syn::AngleBracketedGenericArguments` instance, containing one or more generic arguments. + /// + /// # Returns + /// + /// Returns a new `syn::AngleBracketedGenericArguments` instance containing the merged + /// arguments from both `a` and `b`, with lifetimes appearing first. + /// + /// # Examples + /// + /// ``` + /// use macro_tools::{ + /// generic_args, + /// syn::{parse_quote, AngleBracketedGenericArguments}, + /// }; + /// + /// let a: AngleBracketedGenericArguments = parse_quote! { <'a, T: Clone, U: Default> }; + /// let b: AngleBracketedGenericArguments = parse_quote! { <'b, V: core::fmt::Debug> }; + /// let merged = generic_args::merge(&a, &b); + /// + /// let expected: AngleBracketedGenericArguments = parse_quote! { <'a, 'b, T: Clone, U: Default, V: core::fmt::Debug> }; + /// assert_eq!(merged, expected); + /// ``` + /// + /// This example demonstrates how lifetimes `'a` and `'b` are placed before other generic parameters + /// like `T`, `U`, and `V` in the merged result, adhering to the expected syntax order in Rust generics. + pub fn merge + ( + a : &syn::AngleBracketedGenericArguments, + b : &syn::AngleBracketedGenericArguments + ) -> syn::AngleBracketedGenericArguments + { + let mut lifetimes : syn::punctuated::Punctuated< syn::GenericArgument, syn::token::Comma > = syn::punctuated::Punctuated::new(); + let mut others : syn::punctuated::Punctuated< syn::GenericArgument, syn::token::Comma > = syn::punctuated::Punctuated::new(); + + // Function to categorize and collect arguments into lifetimes and others + let mut categorize_and_collect = |args : &syn::punctuated::Punctuated| + { + for arg in args.iter() + { + match arg + { + syn::GenericArgument::Lifetime( _ ) => lifetimes.push( arg.clone() ), + _ => others.push( arg.clone() ), + } + } + }; + + // Categorize and collect from both input arguments + categorize_and_collect( &a.args ); + categorize_and_collect( &b.args ); + + // Combine lifetimes and other arguments into final merged arguments + let mut args = syn::punctuated::Punctuated::new(); + args.extend( lifetimes ); + args.extend( others ); + + syn::AngleBracketedGenericArguments + { + colon2_token: None, // Adjust if needed based on context + lt_token: syn::token::Lt::default(), + args, + gt_token: syn::token::Gt::default(), + } + } + +} + +#[ 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:: + { + merge, + }; +} + +/// Orphan namespace of the module. +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + IntoGenericArgs, + }; +} + +/// Exposed namespace of the module. +pub mod exposed +{ + pub use super::protected as generic_args; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super:: + { + prelude::*, + }; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +pub mod prelude +{ +} diff --git a/module/core/macro_tools/src/generic_params.rs b/module/core/macro_tools/src/generic_params.rs new file mode 100644 index 0000000000..f2e852f125 --- /dev/null +++ b/module/core/macro_tools/src/generic_params.rs @@ -0,0 +1,535 @@ +//! +//! Manipulations on generic parameters. +//! +//! # Example of generic parameters +//! +//!```rust +//! +//! pub struct CommandFormer< K, Context = () > +//! where +//! K : core::hash::Hash + std::cmp::Eq, +//! { +//! properties : core::option::Option< std::collections::HashMap< K, String > >, +//! _phantom : core::marker::PhantomData< Context >, +//! } +//! +//! impl< K, Context > +//! CommandFormer< K, Context > +//! where +//! K : core::hash::Hash + std::cmp::Eq, +//! {} +//!``` +//! xxx : update documentation of file + +/// Internal namespace. +pub( crate ) mod private +{ + use super::super::*; + + /// A `GenericsWithWhere` struct to handle the parsing of Rust generics with an explicit `where` clause. + /// + /// This wrapper addresses the limitation in the `syn` crate where parsing `Generics` directly from a `ParseStream` + /// does not automatically handle associated `where` clauses. By integrating `where` clause parsing into the + /// `GenericsWithWhere`, this struct provides a seamless way to capture both the generics and their constraints + /// in scenarios where the `where` clause is crucial for type constraints and bounds in Rust macros and code generation. + /// + /// Usage: + /// ``` + /// let parsed_generics : macro_tools::GenericsWithWhere = syn::parse_str( "< T : Clone, U : Default = Default1 > where T : Default").unwrap(); + /// assert!( parsed_generics.generics.params.len() == 2 ); + /// assert!( parsed_generics.generics.where_clause.is_some() ); + /// ``` + /// + + #[ derive( Debug ) ] + pub struct GenericsWithWhere + { + /// Syn's generics parameters. + pub generics : syn::Generics, + } + + impl GenericsWithWhere + { + /// Unwraps the `GenericsWithWhere` to retrieve the inner `syn::Generics`. + pub fn unwrap( self ) -> syn::Generics + { + self.generics + } + + /// Parses a string to a `GenericsWithWhere`, specifically designed to handle generics syntax with where clauses effectively. + pub fn parse_from_str( s : &str ) -> syn::Result< GenericsWithWhere > + { + syn::parse_str::< GenericsWithWhere >( s ) + } + } + + impl syn::parse::Parse for GenericsWithWhere + { + fn parse( input : syn::parse::ParseStream< '_ > ) -> syn::Result< Self > + { + let generics : syn::Generics = input.parse()?; + let where_clause : Option< syn::WhereClause > = input.parse()?; + + let mut generics_clone = generics.clone(); + generics_clone.where_clause = where_clause; + + Ok( GenericsWithWhere + { + generics : generics_clone, + }) + } + } + + impl quote::ToTokens for GenericsWithWhere + { + fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) + { + self.generics.to_tokens( tokens ); + } + } + + impl From for syn::Generics + { + fn from( g : GenericsWithWhere ) -> Self + { + g.generics + } + } + + impl From for GenericsWithWhere + { + fn from( generics : syn::Generics ) -> Self + { + GenericsWithWhere { generics } + } + } + + /// Merges two `syn::Generics` instances into a new one. + /// + /// This function takes two references to `syn::Generics` and combines their + /// type parameters and where clauses into a new `syn::Generics` instance. If + /// both instances have where clauses, the predicates of these clauses are merged + /// into a single where clause. + /// + /// # Arguments + /// + /// * `a` - A reference to the first `syn::Generics` instance. + /// * `b` - A reference to the second `syn::Generics` instance. + /// + /// # Returns + /// + /// Returns a new `syn::Generics` instance containing the merged type parameters + /// and where clauses from `a` and `b`. + /// + /// # Examples + /// + /// + /// # use syn::{Generics, parse_quote}; + /// + /// let mut generics_a : syn::Generics = parse_quote!{ < T : Clone, U : Default > }; + /// generics_a.where_clause = parse_quote!{ where T : Default }; + /// let mut generics_b : syn::Generics = parse_quote!{ < V : core::fmt::Debug > }; + /// generics_b.where_clause = parse_quote!{ where V : Sized }; + /// let got = generic_params::merge( &generics_a, &generics_b ); + /// + /// let mut exp : syn::Generics = parse_quote! + /// { + /// < T : Clone, U : Default, V : core::fmt::Debug > + /// }; + /// exp.where_clause = parse_quote! + /// { + /// where + /// T : Default, + /// V : Sized + /// }; + /// + /// assert_eq!( got, exp ); + + pub fn merge( a : &syn::Generics, b : &syn::Generics ) -> syn::Generics + { + + let mut result = syn::Generics + { + params : Default::default(), + where_clause : None, + lt_token : Some( syn::token::Lt::default() ), + gt_token : Some( syn::token::Gt::default() ), + }; + + // Merge params + // result.params.extend( a.params.iter().chain( b.params.iter() ) ); + for param in &a.params + { + result.params.push( param.clone() ); + } + for param in &b.params + { + result.params.push( param.clone() ); + } + + // Merge where clauses + result.where_clause = match( &a.where_clause, &b.where_clause ) + { + ( Some( a_clause ), Some( b_clause ) ) => + { + let mut merged_where_clause = syn::WhereClause + { + where_token: a_clause.where_token, + predicates: a_clause.predicates.clone(), + }; + for predicate in &b_clause.predicates + { + merged_where_clause.predicates.push( predicate.clone() ); + } + Some( merged_where_clause ) + }, + ( Some( a_clause ), None ) => Some( a_clause.clone() ), + ( None, Some( b_clause ) ) => Some( b_clause.clone() ), + _ => None, + }; + + result + } + + /// Extracts parameter names from the given `Generics`, + /// dropping bounds, defaults, and the where clause. + /// + /// This function simplifies the generics to include only the names of the type parameters, + /// lifetimes, and const parameters, without any of their associated bounds or default values. + /// The resulting `Generics` will have an empty where clause. + /// + /// # Arguments + /// + /// * `generics` - The `Generics` instance from which to extract parameter names. + /// + /// # Returns + /// + /// Returns a new `Generics` instance containing only the names of the parameters. + /// + /// # Examples + /// + /// ```rust + /// # use macro_tools::syn::parse_quote; + /// + /// let mut generics : syn::Generics = parse_quote!{ < T : Clone + Default, U, 'a, const N : usize > }; + /// generics.where_clause = parse_quote!{ where T: core::fmt::Debug }; + /// // let generics : Generics = parse_quote!{ < T : Clone + Default, U, 'a, const N : usize > where T: core::fmt::Debug }; + /// let simplified_generics = macro_tools::generic_params::names( &generics ); + /// + /// assert_eq!( simplified_generics.params.len(), 4 ); // Contains T, U, 'a, and N + /// assert!( simplified_generics.where_clause.is_none() ); // Where clause is removed + /// ``` + + pub fn names( generics : &syn::Generics ) -> syn::Generics + { + // use syn::{ Generics, GenericParam, LifetimeDef, TypeParam, ConstParam }; + use syn::{ Generics, GenericParam, LifetimeParam, TypeParam, ConstParam }; + + let result = Generics + { + params : generics.params.iter().map( | param | match param + { + GenericParam::Type( TypeParam { ident, .. } ) => GenericParam::Type( TypeParam + { + attrs : Vec::new(), + ident : ident.clone(), + colon_token : None, + bounds : Default::default(), + eq_token : None, + default : None, + }), + GenericParam::Lifetime( LifetimeParam { lifetime, .. } ) => GenericParam::Lifetime( LifetimeParam + { + attrs : Vec::new(), + lifetime : lifetime.clone(), + colon_token : None, + bounds : Default::default(), + }), + GenericParam::Const( ConstParam { ident, ty, .. } ) => GenericParam::Const( ConstParam + { + attrs : Vec::new(), + const_token : Default::default(), + ident : ident.clone(), + colon_token : Default::default(), + ty : ty.clone(), + eq_token : Default::default(), + default : None, + }), + }).collect(), + where_clause : None, + lt_token : generics.lt_token, + gt_token : generics.gt_token, + }; + + result + } + + /// Decomposes `syn::Generics` into components suitable for different usage contexts in Rust implementations, + /// specifically focusing on different requirements for `impl` blocks and type definitions. + /// + /// This function prepares three versions of the generics: + /// - One preserving the full structure for `impl` declarations. + /// - One simplified for type definitions, removing bounds and defaults from type and const parameters, retaining only identifiers. + /// - One for the where clauses, if present, ensuring they are correctly punctuated. + /// + /// This helps in situations where you need different representations of generics for implementing traits, + /// defining types, or specifying trait bounds and conditions. + /// + /// # Examples + /// + /// ```rust + /// let code : syn::Generics = syn::parse_quote!{ <'a, T, const N : usize, U : Trait1> }; + /// let ( generics_with_defaults, generics_for_impl, generics_for_ty, generics_where ) = macro_tools::generic_params::decompose( &code ); + /// + /// // Use in a macro for generating code + /// macro_tools::qt! + /// { + /// impl < #generics_for_impl > MyTrait for Struct1 < #generics_for_ty > + /// where + /// #generics_where + /// { + /// // implementation details... + /// } + /// }; + /// ``` + /// + /// # Arguments + /// + /// * `generics` - A reference to the `syn::Generics` to be decomposed. + /// + /// # Returns + /// + /// Returns a tuple containing: + /// - `syn::punctuated::Punctuated`: Original generics with defaults, used where full specification is needed. + /// - `syn::punctuated::Punctuated`: Generics for `impl` blocks, retaining bounds but no defaults. + /// - `syn::punctuated::Punctuated`: Simplified generics for type definitions, only identifiers. + /// - `syn::punctuated::Punctuated`: Where clauses, properly punctuated for use in where conditions. + + pub fn decompose + ( + generics : &syn::Generics, + ) + -> + ( + syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, + syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, + ) + { + + let mut generics_with_defaults = generics.params.clone(); + punctuated::ensure_trailing_comma( &mut generics_with_defaults ); + + let mut generics_for_impl = syn::punctuated::Punctuated::new(); + let mut generics_for_ty = syn::punctuated::Punctuated::new(); + + // Process each generic parameter + for param in &generics.params + { + match param + { + syn::GenericParam::Type( type_param ) => + { + // Retain bounds for generics_for_impl, remove defaults + let impl_param = syn::GenericParam::Type( syn::TypeParam + { + attrs : vec![], + ident : type_param.ident.clone(), + colon_token : type_param.colon_token, + bounds : type_param.bounds.clone(), + eq_token : None, // Remove default token + default : None, // Remove default value + } ); + generics_for_impl.push_value( impl_param ); + generics_for_impl.push_punct( syn::token::Comma::default() ); + + // Simplify for generics_for_ty by removing all except identifiers + let ty_param = syn::GenericParam::Type( syn::TypeParam + { + attrs : vec![], + ident : type_param.ident.clone(), + colon_token : None, + bounds : syn::punctuated::Punctuated::new(), + eq_token : None, + default : None, + } ); + generics_for_ty.push_value( ty_param ); + generics_for_ty.push_punct( syn::token::Comma::default() ); + }, + syn::GenericParam::Const( const_param ) => + { + // Simplify const parameters by removing all details except the identifier + let impl_param = syn::GenericParam::Const( syn::ConstParam + { + attrs : vec![], + const_token : const_param.const_token, + ident : const_param.ident.clone(), + colon_token : const_param.colon_token, + ty : const_param.ty.clone(), + eq_token : None, + default : None, + } ); + generics_for_impl.push_value( impl_param ); + generics_for_impl.push_punct( syn::token::Comma::default() ); + + let ty_param = syn::GenericParam::Type( syn::TypeParam + { + attrs : vec![], + ident : const_param.ident.clone(), + colon_token : None, + bounds : syn::punctuated::Punctuated::new(), + eq_token : None, + default : None, + }); + generics_for_ty.push_value( ty_param ); + generics_for_ty.push_punct( syn::token::Comma::default() ); + }, + syn::GenericParam::Lifetime( lifetime_param ) => + { + // Lifetimes are added as-is to both generics_for_impl and generics_for_ty + generics_for_impl.push_value( syn::GenericParam::Lifetime( lifetime_param.clone() ) ); + generics_for_impl.push_punct( syn::token::Comma::default() ); + generics_for_ty.push_value( syn::GenericParam::Lifetime( lifetime_param.clone() ) ); + generics_for_ty.push_punct( syn::token::Comma::default() ); + } + } + } + + // Clone where predicates if present, ensuring they end with a comma + let generics_where = if let Some( where_clause ) = &generics.where_clause + { + let mut predicates = where_clause.predicates.clone(); + punctuated::ensure_trailing_comma( &mut predicates ); + predicates + } + else + { + syn::punctuated::Punctuated::new() + }; + + ( generics_with_defaults, generics_for_impl, generics_for_ty, generics_where ) + } + +// pub fn decompose +// ( +// generics : &syn::Generics +// ) +// -> +// ( +// syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, +// syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma >, +// syn::punctuated::Punctuated< syn::WherePredicate, syn::token::Comma >, +// ) +// { +// let mut generics_for_impl = generics.params.clone(); +// punctuated::ensure_trailing_comma( &mut generics_for_impl ); +// +// let mut generics_for_ty = syn::punctuated::Punctuated::new(); +// for param in &generics.params +// { +// match param +// { +// syn::GenericParam::Type( type_param ) => +// { +// let simplified = syn::GenericParam::Type( syn::TypeParam +// { +// attrs : vec![], +// ident : type_param.ident.clone(), +// colon_token : None, +// bounds : syn::punctuated::Punctuated::new(), +// eq_token : None, +// default : None, +// }); +// generics_for_ty.push_value( simplified ); +// generics_for_ty.push_punct( syn::token::Comma::default() ); +// }, +// syn::GenericParam::Const( const_param ) => +// { +// let simplified = syn::GenericParam::Type( syn::TypeParam +// { +// attrs : vec![], +// ident : const_param.ident.clone(), +// colon_token : None, +// bounds : syn::punctuated::Punctuated::new(), +// eq_token : None, +// default : None, +// }); +// generics_for_ty.push_value( simplified ); +// generics_for_ty.push_punct( syn::token::Comma::default() ); +// }, +// syn::GenericParam::Lifetime( lifetime_param ) => +// { +// generics_for_ty.push_value( syn::GenericParam::Lifetime( lifetime_param.clone() ) ); +// generics_for_ty.push_punct( syn::token::Comma::default() ); +// } +// } +// } +// +// let generics_where = if let Some( where_clause ) = &generics.where_clause +// { +// let mut predicates = where_clause.predicates.clone(); +// punctuated::ensure_trailing_comma( &mut predicates ); +// predicates +// } +// else +// { +// syn::punctuated::Punctuated::new() +// }; +// +// ( generics_for_impl, generics_for_ty, generics_where ) +// } + +} + +#[ 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:: + { + merge, + names, + decompose, + }; +} + +// xxx : external attr instead of internal? +/// Orphan namespace of the module. +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + GenericsWithWhere, + }; +} + +/// Exposed namespace of the module. +pub mod exposed +{ + pub use super::protected as generic_params; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super:: + { + prelude::*, + }; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +pub mod prelude +{ +} diff --git a/module/core/macro_tools/src/generics.rs b/module/core/macro_tools/src/generics.rs deleted file mode 100644 index 63f8496495..0000000000 --- a/module/core/macro_tools/src/generics.rs +++ /dev/null @@ -1,229 +0,0 @@ -//! -//! Manipulations on generic parameters. -//! -//! # Example of generic parameters -//! -//!```rust -//! -//! pub struct CommandFormer< K, Context = () > -//! where -//! K : core::hash::Hash + std::cmp::Eq, -//! { -//! properties : core::option::Option< std::collections::HashMap< K, String > >, -//! _phantom : core::marker::PhantomData< Context >, -//! } -//! -//! impl< K, Context > -//! CommandFormer< K, Context > -//! where -//! K : core::hash::Hash + std::cmp::Eq, -//! {} -//!``` - -/// Internal namespace. -pub( crate ) mod private -{ - - /// Merges two `syn::Generics` instances into a new one. - /// - /// This function takes two references to `syn::Generics` and combines their - /// type parameters and where clauses into a new `syn::Generics` instance. If - /// both instances have where clauses, the predicates of these clauses are merged - /// into a single where clause. - /// - /// # Arguments - /// - /// * `a` - A reference to the first `syn::Generics` instance. - /// * `b` - A reference to the second `syn::Generics` instance. - /// - /// # Returns - /// - /// Returns a new `syn::Generics` instance containing the merged type parameters - /// and where clauses from `a` and `b`. - /// - /// # Examples - /// - /// - /// # use syn::{Generics, parse_quote}; - /// - /// let mut generics_a : syn::Generics = parse_quote!{ < T : Clone, U : Default > }; - /// generics_a.where_clause = parse_quote!{ where T : Default }; - /// let mut generics_b : syn::Generics = parse_quote!{ < V : std::fmt::Debug > }; - /// generics_b.where_clause = parse_quote!{ where V : Sized }; - /// let got = generics::merge( &generics_a, &generics_b ); - /// - /// let mut exp : syn::Generics = parse_quote! - /// { - /// < T : Clone, U : Default, V : std::fmt::Debug > - /// }; - /// exp.where_clause = parse_quote! - /// { - /// where - /// T : Default, - /// V : Sized - /// }; - /// - /// assert_eq!( got, exp ); - - pub fn merge( a : &syn::Generics, b : &syn::Generics ) -> syn::Generics - { - - let mut result = syn::Generics - { - params : Default::default(), - where_clause : None, - lt_token : Some( syn::token::Lt::default() ), - gt_token : Some( syn::token::Gt::default() ), - }; - - // Merge params - for param in &a.params - { - result.params.push( param.clone() ); - } - for param in &b.params - { - result.params.push( param.clone() ); - } - - // Merge where clauses - result.where_clause = match( &a.where_clause, &b.where_clause ) - { - ( Some( a_clause ), Some( b_clause ) ) => - { - let mut merged_where_clause = syn::WhereClause - { - where_token: a_clause.where_token, - predicates: a_clause.predicates.clone(), - }; - for predicate in &b_clause.predicates - { - merged_where_clause.predicates.push( predicate.clone() ); - } - Some( merged_where_clause ) - }, - ( Some( a_clause ), None ) => Some( a_clause.clone() ), - ( None, Some( b_clause ) ) => Some( b_clause.clone() ), - _ => None, - }; - - result - } - - /// Extracts parameter names from the given `Generics`, - /// dropping bounds, defaults, and the where clause. - /// - /// This function simplifies the generics to include only the names of the type parameters, - /// lifetimes, and const parameters, without any of their associated bounds or default values. - /// The resulting `Generics` will have an empty where clause. - /// - /// # Arguments - /// - /// * `generics` - The `Generics` instance from which to extract parameter names. - /// - /// # Returns - /// - /// Returns a new `Generics` instance containing only the names of the parameters. - /// - /// # Examples - /// - /// ```rust - /// # use macro_tools::syn::parse_quote; - /// - /// let mut generics : syn::Generics = parse_quote!{ < T : Clone + Default, U, 'a, const N : usize > }; - /// generics.where_clause = parse_quote!{ where T: std::fmt::Debug }; - /// // let generics : Generics = parse_quote!{ < T : Clone + Default, U, 'a, const N : usize > where T: std::fmt::Debug }; - /// let simplified_generics = macro_tools::generics::params_names( &generics ); - /// - /// assert_eq!( simplified_generics.params.len(), 4 ); // Contains T, U, 'a, and N - /// assert!( simplified_generics.where_clause.is_none() ); // Where clause is removed - /// ``` - - pub fn params_names( generics : &syn::Generics ) -> syn::Generics - { - // use syn::{ Generics, GenericParam, LifetimeDef, TypeParam, ConstParam }; - use syn::{ Generics, GenericParam, LifetimeParam, TypeParam, ConstParam }; - - let result = Generics - { - params : generics.params.iter().map( | param | match param - { - GenericParam::Type( TypeParam { ident, .. } ) => GenericParam::Type( TypeParam - { - attrs : Vec::new(), - ident : ident.clone(), - colon_token : None, - bounds : Default::default(), - eq_token : None, - default : None, - }), - GenericParam::Lifetime( LifetimeParam { lifetime, .. } ) => GenericParam::Lifetime( LifetimeParam - { - attrs : Vec::new(), - lifetime : lifetime.clone(), - colon_token : None, - bounds : Default::default(), - }), - GenericParam::Const( ConstParam { ident, ty, .. } ) => GenericParam::Const( ConstParam - { - attrs : Vec::new(), - const_token : Default::default(), - ident : ident.clone(), - colon_token : Default::default(), - ty : ty.clone(), - eq_token : Default::default(), - default : None, - }), - }).collect(), - where_clause : None, - lt_token : generics.lt_token, - gt_token : generics.gt_token, - }; - - result - } - -} - -#[ 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::merge; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::private::params_names; -} - -/// 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 generics; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super:: - { - prelude::*, - }; -} - -/// Prelude to use essentials: `use my_module::prelude::*`. -pub mod prelude -{ -} diff --git a/module/core/macro_tools/src/item.rs b/module/core/macro_tools/src/item.rs new file mode 100644 index 0000000000..64897375e7 --- /dev/null +++ b/module/core/macro_tools/src/item.rs @@ -0,0 +1,129 @@ +//! xxx : update documentation of file + +/// Internal namespace. +pub( crate ) mod private +{ + use super::super::*; + + /// Ensures the last field in a struct has a trailing comma. + /// + /// This function checks and modifies the fields of a given struct, `input`, ensuring that the last field, whether in + /// named or unnamed structs, ends with a trailing comma. This adjustment is commonly needed in macro-generated + /// code to maintain consistency and syntactical correctness across different struct types, including unit structs + /// which are unaffected as they do not contain fields. + /// + /// # Arguments + /// + /// * `input` - A reference to the struct (`syn::ItemStruct`) whose fields are to be checked and modified. + /// + /// # Returns + /// + /// Returns a modified clone of the input struct (`syn::ItemStruct`) where the last field in named or unnamed + /// structs has a trailing comma. Unit structs remain unchanged as they do not contain fields. + /// + /// # Examples + /// + /// ``` + /// use macro_tools:: + /// { + /// syn::{ parse_quote, ItemStruct }, + /// quote::quote, + /// }; + /// + /// // Create a struct using `parse_quote!` macro + /// let input_struct : ItemStruct = parse_quote! + /// { + /// struct Example + /// { + /// field1 : i32, + /// field2 : String + /// } + /// }; + /// + /// // Apply `ensure_comma` to ensure the last field has a trailing comma + /// let modified_struct = macro_tools::item::ensure_comma( &input_struct ); + /// + /// // Now `modified_struct` will have a trailing comma after `field2` + /// assert_eq!( quote!( #modified_struct ).to_string(), quote! + /// { + /// struct Example + /// { + /// field1 : i32, + /// field2 : String, + /// } + /// }.to_string() ); + /// ``` + + pub fn ensure_comma( input : &syn::ItemStruct ) -> syn::ItemStruct + { + let mut new_input = input.clone(); // Clone the input to modify it + + match &mut new_input.fields + { + // Handle named fields + syn::Fields::Named( syn::FieldsNamed { named, .. } ) => + { + punctuated::ensure_trailing_comma( named ) + }, + // Handle unnamed fields (tuples) + syn::Fields::Unnamed( syn::FieldsUnnamed { unnamed, .. } ) => + { + punctuated::ensure_trailing_comma( unnamed ) + }, + // Do nothing for unit structs + syn::Fields::Unit => {} + } + + new_input + } + +} + +#[ 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:: + { + ensure_comma, + }; +} + +// xxx : external attr instead of internal? +/// Orphan namespace of the module. +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + }; +} + +/// Exposed namespace of the module. +pub mod exposed +{ + pub use super::protected as item; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super:: + { + prelude::*, + }; +} + +/// 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 6bf4f43554..d447b4b67b 100644 --- a/module/core/macro_tools/src/lib.rs +++ b/module/core/macro_tools/src/lib.rs @@ -3,26 +3,28 @@ #![ doc( html_root_url = "https://docs.rs/proc_macro_tools/latest/proc_macro_tools/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] +/// Modular files. #[ cfg( feature = "enabled" ) ] -pub mod attr; -#[ cfg( feature = "enabled" ) ] -pub mod container_kind; -#[ cfg( feature = "enabled" ) ] -pub mod diag; -#[ cfg( feature = "enabled" ) ] -pub mod generic_analyze; -#[ cfg( feature = "enabled" ) ] -pub mod generics; -#[ cfg( feature = "enabled" ) ] -pub mod name; -#[ cfg( feature = "enabled" ) ] -pub mod quantifier; -#[ cfg( feature = "enabled" ) ] -pub mod tokens; -#[ cfg( feature = "enabled" ) ] -pub mod typ; -#[ cfg( feature = "enabled" ) ] -pub mod type_struct; +#[ path = "." ] +mod file +{ + use super::*; + pub mod attr; + pub mod container_kind; + pub mod derive; + pub mod diag; + pub mod generic_analyze; + pub mod generic_args; + pub mod generic_params; + pub mod item; + pub mod name; + pub mod phantom; + pub mod punctuated; + pub mod quantifier; + pub mod tokens; + pub mod typ; + pub mod type_struct; +} /// /// Dependencies of the module. @@ -35,7 +37,6 @@ pub mod dependency pub use ::quote; pub use ::proc_macro2; pub use ::interval_adapter; - // pub use ::type_constructor; } #[ doc( inline ) ] @@ -52,12 +53,22 @@ pub mod protected pub use super:: { orphan::*, + }; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::file:: + { attr::orphan::*, container_kind::orphan::*, + derive::orphan::*, diag::orphan::*, generic_analyze::orphan::*, - generics::orphan::*, + generic_args::orphan::*, + generic_params::orphan::*, + item::orphan::*, name::orphan::*, + phantom::orphan::*, + punctuated::orphan::*, quantifier::orphan::*, tokens::orphan::*, typ::orphan::*, @@ -91,24 +102,27 @@ pub mod exposed pub use super:: { prelude::*, + }; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::file:: + { attr::exposed::*, container_kind::exposed::*, + derive::orphan::*, diag::exposed::*, generic_analyze::exposed::*, - generics::exposed::*, + generic_args::exposed::*, + generic_params::exposed::*, + item::exposed::*, name::exposed::*, + phantom::exposed::*, + punctuated::exposed::*, quantifier::exposed::*, tokens::exposed::*, typ::exposed::*, type_struct::exposed::*, }; - // #[ doc( inline ) ] - // #[ allow( unused_imports ) ] - // pub use super::quantifier:: - // { - // Pair, - // Many, - // }; } /// Prelude to use essentials: `use my_module::prelude::*`. @@ -157,14 +171,19 @@ pub mod prelude #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use super:: + pub use super::file:: { attr::prelude::*, container_kind::prelude::*, + derive::orphan::*, diag::prelude::*, generic_analyze::prelude::*, - generics::prelude::*, + generic_args::prelude::*, + generic_params::prelude::*, + item::prelude::*, name::prelude::*, + phantom::prelude::*, + punctuated::prelude::*, quantifier::prelude::*, tokens::prelude::*, typ::prelude::*, diff --git a/module/core/macro_tools/src/phantom.rs b/module/core/macro_tools/src/phantom.rs new file mode 100644 index 0000000000..7c9e3bbbda --- /dev/null +++ b/module/core/macro_tools/src/phantom.rs @@ -0,0 +1,224 @@ +//! xxx : update documentation of file + +/// Internal namespace. +pub( crate ) mod private +{ + use super::super::*; + + /// Adds a `PhantomData` field to a struct to manage generic parameter usage. + /// + /// This function clones a given `syn::ItemStruct`, calculates the appropriate `PhantomData` usage + /// based on the struct's generic parameters, and adds a corresponding `PhantomData` field. This field + /// helps in handling ownership and lifetime indications for generic parameters, ensuring that they + /// are correctly accounted for in type checking, even if they are not directly used in the struct's + /// fields. + /// + /// # Parameters + /// - `input`: A reference to the `syn::ItemStruct` which describes the structure to which the + /// `PhantomData` field will be added. + /// + /// # Returns + /// Returns a new `syn::ItemStruct` with the `PhantomData` field added to its list of fields. + /// + /// # Examples + /// ```rust + /// use syn::{ parse_quote, ItemStruct }; + /// + /// let input_struct: ItemStruct = parse_quote! + /// { + /// pub struct MyStruct< T, U > + /// { + /// data : T, + /// } + /// }; + /// + /// let modified_struct = macro_tools::phantom::add_to_item( &input_struct ); + /// println!( "{:#?}", modified_struct ); + /// + /// // Output will include a _phantom field of type `PhantomData< ( T, U ) >` + /// ``` + /// + + pub fn add_to_item( input : &syn::ItemStruct ) -> syn::ItemStruct + { + + // Only proceed if there are generics + if input.generics.params.is_empty() + { + return item::ensure_comma( input ); + } + + // Clone the input struct to work on a modifiable copy + let mut input = input.clone(); + + // Prepare the tuple type for PhantomData based on the struct's generics + let phantom = tuple( &input.generics.params ); + + // Handle different field types: Named, Unnamed, or Unit + match &mut input.fields + { + syn::Fields::Named( fields ) => + { + let phantom_field : syn::Field = syn::parse_quote! + { + _phantom : #phantom + }; + + // Ensure there is a trailing comma if fields are already present + if !fields.named.empty_or_trailing() + { + fields.named.push_punct( Default::default() ); + } + fields.named.push( phantom_field ); + fields.named.push_punct( Default::default() ); // Add trailing comma after adding PhantomData + }, + syn::Fields::Unnamed( fields ) => + { + let phantom_field : syn::Field = syn::parse_quote! + { + #phantom + }; + + // Ensure there is a trailing comma if fields are already present + if !fields.unnamed.empty_or_trailing() + { + fields.unnamed.push_punct( Default::default() ); + } + fields.unnamed.push_value( phantom_field ); + fields.unnamed.push_punct( Default::default() ); // Ensure to add the trailing comma after PhantomData + }, + syn::Fields::Unit => + { + // No fields to modify in a unit struct + } + }; + + input + } + + /// Constructs a `PhantomData` type tuple from the generic parameters of a struct. + /// + /// This function generates a tuple type for `PhantomData` using the given generic parameters, + /// which includes types, lifetimes, and const generics. It ensures that the generated tuple + /// use all parameters. + /// + /// # Parameters + /// - `input`: A reference to a `Punctuated< GenericParam, Comma>` containing the generic parameters. + /// + /// # Returns + /// Returns a `syn::Type` that represents a `PhantomData` tuple incorporating all the generic parameters. + /// + /// # Examples + /// ```rust + /// use syn::{parse_quote, punctuated::Punctuated, GenericParam, token::Comma}; + /// use macro_tools::phantom::tuple; + /// + /// let generics: Punctuated< GenericParam, Comma > = parse_quote! { 'a, T, const N : usize }; + /// let phantom_type = tuple( &generics ); + /// println!( "{}", quote::quote! { #phantom_type } ); + /// // Output: core::marker::PhantomData< ( &'a (), *const T, N ) > + /// ``` + /// + pub fn tuple( input : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma > ) -> syn::Type + { + use proc_macro2::Span; + use syn::{ GenericParam, Type }; + + // Prepare the tuple type for PhantomData based on the struct's generics + let generics_tuple_type = + { + let generics_list = input.iter().map( | param | + { + match param + { + GenericParam::Type( type_param ) => + { + let path = &type_param.ident; + let path2 : syn::Type = parse_quote!{ *const #path }; + path2 + }, + GenericParam::Lifetime( lifetime_param ) => Type::Reference( syn::TypeReference + { + and_token : Default::default(), + lifetime : Some( lifetime_param.lifetime.clone() ), + mutability : None, + elem : Box::new( Type::Tuple( syn::TypeTuple + { + paren_token : syn::token::Paren( Span::call_site() ), + elems : syn::punctuated::Punctuated::new(), + })), + }), + GenericParam::Const( const_param ) => Type::Path( syn::TypePath + { + qself : None, + path : const_param.ident.clone().into(), + }), + } + }).collect::< syn::punctuated::Punctuated< _, syn::token::Comma > >(); + + Type::Tuple( syn::TypeTuple + { + paren_token : syn::token::Paren( Span::call_site() ), + elems : generics_list, + }) + }; + + let result : syn::Type = syn::parse_quote! + { + core::marker::PhantomData< #generics_tuple_type > + }; + + result + } + +} + +#[ 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:: + { + add_to_item, + tuple, + }; +} + +// xxx : external attr instead of internal? +/// Orphan namespace of the module. +pub mod orphan +{ + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::exposed::*; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super::private:: + { + }; +} + +/// Exposed namespace of the module. +pub mod exposed +{ + pub use super::protected as phantom; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super:: + { + prelude::*, + }; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +pub mod prelude +{ +} diff --git a/module/core/macro_tools/src/punctuated.rs b/module/core/macro_tools/src/punctuated.rs new file mode 100644 index 0000000000..5ea50730c4 --- /dev/null +++ b/module/core/macro_tools/src/punctuated.rs @@ -0,0 +1,60 @@ +// ! xxx : write description + +/// Internal namespace. +pub( crate ) mod private +{ + + /// Ensures that a `syn::punctuated::Punctuated` collection ends with a comma if it contains elements. + pub fn ensure_trailing_comma< T : Clone > + ( punctuated : &mut syn::punctuated::Punctuated< T, syn::token::Comma > ) + { + if !punctuated.empty_or_trailing() + { + punctuated.push_punct( syn::token::Comma::default() ); + } + } + +} + +#[ 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:: + { + ensure_trailing_comma, + }; +} + +/// 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 punctuated; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use super:: + { + prelude::*, + }; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +pub mod prelude +{ +} diff --git a/module/core/macro_tools/src/tokens.rs b/module/core/macro_tools/src/tokens.rs index b1740ad332..9f0cd32435 100644 --- a/module/core/macro_tools/src/tokens.rs +++ b/module/core/macro_tools/src/tokens.rs @@ -6,7 +6,7 @@ pub( crate ) mod private { use super::super::*; - use std::fmt; + use core::fmt; /// `Tokens` is a wrapper around `proc_macro2::TokenStream`. /// It is designed to facilitate the parsing and manipulation of token streams @@ -63,9 +63,9 @@ pub( crate ) mod private } } - impl std::fmt::Display for Tokens + impl core::fmt::Display for Tokens { - fn fmt( &self, f : &mut std::fmt::Formatter< '_ > ) -> std::fmt::Result + fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result { write!( f, "{}", self.inner.to_string() ) } @@ -137,9 +137,9 @@ pub( crate ) mod private } } - // impl std::fmt::Display for Equation + // impl core::fmt::Display for Equation // { - // fn fmt( &self, f : &mut std::fmt::Formatter< '_ > ) -> std::fmt::Result + // fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result // { // write!( f, "{}", self.left.to_string() ); // write!( f, "{}", self.op.to_string() ); diff --git a/module/core/macro_tools/src/typ.rs b/module/core/macro_tools/src/typ.rs index 81b48b675b..34d45e32b3 100644 --- a/module/core/macro_tools/src/typ.rs +++ b/module/core/macro_tools/src/typ.rs @@ -90,6 +90,68 @@ pub( crate ) mod private vec![ ty ] } +// /// Extract generics from a type. +// pub fn all_type_parameters( type_example : &syn::Type ) +// -> +// Option< syn::punctuated::Punctuated< syn::GenericArgument, syn::token::Comma > > +// { +// if let syn::Type::Path( type_path ) = type_example +// { +// let segments = &type_path.path.segments; +// let last_segment = segments.last()?; +// +// if let syn::PathArguments::AngleBracketed( generics ) = &last_segment.arguments +// { +// return Some( generics.args.clone() ); +// } +// } +// None +// } + + + /// Checks if a given [`syn::Type`] is an `Option` type. + /// + /// This function examines a type to determine if it represents an `Option`. + /// It is useful for scenarios where type-specific behavior needs to be conditional + /// on whether the type is optional or not. + /// + /// # Example + /// + /// ```rust + /// let type_string = "Option< i32 >"; + /// let parsed_type : syn::Type = syn::parse_str( type_string ).expect( "Type should parse correctly" ); + /// assert!( macro_tools::typ::is_optional( &parsed_type ) ); + /// ``` + /// + + pub fn is_optional( ty : &syn::Type ) -> bool + { + typ::type_rightmost( ty ) == Some( "Option".to_string() ) + } + + /// Extracts the first generic parameter from a given `syn::Type` if any exists. + /// + /// This function is designed to analyze a type and retrieve its first generic parameter. + /// It is particularly useful when working with complex types in macro expansions and needs + /// to extract specific type information for further processing. + /// +/// + /// # Example + /// ```rust + /// let type_string = "Result< Option< i32 >, Error >"; + /// let parsed_type : syn::Type = syn::parse_str( type_string ).expect( "Type should parse correctly" ); + /// let first_param = macro_tools::typ::parameter_first( &parsed_type ).expect( "Should have at least one parameter" ); + /// // Option< i32 > + /// ``` + + pub fn parameter_first( ty : &syn::Type ) -> Result< &syn::Type > + { + typ::type_parameters( ty, 0 ..= 0 ) + .first() + .copied() + .ok_or_else( || syn_err!( ty, "Expects at least one parameter here:\n {}", qt!{ #ty } ) ) + } + } #[ doc( inline ) ] @@ -108,7 +170,9 @@ pub mod protected { type_rightmost, type_parameters, - // xxx : rename + // all_type_parameters, + is_optional, + parameter_first, }; } diff --git a/module/core/macro_tools/tests/inc/attr.rs b/module/core/macro_tools/tests/inc/attr.rs new file mode 100644 index 0000000000..6bba6e98fc --- /dev/null +++ b/module/core/macro_tools/tests/inc/attr.rs @@ -0,0 +1,53 @@ + +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_test.rs b/module/core/macro_tools/tests/inc/attr_test.rs deleted file mode 100644 index 942289e7b3..0000000000 --- a/module/core/macro_tools/tests/inc/attr_test.rs +++ /dev/null @@ -1,24 +0,0 @@ - -use super::*; - -// - -#[ test ] -fn basic() -{ - - 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() ); - -} diff --git a/module/core/macro_tools/tests/inc/basic_test.rs b/module/core/macro_tools/tests/inc/basic.rs similarity index 100% rename from module/core/macro_tools/tests/inc/basic_test.rs rename to module/core/macro_tools/tests/inc/basic.rs diff --git a/module/core/macro_tools/tests/inc/derive.rs b/module/core/macro_tools/tests/inc/derive.rs new file mode 100644 index 0000000000..b6983e34d5 --- /dev/null +++ b/module/core/macro_tools/tests/inc/derive.rs @@ -0,0 +1,72 @@ + +use super::*; + +// + +#[test] +fn named_fields_with_named_fields() +{ + use syn::{parse_quote, punctuated::Punctuated, Field, token::Comma}; + use the_module::derive; + + let ast: syn::DeriveInput = parse_quote! + { + struct Test + { + a : i32, + b : String, + } + }; + + let result = derive::named_fields( &ast ).expect( "Expected successful extraction of named fields" ); + + let mut expected_fields = Punctuated::new(); + let field_a : Field = parse_quote! { a: i32 }; + let field_b : Field = parse_quote! { b: String }; + expected_fields.push_value( field_a); + expected_fields.push_punct( Comma::default() ); + expected_fields.push_value( field_b ); + expected_fields.push_punct( Comma::default() ); + + a_id!( format!( "{:?}", result ), format!( "{:?}", expected_fields ), "Fields did not match expected output" ); +} + +// + +#[ test ] +fn named_fields_with_tuple_struct() +{ + use syn::{ parse_quote }; + use the_module::derive::named_fields; + + let ast : syn::DeriveInput = parse_quote! + { + struct Test( i32, String ); + }; + + let result = named_fields( &ast ); + + assert!( result.is_err(), "Expected an error for tuple struct, but extraction was successful" ); +} + +// + +#[ test ] +fn named_fields_with_enum() +{ + use syn::{ parse_quote }; + use the_module::derive::named_fields; + + let ast : syn::DeriveInput = parse_quote! + { + enum Test + { + Variant1, + Variant2, + } + }; + + let result = named_fields( &ast ); + + assert!( result.is_err(), "Expected an error for enum, but extraction was successful" ); +} diff --git a/module/core/macro_tools/tests/inc/generic_args.rs b/module/core/macro_tools/tests/inc/generic_args.rs new file mode 100644 index 0000000000..8076737930 --- /dev/null +++ b/module/core/macro_tools/tests/inc/generic_args.rs @@ -0,0 +1,356 @@ + +use super::*; + +// + +#[ test ] +fn assumptions() +{ + + // let code : syn::ItemStruct = syn::parse_quote! + // { + // pub struct Struct1Former + // < + // Definition = Struct1FormerDefinition< (), Struct1, former::ReturnPreformed >, + // > + // {} + // }; + // tree_print!( code ); + + // let mut a : syn::Generics = parse_quote! + // { + // < 'a, T > + // }; + // let mut b : syn::IntoGenericArgs = parse_quote! + // { + // < (), Struct1, former::ReturnPreformed > + // }; + // let got = generic_params::generic_args::merge( &a.into(), &b.into() ); + // // let got = definition_extra_generics; + + // let mut _got : syn::Generics = parse_quote! + // { + // < Struct1, former::ReturnPreformed > + // }; + + // let mut _got : syn::Generics = parse_quote! + // { + // < (), Struct1, former::ReturnPreformed > + // }; + +} + +// + +#[ test ] +fn into_generic_args_empty_generics() +{ + use syn::{ Generics, AngleBracketedGenericArguments, token }; + use macro_tools::IntoGenericArgs; + use proc_macro2::Span; + + let generics = Generics::default(); + let got = generics.into_generic_args(); + let exp = AngleBracketedGenericArguments + { + colon2_token: None, + lt_token: token::Lt::default(), + args: syn::punctuated::Punctuated::new(), + gt_token: token::Gt::default(), + }; + a_id!( exp, got, "Failed into_generic_args_empty_generics: exp {:?}, got {:?}", exp, got ); +} + +// +#[ test ] +fn into_generic_args_single_type_parameter() +{ + use syn:: + { + Generics, + AngleBracketedGenericArguments, + parse_quote + }; + use macro_tools::IntoGenericArgs; + + // Generate the generics with a single type parameter using parse_quote + let generics : Generics = parse_quote! + { + < T > + }; + + // Create the exp AngleBracketedGenericArguments using parse_quote + let exp : AngleBracketedGenericArguments = parse_quote! + { + < T > + }; + + let got = generics.into_generic_args(); + a_id!( exp, got, "Failed into_generic_args_single_type_parameter: exp {:?}, got {:?}", exp, got ); +} + +/// + +#[ test ] +fn into_generic_args_single_lifetime_parameter() +{ + use syn:: + { + Generics, + AngleBracketedGenericArguments, + GenericArgument, + parse_quote, + punctuated::Punctuated + }; + use macro_tools::IntoGenericArgs; + + // Generate the generics using parse_quote to include a lifetime parameter + let generics : Generics = parse_quote! + { + < 'a > + }; + + // Create the exp AngleBracketedGenericArguments using parse_quote + let exp : AngleBracketedGenericArguments = parse_quote! + { + < 'a > + }; + + // Use the implementation to generate the actual output + let got = generics.into_generic_args(); + + // Debug prints for better traceability in case of failure + println!( "Expected: {:?}", exp ); + println!( "Got: {:?}", got ); + + // Assert to check if the exp matches the got + a_id!( exp, got, "Failed into_generic_args_single_lifetime_parameter: exp {:?}, got {:?}", exp, got ); +} + +#[ test ] +fn into_generic_args_single_const_parameter() +{ + use syn:: + { + Generics, + AngleBracketedGenericArguments, + GenericArgument, + Expr, + ExprPath, + Ident, + token::{ self, Lt, Gt }, + punctuated::Punctuated + }; + use macro_tools::IntoGenericArgs; + + // Use parse_quote to create the generic parameters + let generics : Generics = parse_quote! + { + < const N: usize > + }; + + let got = generics.into_generic_args(); + + // Manually construct the exp value + let mut args = Punctuated::new(); + args.push_value( GenericArgument::Const( Expr::Path( ExprPath + { + attrs: vec![], + qself: None, + path: syn::Path::from( Ident::new( "N", proc_macro2::Span::call_site() )), + }))); + + let exp = AngleBracketedGenericArguments + { + colon2_token: None, + lt_token: Lt::default(), + args, + gt_token: Gt::default(), + }; + + // Debug prints for better traceability in case of failure + println!( "Expected: {:?}", exp ); + println!( "Got: {:?}", got ); + + a_id!( exp, got, "Failed into_generic_args_single_const_parameter: exp {:?}, got {:?}", exp, got ); +} + + +// + +#[ test ] +fn into_generic_args_mixed_parameters() +{ + use syn:: + { + Generics, + AngleBracketedGenericArguments, + GenericArgument, + Type, + TypePath, + Expr, + ExprPath, + Ident, + Lifetime, + token::{ self, Comma }, + punctuated::Punctuated, + parse_quote + }; + use macro_tools::IntoGenericArgs; + + // Generate the actual value using the implementation + let generics : Generics = parse_quote! + { + + }; + let got = generics.into_generic_args(); + + // Manually construct the exp value + let mut args = Punctuated::new(); + let t_type : GenericArgument = GenericArgument::Type( Type::Path( TypePath + { + qself: None, + path: Ident::new( "T", proc_macro2::Span::call_site() ).into(), + })); + args.push_value( t_type ); + args.push_punct( Comma::default() ); + + let a_lifetime = GenericArgument::Lifetime( Lifetime::new( "'a", proc_macro2::Span::call_site() )); + args.push_value( a_lifetime ); + args.push_punct( Comma::default() ); + + let n_const : GenericArgument = GenericArgument::Const( Expr::Path( ExprPath + { + attrs: vec![], + qself: None, + path: Ident::new( "N", proc_macro2::Span::call_site() ).into(), + })); + args.push_value( n_const ); + + let exp = AngleBracketedGenericArguments + { + colon2_token: None, + lt_token: token::Lt::default(), + args, + gt_token: token::Gt::default(), + }; + + // tree_print!( got ); + // tree_print!( exp ); + // a_id!(tree_diagnostics_str!( exp ), tree_diagnostics_str!( got ) ); + a_id!( exp, got, "Failed into_generic_args_mixed_parameters: exp {:?}, got {:?}", exp, got ); +} + +// = generic_args::merge + +#[ test ] +fn merge_empty_arguments() +{ + use syn::AngleBracketedGenericArguments; + use macro_tools::generic_args; + + let a : AngleBracketedGenericArguments = parse_quote! { <> }; + let b : AngleBracketedGenericArguments = parse_quote! { <> }; + let exp : AngleBracketedGenericArguments = parse_quote! { <> }; + + let got = generic_args::merge( &a, &b ); + a_id!( got, exp, "Merging two empty arguments should got in empty arguments" ); +} + +// + +#[ test ] +fn merge_one_empty_one_non_empty() +{ + use syn::AngleBracketedGenericArguments; + use macro_tools::generic_args; + + let a : AngleBracketedGenericArguments = parse_quote! { < T, U > }; + let b : AngleBracketedGenericArguments = parse_quote! { <> }; + let exp : AngleBracketedGenericArguments = parse_quote! { < T, U > }; + + let got = generic_args::merge( &a, &b ); + a_id!( got, exp, "Merging non-empty with empty should got in the non-empty" ); +} + +// + +#[ test ] +fn merge_duplicate_arguments() +{ + use syn::AngleBracketedGenericArguments; + use macro_tools::generic_args; + + let a : AngleBracketedGenericArguments = parse_quote! { < T > }; + let b : AngleBracketedGenericArguments = parse_quote! { < T > }; + let exp : AngleBracketedGenericArguments = parse_quote! { < T, T > }; + + let got = generic_args::merge( &a, &b ); + a_id!( got, exp, "Duplicates should be preserved in the output" ); +} + +// + +#[ test ] +fn merge_large_number_of_arguments() +{ + use syn::AngleBracketedGenericArguments; + use macro_tools::generic_args; + + let a : AngleBracketedGenericArguments = parse_quote! { }; + let b : AngleBracketedGenericArguments = parse_quote! { }; + let exp : AngleBracketedGenericArguments = parse_quote! { }; + + let got = generic_args::merge( &a, &b ); + a_id!( got, exp, "Merging large number of arguments should succeed without altering order or count" ); +} + +// + +#[ test ] +fn merge_complex_generic_constraints() +{ + use syn::AngleBracketedGenericArguments; + use macro_tools::generic_args; + + let a : AngleBracketedGenericArguments = parse_quote! { < T : Clone + Send, U: Default > }; + let b : AngleBracketedGenericArguments = parse_quote! { < V : core::fmt::Debug + Sync > }; + let exp : AngleBracketedGenericArguments = parse_quote! { < T: Clone + Send, U: Default, V: core::fmt::Debug + Sync > }; + + let got = generic_args::merge( &a, &b ); + a_id!( got, exp, "Complex constraints should be merged correctly" ); +} + +// + +#[ test ] +fn merge_different_orders_of_arguments() +{ + use syn::AngleBracketedGenericArguments; + use macro_tools::generic_args; + + let a : AngleBracketedGenericArguments = parse_quote! { < T, U > }; + let b : AngleBracketedGenericArguments = parse_quote! { < V, W > }; + let exp : AngleBracketedGenericArguments = parse_quote! { < T, U, V, W > }; + + let got = generic_args::merge( &a, &b ); + a_id!( got, exp, "Order of arguments should be preserved as per the inputs" ); +} + +// + +#[ test ] +fn merge_interaction_with_lifetimes_and_constants() +{ + use syn::AngleBracketedGenericArguments; + use macro_tools::generic_args; + + let a : AngleBracketedGenericArguments = parse_quote! { < 'a, M : T > }; + let b : AngleBracketedGenericArguments = parse_quote! { < 'b, N > }; + let exp : AngleBracketedGenericArguments = parse_quote! { <'a, 'b, M : T, N > }; + + let got = generic_args::merge( &a, &b ); + // a_id!(tree_diagnostics_str!( exp ), tree_diagnostics_str!( got ) ); + a_id!( got, exp, "Lifetimes and constants should be interleaved correctly" ); + +} diff --git a/module/core/macro_tools/tests/inc/generic_params.rs b/module/core/macro_tools/tests/inc/generic_params.rs new file mode 100644 index 0000000000..d9395b74a2 --- /dev/null +++ b/module/core/macro_tools/tests/inc/generic_params.rs @@ -0,0 +1,354 @@ + +use super::*; + +// + +#[ test ] +fn generics_with_where() +{ + + let got : the_module::GenericsWithWhere = parse_quote! + { + < 'a, T : Clone, U : Default, V : core::fmt::Debug > + where + Definition : former::FormerDefinition, + }; + let got = got.unwrap(); + + let mut exp : syn::Generics = parse_quote! + { + < 'a, T : Clone, U : Default, V : core::fmt::Debug > + }; + exp.where_clause = parse_quote! + { + where + Definition : former::FormerDefinition, + }; + + // a_id!( tree_print!( got ), tree_print!( exp ) ); + // code_print!( got ); + // code_print!( exp ); + // code_print!( got.where_clause ); + // code_print!( exp.where_clause ); + + assert_eq!( got.params, exp.params ); + assert_eq!( got.where_clause, exp.where_clause ); + assert_eq!( got, exp ); + +} + +// + +#[ test ] +fn merge_assumptions() +{ + use the_module::generic_params; + + let mut generics_a : syn::Generics = parse_quote!{ < T : Clone, U : Default > }; + generics_a.where_clause = parse_quote!{ where T : Default }; + let mut generics_b : syn::Generics = parse_quote!{ < V : core::fmt::Debug > }; + generics_b.where_clause = parse_quote!{ where V : Sized }; + let got = generic_params::merge( &generics_a, &generics_b ); + + let mut exp : syn::Generics = parse_quote! + { + < T : Clone, U : Default, V : core::fmt::Debug > + }; + exp.where_clause = parse_quote! + { + where + T : Default, + V : Sized + }; + + // a_id!( tree_print!( got ), tree_print!( exp ) ); + // code_print!( got ); + // code_print!( exp ); + // code_print!( got.where_clause ); + // code_print!( exp.where_clause ); + + assert_eq!( got.params, exp.params ); + assert_eq!( got.where_clause, exp.where_clause ); + assert_eq!( got, exp ); + +} + +// + +#[ test ] +fn merge_defaults() +{ + use the_module::generic_params; + + let mut generics_a : syn::Generics = parse_quote!{ < T : Clone, U : Default = Default1 > }; + generics_a.where_clause = parse_quote!{ where T : Default }; + let mut generics_b : syn::Generics = parse_quote!{ < V : core::fmt::Debug = Debug1 > }; + generics_b.where_clause = parse_quote!{ where V : Sized }; + let got = generic_params::merge( &generics_a, &generics_b ); + + let mut exp : syn::Generics = parse_quote! + { + < T : Clone, U : Default = Default1, V : core::fmt::Debug = Debug1 > + }; + exp.where_clause = parse_quote! + { + where + T : Default, + V : Sized + }; + + // a_id!( tree_print!( got ), tree_print!( exp ) ); + // code_print!( got ); + // code_print!( exp ); + // code_print!( got.where_clause ); + // code_print!( exp.where_clause ); + + assert_eq!( got.params, exp.params ); + assert_eq!( got.where_clause, exp.where_clause ); + assert_eq!( got, exp ); + +} + +// + +#[ test ] +fn names() +{ + + use macro_tools::syn::parse_quote; + + let generics : the_module::GenericsWithWhere = parse_quote!{ < T : Clone + Default, U, 'a, const N : usize > where T: core::fmt::Debug }; + let simplified_generics = macro_tools::generic_params::names( &generics.unwrap() ); + + assert_eq!( simplified_generics.params.len(), 4 ); // Contains T, U, 'a, and N + assert!( simplified_generics.where_clause.is_none() ); // Where clause is removed + +} + +// + +#[ test ] +fn decompose_empty_generics() +{ + let generics : syn::Generics = syn::parse_quote! {}; + let ( _impl_with_def, impl_gen, ty_gen, where_gen ) = the_module::generic_params::decompose( &generics ); + + assert!( impl_gen.is_empty(), "Impl generics should be empty" ); + assert!( ty_gen.is_empty(), "Type generics should be empty" ); + assert!( where_gen.is_empty(), "Where generics should be empty" ); +} + +#[ test ] +fn decompose_generics_without_where_clause() +{ + let generics : syn::Generics = syn::parse_quote! { < T, U > }; + let ( _impl_with_def, impl_gen, ty_gen, where_gen ) = the_module::generic_params::decompose( &generics ); + + assert_eq!( impl_gen.len(), 2, "Impl generics should have two parameters" ); + assert_eq!( ty_gen.len(), 2, "Type generics should have two parameters" ); + assert!( where_gen.is_empty(), "Where generics should be empty" ); + + let exp : syn::Generics = syn::parse_quote! { < T, U, > }; + a_id!( impl_gen, exp.params ); + let exp : syn::Generics = syn::parse_quote! { < T, U, > }; + a_id!( ty_gen, exp.params ); + // xxx : extend other tests + +} + +#[ test ] +fn decompose_generics_with_where_clause() +{ + use macro_tools::quote::ToTokens; + + let generics : the_module::GenericsWithWhere = syn::parse_quote! { < T, U > where T : Clone, U : Default }; + let generics = generics.unwrap(); + let ( _impl_with_def, impl_gen, ty_gen, where_gen ) = the_module::generic_params::decompose( &generics ); + + let impl_exp : syn::Generics = syn::parse_quote! { < T, U, > }; + let ty_exp : syn::Generics = syn::parse_quote! { < T, U, > }; + a_id!( impl_gen, impl_exp.params ); + a_id!( ty_gen, ty_exp.params ); + + assert_eq!( impl_gen.len(), 2, "Impl generics should have two parameters" ); + assert_eq!( ty_gen.len(), 2, "Type generics should have two parameters" ); + assert_eq!( where_gen.len(), 2, "Where generics should have two predicates" ); + + let where_clauses : Vec< _ > = where_gen.iter().collect(); + + // Properly match against the `syn::WherePredicate::Type` variant to extract `bounded_ty` + if let syn::WherePredicate::Type( pt ) = &where_clauses[0] + { + assert_eq!( pt.bounded_ty.to_token_stream().to_string(), "T", "The first where clause should be for T" ); + } + else + { + panic!( "First where clause is not a Type predicate as expected." ); + } + + if let syn::WherePredicate::Type( pt ) = &where_clauses[1] + { + assert_eq!( pt.bounded_ty.to_token_stream().to_string(), "U", "The second where clause should be for U" ); + } + else + { + panic!( "Second where clause is not a Type predicate as expected." ); + } +} + +#[ test ] +fn decompose_generics_with_only_where_clause() +{ + let generics : the_module::GenericsWithWhere = syn::parse_quote! { where T : Clone, U : Default }; + let generics = generics.unwrap(); + let ( _impl_with_def, impl_gen, ty_gen, where_gen ) = the_module::generic_params::decompose( &generics ); + + assert!( impl_gen.is_empty(), "Impl generics should be empty" ); + assert!( ty_gen.is_empty(), "Type generics should be empty" ); + assert_eq!( where_gen.len(), 2, "Where generics should have two predicates" ); + +} + +#[ test ] +fn decompose_generics_with_complex_constraints() +{ + use macro_tools::quote::ToTokens; + let generics : the_module::GenericsWithWhere = syn::parse_quote! { < T : Clone + Send, U : Default > where T: Send, U: Default }; + let generics = generics.unwrap(); + let ( _impl_with_def, impl_gen, ty_gen, where_gen ) = the_module::generic_params::decompose( &generics ); + + let impl_exp : syn::Generics = syn::parse_quote! { < T : Clone + Send, U : Default, > }; + let ty_exp : syn::Generics = syn::parse_quote! { < T, U, > }; + a_id!( impl_gen, impl_exp.params ); + a_id!( ty_gen, ty_exp.params ); + + assert_eq!( impl_gen.len(), 2, "Impl generics should reflect complex constraints" ); + assert_eq!( ty_gen.len(), 2, "Type generics should reflect complex constraints" ); + assert_eq!( where_gen.len(), 2, "Where generics should reflect complex constraints" ); + + let where_clauses : Vec<_> = where_gen.iter().collect(); + + // Properly matching against the WherePredicate::Type variant + if let syn::WherePredicate::Type( pt ) = &where_clauses[0] + { + assert_eq!( pt.bounded_ty.to_token_stream().to_string(), "T", "The first where clause should be for T" ); + } + else + { + panic!( "First where clause is not a Type predicate as expected." ); + } + + if let syn::WherePredicate::Type( pt ) = &where_clauses[1] + { + assert_eq!( pt.bounded_ty.to_token_stream().to_string(), "U", "The second where clause should be for U" ); + } + else + { + panic!( "Second where clause is not a Type predicate as expected." ); + } +} + +#[ test ] +fn decompose_generics_with_nested_generic_types() +{ + let generics : syn::Generics = syn::parse_quote! { < T : Iterator< Item = U >, U > }; + let ( _impl_with_def, impl_gen, ty_gen, where_gen ) = the_module::generic_params::decompose( &generics ); + + let impl_exp : syn::Generics = syn::parse_quote! { < T : Iterator< Item = U >, U, > }; + let ty_exp : syn::Generics = syn::parse_quote! { < T, U, > }; + a_id!( impl_gen, impl_exp.params ); + a_id!( ty_gen, ty_exp.params ); + + assert_eq!( impl_gen.len(), 2, "Impl generics should handle nested generics" ); + assert_eq!( ty_gen.len(), 2, "Type generics should handle nested generics" ); + assert!( where_gen.is_empty(), "Where generics should be empty for non-conditional types" ); +} + +#[ test ] +fn decompose_generics_with_lifetime_parameters_only() +{ + let generics : syn::Generics = syn::parse_quote! { < 'a, 'b > }; + let ( _impl_with_def, impl_gen, ty_gen, where_gen ) = the_module::generic_params::decompose( &generics ); + + let impl_exp : syn::Generics = syn::parse_quote! { < 'a, 'b, > }; + let ty_exp : syn::Generics = syn::parse_quote! { < 'a, 'b, > }; + a_id!( impl_gen, impl_exp.params ); + a_id!( ty_gen, ty_exp.params ); + + assert_eq!( impl_gen.len(), 2, "Impl generics should contain only lifetimes" ); + assert_eq!( ty_gen.len(), 2, "Type generics should contain only lifetimes" ); + assert!( where_gen.is_empty(), "Where generics should be empty" ); +} + +#[ test ] +fn decompose_generics_with_constants_only() +{ + let generics : syn::Generics = syn::parse_quote! { < const N : usize, const M : usize > }; + let ( _impl_with_def, impl_gen, ty_gen, where_gen ) = the_module::generic_params::decompose( &generics ); + + let impl_exp : syn::Generics = syn::parse_quote! { < const N : usize, const M : usize, > }; + let ty_exp : syn::Generics = syn::parse_quote! { < N, M, > }; + a_id!( impl_gen, impl_exp.params ); + a_id!( ty_gen, ty_exp.params ); + + assert_eq!( impl_gen.len(), 2, "Impl generics should contain constants" ); + assert_eq!( ty_gen.len(), 2, "Type generics should contain constants" ); + assert!( where_gen.is_empty(), "Where generics should be empty" ); +} + +#[ test ] +fn decompose_generics_with_default_values() +{ + let generics : syn::Generics = syn::parse_quote! { < T = usize, U = i32 > }; + let ( impl_with_def, impl_gen, ty_gen, where_gen ) = the_module::generic_params::decompose( &generics ); + + let impl_with_exp : syn::Generics = syn::parse_quote! { < T = usize, U = i32, > }; + let impl_exp : syn::Generics = syn::parse_quote! { < T, U, > }; + let ty_exp : syn::Generics = syn::parse_quote! { < T, U, > }; + a_id!( impl_with_def, impl_with_exp.params ); + a_id!( impl_gen, impl_exp.params ); + a_id!( ty_gen, ty_exp.params ); + + assert_eq!( impl_gen.len(), 2, "Impl generics should retain default types" ); + assert_eq!( ty_gen.len(), 2, "Type generics should retain default types" ); + assert!( where_gen.is_empty(), "Where generics should be empty" ); +} + +#[ test ] +fn decompose_mixed_generics_types() +{ + use macro_tools::quote::ToTokens; + let generics : the_module::GenericsWithWhere = syn::parse_quote! { < 'a, T, const N : usize, U : Trait1 > where T : Clone, U : Default }; + let generics = generics.unwrap(); + let ( _impl_with_def, impl_gen, ty_gen, where_gen ) = the_module::generic_params::decompose( &generics ); + + let impl_exp : syn::Generics = syn::parse_quote! { < 'a, T, const N : usize, U : Trait1, > }; + let ty_exp : syn::Generics = syn::parse_quote! { < 'a, T, N, U, > }; + a_id!( impl_gen, impl_exp.params ); + a_id!( ty_gen, ty_exp.params ); + + assert_eq!( impl_gen.len(), 4, "Impl generics should correctly interleave types" ); + assert_eq!( ty_gen.len(), 4, "Type generics should correctly interleave types" ); + assert_eq!( where_gen.len(), 2, "Where generics should include conditions for T and U" ); + + // Correctly handling the pattern matching for WherePredicate::Type + let where_clauses : Vec<_> = where_gen.iter().collect(); + if let syn::WherePredicate::Type( pt ) = &where_clauses[0] + { + assert_eq!( pt.bounded_ty.to_token_stream().to_string(), "T", "The first where clause should be for T" ); + } + else + { + panic!( "First where clause is not a Type predicate as expected." ); + } + + if let syn::WherePredicate::Type( pt ) = &where_clauses[1] + { + assert_eq!( pt.bounded_ty.to_token_stream().to_string(), "U", "The second where clause should be for U" ); + } + else + { + panic!( "Second where clause is not a Type predicate as expected." ); + } + +} diff --git a/module/core/macro_tools/tests/inc/generics_test.rs b/module/core/macro_tools/tests/inc/generics_test.rs deleted file mode 100644 index 84c5090d0c..0000000000 --- a/module/core/macro_tools/tests/inc/generics_test.rs +++ /dev/null @@ -1,90 +0,0 @@ - -use super::*; - -// - -#[ test ] -fn basic() -{ - - let mut generics_a : syn::Generics = parse_quote!{ < T : Clone, U : Default > }; - generics_a.where_clause = parse_quote!{ where T : Default }; - let mut generics_b : syn::Generics = parse_quote!{ < V : std::fmt::Debug > }; - generics_b.where_clause = parse_quote!{ where V : Sized }; - let got = generics::merge( &generics_a, &generics_b ); - - let mut exp : syn::Generics = parse_quote! - { - < T : Clone, U : Default, V : std::fmt::Debug > - }; - exp.where_clause = parse_quote! - { - where - T : Default, - V : Sized - }; - - // a_id!( tree_print!( got ), tree_print!( exp ) ); - // code_print!( got ); - // code_print!( exp ); - // code_print!( got.where_clause ); - // code_print!( exp.where_clause ); - - assert_eq!( got.params, exp.params ); - assert_eq!( got.where_clause, exp.where_clause ); - assert_eq!( got, exp ); - -} - -// - -#[ test ] -fn merge_defaults() -{ - - let mut generics_a : syn::Generics = parse_quote!{ < T : Clone, U : Default = Default1 > }; - generics_a.where_clause = parse_quote!{ where T : Default }; - let mut generics_b : syn::Generics = parse_quote!{ < V : std::fmt::Debug = Debug1 > }; - generics_b.where_clause = parse_quote!{ where V : Sized }; - let got = generics::merge( &generics_a, &generics_b ); - - let mut exp : syn::Generics = parse_quote! - { - < T : Clone, U : Default = Default1, V : std::fmt::Debug = Debug1 > - }; - exp.where_clause = parse_quote! - { - where - T : Default, - V : Sized - }; - - // a_id!( tree_print!( got ), tree_print!( exp ) ); - // code_print!( got ); - // code_print!( exp ); - // code_print!( got.where_clause ); - // code_print!( exp.where_clause ); - - assert_eq!( got.params, exp.params ); - assert_eq!( got.where_clause, exp.where_clause ); - assert_eq!( got, exp ); - -} - -// - -#[ test ] -fn params_names() -{ - - use macro_tools::syn::parse_quote; - - let mut generics : syn::Generics = parse_quote!{ < T : Clone + Default, U, 'a, const N : usize > }; - generics.where_clause = parse_quote!{ where T: std::fmt::Debug }; - // let generics : Generics = parse_quote!{ < T : Clone + Default, U, 'a, const N : usize > where T: std::fmt::Debug }; - let simplified_generics = macro_tools::generics::params_names( &generics ); - - assert_eq!( simplified_generics.params.len(), 4 ); // Contains T, U, 'a, and N - assert!( simplified_generics.where_clause.is_none() ); // Where clause is removed - -} diff --git a/module/core/macro_tools/tests/inc/item.rs b/module/core/macro_tools/tests/inc/item.rs new file mode 100644 index 0000000000..a9652f81cd --- /dev/null +++ b/module/core/macro_tools/tests/inc/item.rs @@ -0,0 +1,118 @@ + +use super::*; + +#[ test ] +fn ensure_comma_named_struct_with_multiple_fields() +{ + use syn::{ parse_quote, ItemStruct }; + + let input_struct : ItemStruct = parse_quote! + { + struct Example + { + field1 : i32, + field2 : String + } + }; + + let got = the_module::item::ensure_comma( &input_struct ); + // let exp = "struct Example { field1 : i32, field2 : String, }"; + let exp : syn::ItemStruct = parse_quote! { struct Example { field1 : i32, field2 : String, } }; + // let got = quote!( #got ).to_string(); + // assert_eq!( exp, got ); + a_id!( got, exp ); + +} + +#[ test ] +fn ensure_comma_named_struct_with_single_field() +{ + use syn::{ parse_quote, ItemStruct }; + + let input_struct : ItemStruct = parse_quote! + { + struct Example + { + field1 : i32 + } + }; + + let got = the_module::item::ensure_comma( &input_struct ); + let exp : ItemStruct = parse_quote! { struct Example { field1 : i32, } }; + assert_eq!( got, exp ); +} + +#[ test ] +fn ensure_comma_named_struct_with_no_fields() +{ + use syn::{ parse_quote, ItemStruct }; + + let input_struct : ItemStruct = parse_quote! + { + struct Example { } + }; + + let got = the_module::item::ensure_comma( &input_struct ); + let exp : ItemStruct = parse_quote! { struct Example { } }; + assert_eq!( got, exp ); +} + +#[ test ] +fn ensure_comma_unnamed_struct_with_multiple_fields() +{ + use syn::{ parse_quote, ItemStruct }; + + let input_struct : ItemStruct = parse_quote! + { + struct Example( i32, String ); + }; + + let got = the_module::item::ensure_comma( &input_struct ); + let exp : ItemStruct = parse_quote! { struct Example( i32, String, ); }; + assert_eq!( got, exp ); +} + +#[ test ] +fn ensure_comma_unnamed_struct_with_single_field() +{ + use syn::{ parse_quote, ItemStruct }; + + let input_struct : ItemStruct = parse_quote! + { + struct Example( i32 ); + }; + + let got = the_module::item::ensure_comma( &input_struct ); + let exp : ItemStruct = parse_quote! { struct Example( i32, ); }; + assert_eq!( got, exp ); +} + +#[ test ] +fn ensure_comma_unnamed_struct_with_no_fields() +{ + use syn::{ parse_quote, ItemStruct }; + + let input_struct : ItemStruct = parse_quote! + { + struct Example( ); + }; + + let got = the_module::item::ensure_comma( &input_struct ); + let exp : ItemStruct = parse_quote! { struct Example( ); }; + assert_eq!( got, exp ); +} + +#[ test ] +fn ensure_comma_unit_struct_with_no_fields() +{ + use syn::{ parse_quote, ItemStruct }; + + let input_struct : ItemStruct = parse_quote! + { + struct Example; + }; + + let got = the_module::item::ensure_comma( &input_struct ); + let exp : ItemStruct = parse_quote! { struct Example; }; + assert_eq!( got, exp ); +} diff --git a/module/core/macro_tools/tests/inc/mod.rs b/module/core/macro_tools/tests/inc/mod.rs index c4063e67eb..9bf5c92947 100644 --- a/module/core/macro_tools/tests/inc/mod.rs +++ b/module/core/macro_tools/tests/inc/mod.rs @@ -6,17 +6,23 @@ use test_tools::exposed::*; #[ allow( unused_imports ) ] #[ cfg( feature = "enabled" ) ] -use the_module::exposed::*; +#[ path = "." ] +mod if_enabled +{ -#[ cfg( feature = "enabled" ) ] -mod attr_test; -#[ cfg( feature = "enabled" ) ] -mod basic_test; -#[ cfg( feature = "enabled" ) ] -mod generics_test; -#[ cfg( feature = "enabled" ) ] -mod quantifier_test; -#[ cfg( feature = "enabled" ) ] -mod syntax_test; -#[ cfg( feature = "enabled" ) ] -mod tokens_test; + use super::*; + use the_module::exposed::*; + + mod attr; + mod basic; + mod derive; + mod generic_args; + mod generic_params; + mod item; + mod phantom; + mod quantifier; + mod syntax; + mod tokens; + mod typ; + +} diff --git a/module/core/macro_tools/tests/inc/phantom.rs b/module/core/macro_tools/tests/inc/phantom.rs new file mode 100644 index 0000000000..990a63d2e7 --- /dev/null +++ b/module/core/macro_tools/tests/inc/phantom.rs @@ -0,0 +1,298 @@ + +use super::*; + +#[ test ] +fn phantom_add_basic() +{ + + let item : syn::ItemStruct = syn::parse_quote! + { + pub struct Struct1< 'a, Context, Formed > + { + f1 : int32, + } + }; + + let exp : syn::ItemStruct = syn::parse_quote! + { + pub struct Struct1< 'a, Context, Formed > + { + f1 : int32, + _phantom : core::marker::PhantomData< ( &'a(), *const Context, *const Formed ) >, + } + }; + + let got = the_module::phantom::add_to_item( &item ); + // a_id!( tree_print!( got ), tree_print!( exp ) ); + a_id!( got, exp ); + +} + +// + +#[ test ] +fn phantom_add_no_generics() +{ + use syn::parse_quote; + use quote::ToTokens; + + let input : syn::ItemStruct = parse_quote! { struct TestStruct {} }; + let got = the_module::phantom::add_to_item( &input ); + + let exp : syn::ItemStruct = parse_quote! + { + struct TestStruct + { + } + }; + + assert_eq!( got.to_token_stream().to_string(), exp.to_token_stream().to_string() ); +} + +// + +#[ test ] +fn phantom_add_type_generics() +{ + use syn::parse_quote; + use quote::ToTokens; + + let input : syn::ItemStruct = parse_quote! { struct TestStruct< T, U > {} }; + let got = the_module::phantom::add_to_item( &input ); + + let exp : syn::ItemStruct = parse_quote! + { + struct TestStruct< T, U > + { + _phantom : core::marker::PhantomData< ( *const T, *const U ) >, + } + }; + + assert_eq!( got.to_token_stream().to_string(), exp.to_token_stream().to_string() ); +} + +// + +#[ test ] +fn phantom_add_lifetime_generics() +{ + use syn::parse_quote; + use quote::ToTokens; + + let input : syn::ItemStruct = parse_quote! { struct TestStruct< 'a, 'b > {} }; + let got = the_module::phantom::add_to_item( &input ); + + let exp : syn::ItemStruct = parse_quote! + { + struct TestStruct< 'a, 'b > + { + _phantom : core::marker::PhantomData< ( &'a (), &'b () ) >, + } + }; + + assert_eq!( got.to_token_stream().to_string(), exp.to_token_stream().to_string() ); +} + +// + +#[ test ] +fn phantom_add_const_generics() +{ + use syn::parse_quote; + use quote::ToTokens; + + let input : syn::ItemStruct = parse_quote! { struct TestStruct< const N : usize > {} }; + let got = the_module::phantom::add_to_item( &input ); + + let exp : syn::ItemStruct = parse_quote! + { + struct TestStruct< const N : usize > + { + _phantom : core::marker::PhantomData< ( N, ) >, + } + }; + + assert_eq!( got.to_token_stream().to_string(), exp.to_token_stream().to_string() ); +} + +// + +#[ test ] +fn phantom_add_mixed_generics() +{ + use syn::parse_quote; + use quote::ToTokens; + + let input : syn::ItemStruct = parse_quote! { struct TestStruct< T, 'a, const N : usize > {} }; + let got = the_module::phantom::add_to_item( &input ); + + let exp : syn::ItemStruct = parse_quote! + { + struct TestStruct< T, 'a, const N : usize > + { + _phantom : core::marker::PhantomData< ( *const T, &'a (), N ) >, + } + }; + + assert_eq!( got.to_token_stream().to_string(), exp.to_token_stream().to_string() ); +} + +// + +#[ test ] +fn phantom_add_named_fields() +{ + use syn::parse_quote; + use quote::ToTokens; + + let input : syn::ItemStruct = parse_quote! { struct TestStruct { field1 : i32, field2 : f64 } }; + let got = the_module::phantom::add_to_item( &input ); + + let exp : syn::ItemStruct = parse_quote! + { + struct TestStruct + { + field1 : i32, + field2 : f64, + } + }; + + assert_eq!( got.to_token_stream().to_string(), exp.to_token_stream().to_string() ); +} + +// + +#[ test ] +fn phantom_add_unnamed_fields() +{ + use syn::parse_quote; + use quote::ToTokens; + + let input : syn::ItemStruct = parse_quote! { struct TestStruct( i32, f64 ); }; + let got = the_module::phantom::add_to_item( &input ); + let exp : syn::ItemStruct = parse_quote! { struct TestStruct( i32, f64, ); }; + + assert_eq!( got.to_token_stream().to_string(), exp.to_token_stream().to_string() ); +} + +// + +#[ test ] +fn phantom_add_unnamed_fields_with_generics() +{ + use syn::parse_quote; + use quote::ToTokens; + + let input : syn::ItemStruct = parse_quote! { struct TestStruct< T, U >( T, U ); }; + let got = the_module::phantom::add_to_item( &input ); + + let exp : syn::ItemStruct = parse_quote! + { + struct TestStruct< T, U > + ( + T, U, + core::marker::PhantomData< ( *const T, *const U ) >, + ); + }; + + assert_eq!( got.to_token_stream().to_string(), exp.to_token_stream().to_string() ); +} + +// + +#[ test ] +fn phantom_add_unnamed_fields_lifetime_generics() +{ + use syn::parse_quote; + use quote::ToTokens; + + let input : syn::ItemStruct = parse_quote! { struct TestStruct< 'a, 'b >( &'a i32, &'b f64 ); }; + let got = the_module::phantom::add_to_item( &input ); + + let exp : syn::ItemStruct = parse_quote! + { + struct TestStruct< 'a, 'b > + ( + &'a i32, + &'b f64, + core::marker::PhantomData< ( &'a (), &'b () ) >, + ); + }; + + assert_eq!( got.to_token_stream().to_string(), exp.to_token_stream().to_string() ); +} + +// + +#[ test ] +fn phantom_add_unnamed_fields_const_generics() +{ + use syn::parse_quote; + use quote::ToTokens; + + let input : syn::ItemStruct = parse_quote! { struct TestStruct< const N : usize >( [ i32 ; N ] ); }; + let got = the_module::phantom::add_to_item( &input ); + + let exp : syn::ItemStruct = parse_quote! + { + struct TestStruct< const N : usize > + ( + [ i32 ; N ], + core::marker::PhantomData< ( N, ) >, + ); + }; + + assert_eq!( got.to_token_stream().to_string(), exp.to_token_stream().to_string() ); +} + +// + +// +#[ test ] +fn phantom_tuple_empty_generics() +{ + use syn::{ punctuated::Punctuated, GenericParam, token::Comma, parse_quote }; + use macro_tools::phantom::tuple; + + let input : Punctuated< GenericParam, Comma > = Punctuated::new(); + let result = tuple( &input ); + + let exp : syn::Type = parse_quote! { core::marker::PhantomData<()> }; + let got = result; + + assert_eq!( format!( "{:?}", exp ), format!( "{:?}", got ), "Expected empty PhantomData, got: {:?}", got ); +} + +// + +#[ test ] +fn phantom_tuple_only_type_parameters() +{ + use syn::{ parse_quote, punctuated::Punctuated, GenericParam, token::Comma }; + use macro_tools::phantom::tuple; + + let input : Punctuated< GenericParam, Comma > = parse_quote! { T, U }; + let result = tuple( &input ); + + let exp : syn::Type = parse_quote! { core::marker::PhantomData< ( *const T, *const U ) > }; + let got = result; + + assert_eq!( format!( "{:?}", exp ), format!( "{:?}", got ), "Expected PhantomData with type parameters, got: {:?}", got ); +} + +// + +#[ test ] +fn phantom_tuple_mixed_generics() +{ + use syn::{ parse_quote, punctuated::Punctuated, GenericParam, token::Comma }; + use macro_tools::phantom::tuple; + + let input : Punctuated< GenericParam, Comma > = parse_quote! { T, 'a, const N: usize }; + let result = tuple( &input ); + + let exp : syn::Type = parse_quote! { core::marker::PhantomData< ( *const T, &'a (), N ) > }; + let got = result; + + assert_eq!( format!( "{:?}", exp ), format!( "{:?}", got ), "Expected PhantomData with mixed generics, got: {:?}", got ); +} diff --git a/module/core/macro_tools/tests/inc/quantifier_test.rs b/module/core/macro_tools/tests/inc/quantifier.rs similarity index 100% rename from module/core/macro_tools/tests/inc/quantifier_test.rs rename to module/core/macro_tools/tests/inc/quantifier.rs diff --git a/module/core/macro_tools/tests/inc/syntax_test.rs b/module/core/macro_tools/tests/inc/syntax.rs similarity index 100% rename from module/core/macro_tools/tests/inc/syntax_test.rs rename to module/core/macro_tools/tests/inc/syntax.rs diff --git a/module/core/macro_tools/tests/inc/tokens_test.rs b/module/core/macro_tools/tests/inc/tokens.rs similarity index 100% rename from module/core/macro_tools/tests/inc/tokens_test.rs rename to module/core/macro_tools/tests/inc/tokens.rs diff --git a/module/core/macro_tools/tests/inc/typ.rs b/module/core/macro_tools/tests/inc/typ.rs new file mode 100644 index 0000000000..75bd10096d --- /dev/null +++ b/module/core/macro_tools/tests/inc/typ.rs @@ -0,0 +1,128 @@ + +use super::*; + +// + +#[ test ] +fn is_optional_with_option_type() +{ + use syn::parse_str; + use macro_tools::typ::is_optional; + + let type_string = "Option"; + let parsed_type : syn::Type = parse_str( type_string ).expect( "Type should parse correctly" ); + + assert!( is_optional( &parsed_type ), "Expected type to be recognized as an Option" ); +} + +#[ test ] +fn is_optional_with_non_option_type() +{ + use syn::parse_str; + use macro_tools::typ::is_optional; + + let type_string = "Vec"; + let parsed_type : syn::Type = parse_str( type_string ).expect( "Type should parse correctly" ); + + assert!( !is_optional( &parsed_type ), "Expected type not to be recognized as an Option" ); +} + +#[ test ] +fn is_optional_with_nested_option_type() +{ + use syn::parse_str; + use macro_tools::typ::is_optional; + + let type_string = "Option>"; + let parsed_type : syn::Type = parse_str( type_string ).expect( "Type should parse correctly" ); + + assert!( is_optional( &parsed_type ), "Expected nested Option type to be recognized as an Option" ); +} + +#[ test ] +fn is_optional_with_similar_name_type() +{ + use syn::parse_str; + use macro_tools::typ::is_optional; + + let type_string = "OptionalValue"; + let parsed_type : syn::Type = parse_str( type_string ).expect( "Type should parse correctly" ); + + assert!( !is_optional( &parsed_type ), "Expected type with similar name not to be recognized as an Option" ); +} + +#[ test ] +fn is_optional_with_empty_input() +{ + use syn::{ parse_str, Type }; + use macro_tools::typ::is_optional; + + let type_string = ""; + let parsed_type_result = parse_str::< Type >( type_string ); + + assert!( parsed_type_result.is_err(), "Expected parsing to fail for empty input" ); +} + +// xxx + +#[ test ] +fn parameter_first_with_multiple_generics() +{ + use syn::{ parse_str, Type }; + use macro_tools::typ::parameter_first; + + let type_string = "Result, Error>"; + let parsed_type : Type = parse_str( type_string ).expect( "Type should parse correctly" ); + + let first_param = parameter_first( &parsed_type ).expect( "Expected to extract the first generic parameter" ); + + let expected_type : Type = parse_str( "Option" ).expect( "Expected type to parse correctly" ); + assert_eq!( format!( "{:?}", expected_type ), format!( "{:?}", first_param ), "Extracted type does not match expected" ); +} + +#[ test ] +fn parameter_first_with_no_generics() +{ + use syn::{ parse_str, Type }; + use macro_tools::typ::parameter_first; + + let type_string = "i32"; + let parsed_type : Type = parse_str( type_string ).expect( "Type should parse correctly" ); + let got = parameter_first( &parsed_type ).expect( "Type should parse correctly" ); + + // tree_print!( got.as_ref().unwrap() ); + + let expected_type : Type = parse_str( "i32" ).expect( "Expected type to parse correctly" ); + assert_eq!( format!( "{:?}", expected_type ), format!( "{:?}", got ), "Extracted type does not match expected" ); + +} + +#[ test ] +fn parameter_first_with_single_generic() +{ + use syn::{ parse_str, Type }; + use macro_tools::typ::parameter_first; + + let type_string = "Vec< i32 >"; + let parsed_type : Type = parse_str( type_string ).expect( "Type should parse correctly" ); + + let first_param = parameter_first( &parsed_type ).expect( "Expected to extract the first generic parameter" ); + + let expected_type : Type = parse_str( "i32" ).expect( "Expected type to parse correctly" ); + assert_eq!( format!( "{:?}", expected_type ), format!( "{:?}", first_param ), "Extracted type does not match expected" ); +} + +#[ test ] +fn parameter_first_with_deeply_nested_generics() +{ + use syn::{ parse_str, Type }; + use macro_tools::typ::parameter_first; + + let type_string = "Vec< HashMap< String, Option< i32 > > >"; + let parsed_type : Type = parse_str( type_string ).expect( "Type should parse correctly" ); + + let first_param = parameter_first( &parsed_type ).expect( "Expected to extract the first generic parameter" ); + + 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" ); +} diff --git a/module/core/meta_tools/src/lib.rs b/module/core/meta_tools/src/lib.rs index 4511f3c3ca..cd49c9841e 100644 --- a/module/core/meta_tools/src/lib.rs +++ b/module/core/meta_tools/src/lib.rs @@ -2,11 +2,6 @@ #![ 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/meta_tools/latest/meta_tools/" ) ] - -//! -//! Collection of general purpose meta tools. -//! - #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] /// Namespace with dependencies. diff --git a/module/core/mod_interface_meta/src/impls.rs b/module/core/mod_interface_meta/src/impls.rs index 1538fb42e4..0f4608e420 100644 --- a/module/core/mod_interface_meta/src/impls.rs +++ b/module/core/mod_interface_meta/src/impls.rs @@ -460,9 +460,15 @@ pub( crate ) mod private if has_debug { - diag::debug_report_print( "derive : mod_interface", original_input, &result ); + let about = format!( "derive : mod_interface" ); + diag::report_print( about, &original_input, &result ); } + // if has_debug + // { + // diag::report_print( "derive : mod_interface", original_input, &result ); + // } + Ok( result ) } diff --git a/module/core/mod_interface_meta/src/lib.rs b/module/core/mod_interface_meta/src/lib.rs index c8e6d54d54..622fcd83d6 100644 --- a/module/core/mod_interface_meta/src/lib.rs +++ b/module/core/mod_interface_meta/src/lib.rs @@ -27,6 +27,17 @@ // xxx : make use proper_path_tools::protected::path working +// xxx : put modular files into a namespace `file` maybe +// #[ cfg( feature = "enabled" ) ] +// #[ path = "." ] +// mod file +// { +// use super::*; +// pub mod tokens; +// pub mod typ; +// pub mod type_struct; +// } + mod impls; #[ allow( unused_imports ) ] use impls::exposed::*; diff --git a/module/core/process_tools/src/process.rs b/module/core/process_tools/src/process.rs index 3060f0d105..eac9643740 100644 --- a/module/core/process_tools/src/process.rs +++ b/module/core/process_tools/src/process.rs @@ -211,7 +211,7 @@ pub( crate ) mod private bin_path : PathBuf, current_path : PathBuf, args : Vec< OsString >, - #[ default( false ) ] + #[ former( default = false ) ] joining_streams : bool, env_variable : HashMap< String, String >, } @@ -298,9 +298,9 @@ pub( crate ) mod private } } } - impl std::fmt::Display for Report + impl core::fmt::Display for Report { - fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + fn fmt( &self, f : &mut Formatter< '_ > ) -> core::fmt::Result { // Trim prevents writing unnecessary whitespace or empty lines f.write_fmt( format_args!( "> {}\n", self.command ) )?; diff --git a/module/core/reflect_tools/src/reflect/axiomatic.rs b/module/core/reflect_tools/src/reflect/axiomatic.rs index 4dd0cfb454..df63730d59 100644 --- a/module/core/reflect_tools/src/reflect/axiomatic.rs +++ b/module/core/reflect_tools/src/reflect/axiomatic.rs @@ -349,36 +349,36 @@ pub( crate ) mod private } } - impl< T > std::fmt::Debug for EntityDescriptor< T > + impl< T > core::fmt::Debug for EntityDescriptor< T > where T : Instance + 'static, EntityDescriptor< T > : Entity, { - fn fmt( &self, f: &mut std::fmt::Formatter< '_ > ) -> std::fmt::Result + fn fmt( &self, f: &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result { f .write_str( &format!( "{}#{:?}", self.type_name(), self.type_id() ) ) } } - impl< T > std::fmt::Debug for CollectionDescriptor< T > + impl< T > core::fmt::Debug for CollectionDescriptor< T > where T : Instance + 'static, CollectionDescriptor< T > : Entity, { - fn fmt( &self, f: &mut std::fmt::Formatter< '_ > ) -> std::fmt::Result + fn fmt( &self, f: &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result { f .write_str( &format!( "{}#{:?}", self.type_name(), self.type_id() ) ) } } - impl< T > std::fmt::Debug for KeyedCollectionDescriptor< T > + impl< T > core::fmt::Debug for KeyedCollectionDescriptor< T > where T : Instance + 'static, KeyedCollectionDescriptor< T > : Entity, { - fn fmt( &self, f: &mut std::fmt::Formatter< '_ > ) -> std::fmt::Result + fn fmt( &self, f: &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result { f .write_str( &format!( "{}#{:?}", self.type_name(), self.type_id() ) ) @@ -415,9 +415,9 @@ pub( crate ) mod private } } - impl std::fmt::Debug for KeyVal + impl core::fmt::Debug for KeyVal { - fn fmt( &self, f: &mut std::fmt::Formatter< '_ > ) -> std::fmt::Result + fn fmt( &self, f: &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result { f .debug_struct( "KeyVal" ) diff --git a/module/core/strs_tools/src/string/isolate.rs b/module/core/strs_tools/src/string/isolate.rs index abe3ddc13b..78d23f6658 100644 --- a/module/core/strs_tools/src/string/isolate.rs +++ b/module/core/strs_tools/src/string/isolate.rs @@ -11,17 +11,17 @@ pub( crate ) mod private #[ perform( fn isolate( &self ) -> ( &'a str, Option<&'a str>, &'a str ) ) ] pub struct IsolateOptions<'a> { - #[ default( "" ) ] + #[ former( default = "" ) ] src : &'a str, - #[ default( " " ) ] + #[ former( default = " " ) ] delimeter : &'a str, - #[ default( true ) ] + #[ former( default = true ) ] quote : bool, - #[ default( true ) ] + #[ former( default = true ) ] left : bool, - #[ default( 1 ) ] + #[ former( default = 1 ) ] times : u8, /* rrr : Dmytro : former do not form u16, u32, u64, usize, replace after fix */ - #[ default( true ) ] + #[ former( default = true ) ] none : bool, } diff --git a/module/core/strs_tools/src/string/parse_request.rs b/module/core/strs_tools/src/string/parse_request.rs index f972a50852..e3c68de8f9 100644 --- a/module/core/strs_tools/src/string/parse_request.rs +++ b/module/core/strs_tools/src/string/parse_request.rs @@ -160,21 +160,21 @@ pub( crate ) mod private #[ perform( fn parse( mut self ) -> Request< 'a > ) ] pub struct ParseOptions< 'a > { - #[ default( "" ) ] + #[ former( default = "" ) ] src : &'a str, - #[ default( ":" ) ] + #[ former( default = ":" ) ] key_val_delimeter : &'a str, - #[ default( ";" ) ] + #[ former( default = ";" ) ] commands_delimeter : &'a str, - #[ default( true ) ] + #[ former( default = true ) ] quoting : bool, - #[ default( true ) ] + #[ former( default = true ) ] unquoting : bool, - #[ default( true ) ] + #[ former( default = true ) ] parsing_arrays : bool, - #[ default( false ) ] + #[ former( default = false ) ] several_values : bool, - #[ default( false ) ] + #[ former( default = false ) ] subject_win_paths_maybe : bool, } diff --git a/module/core/test_tools/Cargo.toml b/module/core/test_tools/Cargo.toml index 26b95f2926..d3c8134dee 100644 --- a/module/core/test_tools/Cargo.toml +++ b/module/core/test_tools/Cargo.toml @@ -38,8 +38,8 @@ no_std = [ # "typing_tools/no_std", # "data_type/no_std", # "diagnostics_tools/no_std", - # "process_tools/no_std", - "former/use_alloc", + # "process_tools_published/no_std", + # "former_stable/use_alloc", ] use_alloc = [ "no_std", @@ -49,8 +49,8 @@ use_alloc = [ # "typing_tools/use_alloc", # "data_type/use_alloc", # "diagnostics_tools/use_alloc", - # "process_tools/use_alloc", - "former/use_alloc", + # "process_tools_published/use_alloc", + # "former_stable/use_alloc", ] enabled = [ "error_tools/enabled", @@ -59,7 +59,7 @@ enabled = [ "typing_tools/enabled", "data_type/enabled", "diagnostics_tools/enabled", - "process_tools/enabled", + "process_tools_published/enabled", ] # nightly = [ "typing_tools/nightly" ] @@ -82,8 +82,8 @@ mem_tools = { workspace = true, features = [ "full" ] } typing_tools = { workspace = true, features = [ "full" ] } data_type = { workspace = true, features = [ "full" ] } diagnostics_tools = { workspace = true, features = [ "full" ] } -process_tools = { workspace = true, features = [ "full" ] } -former = { workspace = true, features = [ "full" ] } +process_tools_published = { workspace = true, features = [ "full" ] } +# former_stable = { workspace = true, features = [ "full" ] } [build-dependencies] rustc_version = "0.4" diff --git a/module/core/test_tools/src/lib.rs b/module/core/test_tools/src/lib.rs index ed762f40fe..babcb96c49 100644 --- a/module/core/test_tools/src/lib.rs +++ b/module/core/test_tools/src/lib.rs @@ -39,9 +39,13 @@ pub mod dependency #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use ::diagnostics_tools; - // #[ doc( inline ) ] - // #[ allow( unused_imports ) ] - // pub use ::process_tools; + + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use ::process_tools_published; + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + pub use ::process_tools_published as process_tools; } diff --git a/module/core/test_tools/src/test/smoke_test.rs b/module/core/test_tools/src/test/smoke_test.rs index 8c671f72fc..29016a79bf 100644 --- a/module/core/test_tools/src/test/smoke_test.rs +++ b/module/core/test_tools/src/test/smoke_test.rs @@ -11,7 +11,8 @@ /// Internal namespace. pub( crate ) mod private { - use process_tools::environment; + use crate::*; + use dependency::process_tools::environment; // zzz : comment out // pub mod environment // { diff --git a/module/move/wca/src/ca/aggregator.rs b/module/move/wca/src/ca/aggregator.rs index 9c287326a7..1174de448f 100644 --- a/module/move/wca/src/ca/aggregator.rs +++ b/module/move/wca/src/ca/aggregator.rs @@ -5,13 +5,19 @@ pub( crate ) mod private { Verifier, Executor, - Command, - grammar::command::private::CommandFormer, + grammar::command::private:: + { + CommandFormer, + CommandAsSubformer, + CommandAsSubformerEnd, + CommandFormerStorage + }, help::{ HelpGeneratorFn, HelpGeneratorOptions, HelpVariants }, }; use std::collections::HashSet; use std::fmt; + use former::StoragePreform; use wtools::thiserror; use wtools::error:: { @@ -94,55 +100,70 @@ pub( crate ) mod private /// ``` #[ derive( Debug ) ] #[ derive( former::Former ) ] - #[ perform( fn build() -> CommandsAggregator ) ] + #[ storage_fields( help_generator : HelpGeneratorFn, help_variants : HashSet< HelpVariants > ) ] + #[ mutator( custom = true ) ] + // #[ debug ] pub struct CommandsAggregator { - #[ default( Dictionary::default() ) ] + #[ former( default = Dictionary::default() ) ] dictionary : Dictionary, - #[ default( Parser ) ] + #[ former( default = Parser ) ] parser : Parser, - #[ setter( false ) ] - #[ default( Executor::former().form() ) ] + #[ scalar( setter = false, hint = false ) ] + #[ former( default = Executor::former().form() ) ] executor : Executor, - help_generator : Option< HelpGeneratorFn >, - #[ default( HashSet::from([ HelpVariants::All ]) ) ] - help_variants : HashSet< HelpVariants >, - // aaa : for Bohdan : should not have fields help_generator and help_variants - // help_generator generateds VerifiedCommand(s) and stop to exist - // aaa : Defaults after formation - - // #[ default( Verifier::former().form() ) ] - #[ default( Verifier ) ] + #[ former( default = Verifier ) ] verifier : Verifier, - // #[ default( ExecutorConverter::former().form() ) ] - // executor_converter : ExecutorConverter, - callback_fn : Option< CommandsAggregatorCallback >, } - impl< Context, End > CommandsAggregatorFormer< Context, End > + impl< Context, Formed > former::FormerMutator for CommandsAggregatorFormerDefinitionTypes< Context, Formed > + { + fn form_mutation( storage : &mut Self::Storage, _context : &mut Option< Self::Context > ) + { + let ca = storage; + let dictionary = ca.dictionary.get_or_insert_with( Dictionary::default ); + + let help_generator = std::mem::take( &mut ca.help_generator ).unwrap_or_default(); + let help_variants = std::mem::take( &mut ca.help_variants ).unwrap_or_else( || HashSet::from([ HelpVariants::All ]) ); + + if help_variants.contains( &HelpVariants::All ) + { + HelpVariants::All.generate( &help_generator, dictionary ); + } + else + { + for help in help_variants.iter().sorted() + { + help.generate( &help_generator, dictionary ); + } + } + } + } + + impl< Definition > CommandsAggregatorFormer< Definition > where - End : former::FormingEnd< CommandsAggregator, Context >, + Definition : former::FormerDefinition< Storage = < CommandsAggregator as former::EntityToStorage >::Storage >, { /// Creates a command in the command chain. /// /// # Arguments /// /// * `name` - The name of the command. - pub fn command< IntoName >( self, name : IntoName ) -> CommandFormer< Self, impl former::FormingEnd< Command, Self > > + pub fn command< IntoName >( self, name : IntoName ) -> CommandAsSubformer< Self, impl CommandAsSubformerEnd< Self > > where IntoName : Into< String >, { - let on_end = | command : Command, super_former : Option< Self > | -> Self + let on_end = | command : CommandFormerStorage, super_former : Option< Self > | -> Self { let mut super_former = super_former.unwrap(); let mut dictionary = super_former.storage.dictionary.unwrap_or_default(); - dictionary.register( command ); + dictionary.register( command.preform() ); super_former.storage.dictionary = Some( dictionary ); @@ -199,7 +220,8 @@ pub( crate ) mod private self.storage.help_generator = Some( HelpGeneratorFn::new( func ) ); self } - // qqq : it is good access method, but formed structure should not have help_generator anymore + // aaa : it is good access method, but formed structure should not have help_generator anymore + // aaa : mutator used /// Set callback function that will be executed after validation state /// @@ -227,29 +249,6 @@ pub( crate ) mod private impl CommandsAggregator { - /// Construct CommandsAggregator - fn build( self ) -> CommandsAggregator - { - let mut ca = self; - - let help_generator = std::mem::take( &mut ca.help_generator ).unwrap_or_default(); - let help_variants = std::mem::take( &mut ca.help_variants ); - - if help_variants.contains( &HelpVariants::All ) - { - HelpVariants::All.generate( &help_generator, &mut ca.dictionary ); - } - else - { - for help in help_variants.iter().sorted() - { - help.generate( &help_generator, &mut ca.dictionary ); - } - } - - ca - } - /// Parse, converts and executes a program /// /// Takes a string with program and executes it diff --git a/module/move/wca/src/ca/executor/executor.rs b/module/move/wca/src/ca/executor/executor.rs index a48cc95bb7..1ba7ce66a4 100644 --- a/module/move/wca/src/ca/executor/executor.rs +++ b/module/move/wca/src/ca/executor/executor.rs @@ -16,7 +16,7 @@ pub( crate ) mod private pub struct Executor { /// The default context for the executor - #[ default( Context::default() ) ] + #[ former( default = Context::default() ) ] pub context : Context, } diff --git a/module/move/wca/src/ca/grammar/command.rs b/module/move/wca/src/ca/grammar/command.rs index 7d61e0ac38..2f12e03921 100644 --- a/module/move/wca/src/ca/grammar/command.rs +++ b/module/move/wca/src/ca/grammar/command.rs @@ -5,7 +5,7 @@ pub( crate ) mod private use { Handler, Routine, Type }; use std::collections::HashMap; - use former::Former; + use former::{ Former, StoragePreform }; /// A description of a Value in a command. Used to specify the expected type and provide a hint for the Value. /// @@ -27,7 +27,7 @@ pub( crate ) mod private /// expected type of a value pub kind : Type, /// subject optional parameter - #[ default( false ) ] + #[ former( default = false ) ] pub optional : bool, } @@ -42,16 +42,16 @@ pub( crate ) mod private /// expected type of a value kind : Type, /// subject optional parameter - #[ default( false ) ] + #[ former( default = false ) ] optional : bool, - #[ setter( false ) ] - #[ default( Vec::new() ) ] + #[ scalar( setter = false ) ] + #[ former( default = Vec::new() ) ] properties_aliases : Vec< String >, } - impl< C, End > PropertyDescriptionFormer< C, End > + impl< Definition > PropertyDescriptionFormer< Definition > where - End : former::FormingEnd< PropertyDescription, C >, + Definition : former::FormerDefinition< Storage = < PropertyDescription as former::EntityToStorage >::Storage >, { pub fn alias< IntoName >( mut self, name : IntoName ) -> Self where @@ -86,17 +86,17 @@ pub( crate ) mod private #[ derive( Debug, Clone, PartialEq, Eq ) ] #[ derive( Former ) ] + // #[ debug ] pub struct Command { /// Command common hint. - #[ alias( h ) ] pub hint : String, /// Command full hint. - #[ alias( lh ) ] pub long_hint : String, /// Phrase descriptor for command. pub phrase : String, /// Command subjects hints and types. + #[ subform( setter = true ) ] pub subjects : Vec< ValueDescription >, /// Hints and types for command options. pub properties : HashMap< String, ValueDescription >, @@ -107,14 +107,14 @@ pub( crate ) mod private // aaa : here it is // qqq : make it usable and remove default(?) /// The type `Routine` represents the specific implementation of the routine. - #[ setter( false ) ] - #[ default( Routine::from( Handler::from( || { panic!( "No routine available: A handler function for the command is missing" ) } ) ) ) ] + #[ scalar( setter = false ) ] + #[ former( default = Routine::from( Handler::from( || { panic!( "No routine available: A handler function for the command is missing" ) } ) ) ) ] pub routine : Routine, } - impl< Context, End > CommandFormer< Context, End > + impl< Definition > CommandFormer< Definition > where - End : former::FormingEnd< Command, Context >, + Definition : former::FormerDefinition< Storage = < Command as former::EntityToStorage >::Storage >, { /// Setter for separate properties aliases. pub fn property_alias< S : Into< String > >( mut self, key : S, alias : S ) -> Self @@ -169,27 +169,17 @@ pub( crate ) mod private } } - impl< Context, End > CommandFormer< Context, End > + impl< Definition > CommandFormer< Definition > where - End : former::FormingEnd< Command, Context >, + Definition : former::FormerDefinition< Storage = < Command as former::EntityToStorage >::Storage >, { /// Implements the `subject` method for a value. /// /// This method allows chaining, where `subject` is the current value and `ValueDescription` is the super-former. /// It returns a `ValueDescriptionFormer` which can be used to further build the super-former. - pub fn subject( self ) -> ValueDescriptionFormer< Self, impl former::FormingEnd< ValueDescription, Self > > + pub fn subject( self ) -> ValueDescriptionAsSubformer< Self, impl ValueDescriptionAsSubformerEnd< Self > > { - let on_end = | subject : ValueDescription, super_former : Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - let mut subjects = super_former.storage.subjects.unwrap_or_default(); - subjects.push( subject ); - - super_former.storage.subjects = Some( subjects ); - - super_former - }; - ValueDescriptionFormer::begin( None, Some( self ), on_end ) + self._subjects_add() } /// Sets the name and other properties of the current property. @@ -201,14 +191,15 @@ pub( crate ) mod private /// # Arguments /// /// * `name` - The name of the property. It should implement the `Into< String >` trait. - pub fn property< IntoName >( self, name : IntoName ) -> PropertyDescriptionFormer< Self, impl former::FormingEnd< PropertyDescription, Self > > + pub fn property< IntoName >( self, name : IntoName ) -> PropertyDescriptionAsSubformer< Self, impl PropertyDescriptionAsSubformerEnd< Self > > where IntoName : Into< String >, { - let on_end = | property : PropertyDescription, super_former : Option< Self > | -> Self + let on_end = | property : PropertyDescriptionFormerStorage, super_former : Option< Self > | -> Self { let mut super_former = super_former.unwrap(); let mut properties = super_former.storage.properties.unwrap_or_default(); + let property = property.preform(); let value = ValueDescription { hint : property.hint, diff --git a/module/move/wca/src/ca/grammar/dictionary.rs b/module/move/wca/src/ca/grammar/dictionary.rs index 21fc962efb..a9a79d198a 100644 --- a/module/move/wca/src/ca/grammar/dictionary.rs +++ b/module/move/wca/src/ca/grammar/dictionary.rs @@ -20,7 +20,7 @@ pub( crate ) mod private #[ derive( Debug, Default, Former, Clone ) ] pub struct Dictionary { - #[ setter( false ) ] + #[ scalar( setter = false, hint = false ) ] pub( crate ) commands : HashMap< String, Command >, } diff --git a/module/move/wca/src/ca/help.rs b/module/move/wca/src/ca/help.rs index 54d1485a12..028a79347e 100644 --- a/module/move/wca/src/ca/help.rs +++ b/module/move/wca/src/ca/help.rs @@ -29,7 +29,7 @@ pub( crate ) mod private pub struct HelpGeneratorOptions< 'a > { /// Prefix that will be shown before command name - #[ default( String::new() ) ] + #[ former( default = String::new() ) ] pub command_prefix : String, /// Show help for the specified commands pub for_commands : Vec< &'a Command >, diff --git a/module/move/willbe/src/action/test.rs b/module/move/willbe/src/action/test.rs index 3f9bd57009..72483c71d6 100644 --- a/module/move/willbe/src/action/test.rs +++ b/module/move/willbe/src/action/test.rs @@ -69,24 +69,24 @@ mod private { dir : AbsolutePath, channels : HashSet< channel::Channel >, - #[ default( 0u32 ) ] + #[ former( default = 0u32 ) ] concurrent : u32, - #[ default( 1u32 ) ] + #[ former( default = 1u32 ) ] power : u32, include_features : Vec< String >, - #[ default ( [ "full".to_string(), "default".to_string() ] ) ] + #[ former( default = [ "full".to_string(), "default".to_string() ] ) ] exclude_features : Vec< String >, - #[ default( true ) ] + #[ former( default = true ) ] temp : bool, enabled_features : Vec< String >, - #[ default( true ) ] + #[ former( default = true ) ] with_all_features : bool, - #[ default( true ) ] + #[ former( default = true ) ] with_none_features : bool, optimizations : HashSet< optimization::Optimization >, - #[ default( 1000u32 ) ] + #[ former( default = 1000u32 ) ] variants_cap : u32, - #[ default( false ) ] + #[ former( default = false ) ] with_progress : bool, } diff --git a/module/move/willbe/src/command/list.rs b/module/move/willbe/src/command/list.rs index 593f40193d..9c970783f9 100644 --- a/module/move/willbe/src/command/list.rs +++ b/module/move/willbe/src/command/list.rs @@ -19,27 +19,28 @@ mod private use action::{ list as l, list::{ ListFormat, ListOptions } }; use former::Former; + // qqq: `Former` forces the struct to be public #[ derive( Former ) ] - struct ListProperties + pub struct ListProperties { - #[ default( ListFormat::Tree ) ] + #[ former( default = ListFormat::Tree ) ] format : ListFormat, - #[ default( false ) ] + #[ former( default = false ) ] with_version : bool, - #[ default( false ) ] + #[ former( default = false ) ] with_path : bool, - #[ default( true ) ] + #[ former( default = true ) ] with_local : bool, - #[ default( false ) ] + #[ former( default = false ) ] with_remote : bool, - #[ default( true ) ] + #[ former( default = true ) ] with_primary : bool, - #[ default( false ) ] + #[ former( default = false ) ] with_dev : bool, - #[ default( false ) ] + #[ former( default = false ) ] with_build : bool, } diff --git a/module/move/willbe/src/command/publish.rs b/module/move/willbe/src/command/publish.rs index d591f56fe3..2724b33b78 100644 --- a/module/move/willbe/src/command/publish.rs +++ b/module/move/willbe/src/command/publish.rs @@ -9,12 +9,13 @@ mod private use former::Former; use std::fmt::Write; + // qqq: `Former` forces the struct to be public #[ derive( Former ) ] - struct PublishProperties + pub struct PublishProperties { - #[ default( true ) ] + #[ former( default = true ) ] dry : bool, - #[ default( true ) ] + #[ former( default = true ) ] temp : bool, } diff --git a/module/move/willbe/src/command/publish_diff.rs b/module/move/willbe/src/command/publish_diff.rs index 961ba818c4..cbb029393e 100644 --- a/module/move/willbe/src/command/publish_diff.rs +++ b/module/move/willbe/src/command/publish_diff.rs @@ -8,8 +8,9 @@ mod private use wtools::error::Result; use _path::AbsolutePath; + // qqq: `Former` forces the struct to be public #[ derive( former::Former ) ] - struct PublishDiffProperties + pub struct PublishDiffProperties { keep_archive : Option< PathBuf >, } diff --git a/module/move/willbe/src/command/test.rs b/module/move/willbe/src/command/test.rs index 5f72660220..e97ff16b5f 100644 --- a/module/move/willbe/src/command/test.rs +++ b/module/move/willbe/src/command/test.rs @@ -14,35 +14,36 @@ mod private use channel::Channel; use error_tools::for_app::bail; use optimization::Optimization; - + + // qqq: `Former` forces the struct to be public #[ derive( Former, Debug ) ] - struct TestsProperties + pub struct TestsProperties { - #[ default( true ) ] + #[ former( default = true ) ] dry : bool, - #[ default( true ) ] + #[ former( default = true ) ] with_stable : bool, - #[ default( false ) ] + #[ former( default = false ) ] with_nightly : bool, - #[ default( 0u32 ) ] + #[ former( default = 0u32 ) ] concurrent : u32, - #[ default( 1u32 ) ] + #[ former( default = 1u32 ) ] power : u32, include : Vec< String >, - #[ default ( [ "full".to_string(), "default".to_string() ] ) ] + #[ former( default = [ "full".to_string(), "default".to_string() ] ) ] exclude : Vec< String >, - #[ default( true ) ] + #[ former( default = true ) ] temp : bool, enabled_features : Vec< String >, - #[ default( true ) ] + #[ former( default = true ) ] with_all_features : bool, - #[ default( true ) ] + #[ former( default = true ) ] with_none_features : bool, - #[ default( true ) ] + #[ former( default = true ) ] with_debug : bool, - #[ default( false ) ] + #[ former( default = false ) ] with_release : bool, - #[ default( true ) ] + #[ former( default = true ) ] with_progress : bool, } diff --git a/module/move/willbe/src/command/workspace_renew.rs b/module/move/willbe/src/command/workspace_renew.rs index 26cc520bf4..4307e20045 100644 --- a/module/move/willbe/src/command/workspace_renew.rs +++ b/module/move/willbe/src/command/workspace_renew.rs @@ -7,8 +7,9 @@ mod private use wtools::error::{ anyhow::Context, Result }; use action::WorkspaceTemplate; + // qqq: `Former` forces the struct to be public #[ derive( Former ) ] - struct WorkspaceNewProperties + pub struct WorkspaceNewProperties { repository_url : String, branches : Vec< String >, diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index 5464dfe858..ce0e6575fe 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -366,7 +366,7 @@ mod private workspace_dir : CrateDir, package : Package, base_temp_dir : Option< PathBuf >, - #[ default( true ) ] + #[ former( default = true ) ] dry : bool, } @@ -512,7 +512,7 @@ mod private /// `dry` - A boolean value indicating whether to do a dry run. If set to `true`, the application performs /// a simulated run without making any actual changes. If set to `false`, the operations are actually executed. /// This property is optional and defaults to `true`. - #[ default( true ) ] + #[ former( default = true ) ] pub dry : bool, /// Required for tree view only @@ -523,7 +523,7 @@ mod private /// how to build and where to publish the package amongst other instructions. The `#[setter( false )]` /// attribute indicates that there is no setter method for the `plans` variable and it can only be modified /// within the struct. - #[ setter( false ) ] + #[ scalar( setter = false, hint = false ) ] pub plans : Vec< PackagePublishInstruction >, } diff --git a/module/move/willbe/src/entity/test.rs b/module/move/willbe/src/entity/test.rs index f68f4cb56f..7735bc5af1 100644 --- a/module/move/willbe/src/entity/test.rs +++ b/module/move/willbe/src/entity/test.rs @@ -336,11 +336,11 @@ mod private optimization : Optimization, /// Determines whether to use default features in the test. /// Enabled by default. - #[ default( true ) ] + #[ former( default = true ) ] with_default_features : bool, /// Determines whether to use all available features in the test. /// Disabled by default. - #[ default( false ) ] + #[ former( default = false ) ] with_all_features : bool, /// Specifies a list of features to be enabled in the test. enable_features : BTreeSet< String >, @@ -349,7 +349,7 @@ mod private /// A boolean indicating whether to perform a dry run or not. dry : bool, /// RUST_BACKTRACE - #[ default( true ) ] + #[ former( default = true ) ] backtrace : bool, } diff --git a/module/move/willbe/src/tool/cargo.rs b/module/move/willbe/src/tool/cargo.rs index 6a078e0eab..259c828791 100644 --- a/module/move/willbe/src/tool/cargo.rs +++ b/module/move/willbe/src/tool/cargo.rs @@ -14,9 +14,9 @@ mod private pub struct PackOptions { pub( crate ) path : PathBuf, - #[ default( false ) ] + #[ former( default = false ) ] pub( crate ) allow_dirty : bool, - #[ default( false ) ] + #[ former( default = false ) ] pub( crate ) no_verify : bool, pub( crate ) temp_path : Option< PathBuf >, pub( crate ) dry : bool, diff --git a/module/move/willbe/src/tool/template.rs b/module/move/willbe/src/tool/template.rs index 9e00f793b3..044f1157b9 100644 --- a/module/move/willbe/src/tool/template.rs +++ b/module/move/willbe/src/tool/template.rs @@ -107,6 +107,7 @@ mod private #[ derive( Debug, Default, Former ) ] pub struct TemplateParameters { + #[ subform( setter = false ) ] descriptors : Vec< TemplateParameterDescriptor > } @@ -138,28 +139,16 @@ mod private is_mandatory : bool, } - impl< Context, End > TemplateParametersFormer< Context, End > + impl< Definition > TemplateParametersFormer< Definition > where - End : former::FormingEnd< TemplateParameters, Context >, + Definition : former::FormerDefinition< Storage = < TemplateParameters as former::EntityToStorage >::Storage >, { #[ inline( always ) ] pub fn parameter( self, name : &str ) -> - TemplateParameterDescriptorFormer< Self, impl former::FormingEnd< TemplateParameterDescriptor, Self > > + TemplateParameterDescriptorAsSubformer< Self, impl TemplateParameterDescriptorAsSubformerEnd< Self > > { - let on_end = | descriptor : TemplateParameterDescriptor, super_former : core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - if let Some( ref mut descriptors ) = super_former.storage.descriptors - { - descriptors.push( descriptor ); - } - else - { - super_former.storage.descriptors = Some( vec![ descriptor ] ); - } - super_former - }; - TemplateParameterDescriptorFormer::begin( None, Some( self ), on_end ).parameter( name ) + self._descriptors_add::< TemplateParameterDescriptorFormer< _ >, _ >() + .parameter( name ) } } @@ -313,31 +302,19 @@ mod private pub struct TemplateFilesBuilder { /// Stores all file descriptors for current template. - #[ setter( false ) ] + #[ subform( setter = true ) ] + #[ scalar( setter = false, hint = false ) ] pub files : Vec< TemplateFileDescriptor >, } - impl< Context, End > TemplateFilesBuilderFormer< Context, End > + impl< Description > TemplateFilesBuilderFormer< Description > where - End : former::FormingEnd< TemplateFilesBuilder, Context >, + Description : former::FormerDefinition< Storage = < TemplateFilesBuilder as former::EntityToStorage >::Storage >, { #[ inline( always ) ] - pub fn file( self ) -> TemplateFileDescriptorFormer< Self, impl former::FormingEnd< TemplateFileDescriptor, Self > > + pub fn file( self ) -> TemplateFileDescriptorAsSubformer< Self, impl TemplateFileDescriptorAsSubformerEnd< Self > > { - let on_end = | descriptor : TemplateFileDescriptor, super_former : core::option::Option< Self > | -> Self - { - let mut super_former = super_former.unwrap(); - if let Some( ref mut files ) = super_former.storage.files - { - files.push( descriptor ); - } - else - { - super_former.storage.files = Some( vec![ descriptor ] ); - } - super_former - }; - TemplateFileDescriptorFormer::begin( None, Some( self ), on_end ) + self._files_add() } } diff --git a/module/template/template_blank/Readme.md b/module/template/template_blank/Readme.md index c1c128cd89..1651492515 100644 --- a/module/template/template_blank/Readme.md +++ b/module/template/template_blank/Readme.md @@ -1,7 +1,7 @@ # Module :: {{template_blank}} -[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/Module{{TemplateBlank}}Push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/Module{{TemplateBlank}}Push.yml) [![docs.rs](https://img.shields.io/docsrs/{{template_blank}}?color=e3e8f0&logo=docs.rs)](https://docs.rs/{{template_blank}}) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/Module{{template_blank}}Push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/Module{{template_blank}}Push.yml) [![docs.rs](https://img.shields.io/docsrs/{{template_blank}}?color=e3e8f0&logo=docs.rs)](https://docs.rs/{{template_blank}}) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) ___